2016-02-24 10 views
6

Ich wollte libgps als Schnittstelle zum gpsd-Daemon verwenden. Aus diesem Grund habe ich eine kleine Testanwendung implementiert, um einen Wert von einem bestimmten Satelliten zu extrahieren.libgps zum Extrahieren von Daten aus dem gpsd-Daemon

Die Dokumentation auf seiner HOWTO Seite sagt uns, dass

Der schwierige Teil ist zu interpretieren, was Sie aus der blockierenden Lese erhalten. Der Grund, warum es schwierig ist, ist, dass Sie nicht garantieren können, dass jedes Lesen genau ein komplettes JSON-Objekt aus dem Daemon abholen wird. Es kann greifen Sie ein Antwortobjekt, oder mehr als eins oder ein Teil von einem oder einem oder mehr gefolgt von einem Fragment.

Wie in der Dokumentation empfohlen, wird das Maskenbit PACKET_SET überprüft, bevor etwas anderes getan wird.

#define _GNU_SOURCE 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <unistd.h> 
#include <stdint.h> 
#include <gps.h> 
#include <pthread.h> 

pthread_t t_thread; 

struct t_args { 
    unsigned int ID; 
}; 

unsigned int status = 0; 
int elevation; 

int p_nmea(void *targs); 

void start_test(void) 
{ 
    struct t_args *args = malloc(sizeof *args); 
    status = 1; 
    args->ID = 10; 

    pthread_attr_t attr; 

    pthread_attr_init(&attr); 
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); 
    if (pthread_create(&t_thread, &attr, (void *)&p_nmea, args) != 0) 
    { 
     perror("create: \n"); 
    } 
} 

int test_result(int * Svalue) 
{ 
    int res; 

    if(status == 1) 
    { 
     void * t_res; 
     if(pthread_tryjoin_np(t_thread, &t_res) != 0) 
     { 
      status = 1; 
     } 
     else 
     {  
      if((int)t_res == 1) 
      { 
       res = 3; 
       *Svalue = elevation; 
       elevation = 0; 
      } 
      else 
      { 
       res = 4;    
      } 
     } 
    } 
    return res; 
} 

int p_nmea(void *targs) 
{ 
    struct t_args *thread_args = targs;  
    struct gps_data_t gpsdata; 
    int ret = -1; 
    int count = 10; 
    int i,j; 

    if(gps_open((char *)"localhost", (char *)DEFAULT_GPSD_PORT, &gpsdata) != 0) 
    { 
     (void)fprintf(stderr, "cgps: no gpsd running or network error: %d, %s\n", errno, gps_errstr(errno)); 
     return (-1); 
    } 
    else 
    { 
     (void)gps_stream(&gpsdata, WATCH_ENABLE, NULL); 
     do 
     { 
      if(!gps_waiting(&gpsdata, 1000000)) 
      {  
       (void)gps_close(&gpsdata); 
      } 
      else 
      { 
       if(gps_read(&gpsdata) == -1) 
       { 
        return (-1); 
       } 
       else 
       { 
        if(gpsdata.set & PACKET_SET) 
        { 
         for (i = 0; i < MAXCHANNELS; i++) 
         { 
          for (j = 0; j < gpsdata->satellites_visible; j++) 
          { 
           if(gpsdata->PRN[i] == thread_args.ID) 
           { 
            elevation = (int)gpsdata->elevation[i]; 
            ret = 1; 
            break; 
           }  
          } 
          if(gpsdata->PRN[i] == thread_args.ID) 
          { 
           break; 
          } 
         } 
        } 
       } 
      } 
      --count; 
     }while(count != 0); 
    } 
    (void)gps_stream(&gpsdata, WATCH_DISABLE, NULL); 
    (void)gps_close(&gpsdata); 
    (void)free(thread_args); 
    (void)pthread_exit((void*) ret); 
} 

Wie auch in der Dokumentation empfohlen, ich hatte einen Blick auf CGPS ​​und gpxlogger zum Beispiel Codes, aber die Feinheit der libgps mich entkommen. Eine While-Schleife wurde vor gps_waiting() hinzugefügt, um mindestens ein gesamtes Antwortobjekt zu erhalten. Bevor ich PThread einführte, bemerkte ich, dass die Funktion kurz nach start_test() einige Sekunden dauert, bevor sie eine Antwort zurücksendet. Mit einem Thread dachte ich, dass 3 würde sofort zurückgegeben werden, dann 3 oder 4 .. aber es ist nicht! Ich verliere immer noch einige Sekunden. Außerdem verwende ich pthread_tryjoin_np() freiwillig, weil seine Manpage sagt

Die pthread_tryjoin_np() Funktion ein nicht-blockierende mit dem Gewinde

mir jemand geben kann seine Hilfe verbinden führt, denke ich, dass ich etwas falsch verstehen aber was kann ich noch nicht sagen? Warum komme ich mindestens viermal in die do while-Schleife, bevor ich den ersten Wert zurückgebe?

EDIT 1:

Nach dem Lesen der Dokumentation HOWTO wieder markierte ich die Zeilen:

Die Tatsache, dass die Daten-Warteprüfung und die Lese beide Block bedeuten, dass, wenn Ihre Anwendung zu tun hat Bei anderen Eingangsquellen als dem GPS müssen Sie wahrscheinlich die Leseschleife in einem Thread mit einer Mutex-Sperre für die gps_data-Struktur isolieren.

Ich bin ein bisschen verwirrend. Was bedeutet das wirklich?

+0

Ich bin nicht vertraut mit dem Lesen von Daten von GPS, aber Ihre Threading wie gepostet aussieht wie Ärger. An anderer Stelle rufen Sie 'start_test' und dann' test_result' in der nächsten Zeile an? Und was genau versuchst du zu tun? Höhendaten von GPS Satellit 10 lesen? Ich begann mit einer Antwort, aber es stellte sich heraus, dass ich zu viele Fragen hatte. Ihre zitierte EDIT1-Dokumentation bedeutet einfach, dass Aufrufe von 'gps_waiting()' und 'gps_read()' blockieren. Wenn Sie nur einen einzigen Thread in Ihrem Prozess haben, bedeutet dies, dass Ihr gesamter Prozess zum Stillstand kommt, bis der Aufruf der blockierenden Funktion zurückkehrt. (Forts.) – yano

+0

(Forts.) Wenn also Ihr Prozess auf andere Eingabequellen wartet, verlieren Sie alle Daten, die während der Blockierung Ihres einzelnen Threads auf 'gps_waiting()' und/oder 'gps_read()' eingehen. Aus diesem Grund schlägt es vor, diese Anrufe zu einem separaten Thread zu machen, dessen einzige Aufgabe darin besteht, diese Anrufe zu blockieren und Daten von ihnen abzurufen. Währenddessen sind andere Threads in Ihrem Prozess frei für alles, was Ihr Prozess sonst noch tun möchte. Der Mutex für die 'gps_data'-Struktur wird empfohlen, um den Zugriff darauf zu schützen, falls andere Threads modifizieren und/oder lesen. Mutexe sorgen für die Daten-Parallelität und Integrität in (Forts.) – yano

+0

(Forts.) Multi-Thread-Umgebungen. Wenn Ihnen das alles neu ist, empfehle ich Ihnen, ein Pthread-Tutorial zu lesen. Das ist ein guter Tipp: https://computing.llnl.gov/tutorials/pthreads/. Aber basierend auf dem, was Sie hier versuchen, brauchen Sie vielleicht gar keine Threads. Wenn das nur ein Test/Proof-of-Concept ist, dass Sie tatsächlich GPS-Daten lesen können, würde ich nicht mit Threads herumspielen. Threads fügen immer Komplikationen hinzu und öffnen die Tür für seltsame Bugs, wenn sie nicht richtig verwendet werden. Entschuldigung für den langen Kommentar; Wünschte, SO hätte eine Seite für Antworten und eine Seite zur Diskussion. – yano

Antwort

0

Ihre Schleife wird mehrmals ausgeführt, bevor ein vollständiges Paket zurückgegeben wird, da Sie keine Schlafbedingung haben. Daher gibt die Funktion gps_waiting() jedes Mal, wenn der Dämon ein Paket registriert (selbst wenn keine vollständige NMEA-Nachricht vorliegt), zurück. Ich würde empfehlen, mindestens so lange zu schlafen, bis Ihr GPS eine vollständige Nachricht registriert. Wenn Sie beispielsweise GPPAT Nachrichten erwarten, können Sie vernünftigerweise erwarten, 12 Zeichen in der Nachricht zu haben. Bei 9600 Baud würde das 1/17,5 Sekunden oder etwa 57 ms dauern.In diesem Fall könnte Ihr Code wie folgt aussehen:

#define _GNU_SOURCE 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <unistd.h> 
#include <stdint.h> 
#include <gps.h> 
#include <pthread.h> 

pthread_t t_thread; 

struct t_args { 
    unsigned int ID; 
}; 

unsigned int status = 0; 
int elevation; 

int p_nmea(void *targs); 

void start_test(void) 
{ 
    struct t_args *args = malloc(sizeof *args); 
    status = 1; 
    args->ID = 10; 

    pthread_attr_t attr; 

    pthread_attr_init(&attr); 
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); 
    if (pthread_create(&t_thread, &attr, (void *)&p_nmea, args) != 0) 
    { 
     perror("create: \n"); 
    } 
} 

int test_result(int * Svalue) 
{ 
    int res; 

    if(status == 1) 
    { 
     void * t_res; 
     if(pthread_tryjoin_np(t_thread, &t_res) != 0) 
     { 
      status = 1; 
     } 
     else 
     {  
      if((int)t_res == 1) 
      { 
       res = 3; 
       *Svalue = elevation; 
       elevation = 0; 
      } 
      else 
      { 
       res = 4;    
      } 
     } 
    } 
    return res; 
} 

int p_nmea(void *targs) 
{ 
    struct t_args *thread_args = targs;  
    struct gps_data_t gpsdata; 
    int ret = 0; 
    int count = 10; 
    int i,j; 

    if(gps_open((char *)"localhost", (char *)DEFAULT_GPSD_PORT, &gpsdata) != 0) 
    { 
     (void)fprintf(stderr, "cgps: no gpsd running or network error: %d, %s\n", errno, gps_errstr(errno)); 
     return (-1); 
    } 
    else 
    { 
     (void)gps_stream(&gpsdata, WATCH_ENABLE, NULL); 
     do 
     { 
      ret = 0; // Set this here to allow breaking correctly 
      usleep(50000); // Sleep here to wait for approx 1 msg 
      if(!gps_waiting(&gpsdata, 1000000)) break; 

      if(gps_read(&gpsdata) == -1) break; 

      if(gpsdata.set & PACKET_SET) 
      { 
       for (i = 0; i < MAXCHANNELS && !ret; i++) 
       { 
       for (j = 0; j < gpsdata.satellites_visible; j++) 
       { 
        if(gpsdata.PRN[i] == thread_args.ID) 
        { 
        elevation = (int)gpsdata.elevation[i]; // Be sure to not deref structure here 
        ret = 1; 
        break; 
        }  
       } 
      } 
      --count; 
     }while(count != 0); 
    } 
    (void)gps_stream(&gpsdata, WATCH_DISABLE, NULL); 
    (void)gps_close(&gpsdata); 
    (void)free(thread_args); 
    (void)pthread_exit((void*) ret); 
} 

Alternativ können Sie auch Ihre Zählung höher setzen und auf die vollständige Nachricht warten.

Verwandte Themen