I wanted to use libgps to interface with gpsd daemon. That's why I've implemented a little testing application in order to extract a value from a specific satellite.
The documentation on its HOWTO page tells us that
The tricky part is interpreting what you get from the blocking read. The reason it’s tricky is that you’re not guaranteed that every read will pick up exactly one complete JSON object from the daemon. It may grab one response object, or more than one, or part of one, or one or more followed by a fragment.
As recommended the documentation, the PACKET_SET
mask bit is checked before doing anything else.
#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);
}
As recommended in the documentation too, I had a look at cgps and gpxlogger for example codes, but the subtleties of libgps escape me. A while loop has been added before gps_waiting()
in order to get, at least, one entire response object. Before introducing pthread, I noted that call the function test_result()
just after start_test()
take few seconds before returning an answer. By using a thread I thought that 3
would be imediately returned, then 3
or 4
.. but it's not ! I am still losing few seconds. In addition, I voluntarily use pthread_tryjoin_np()
because its man page says
The pthread_tryjoin_np() function performs a nonblocking join with the thread
Can anybody give me his help, I guess that I understand something wrongly but I am not able to say about which part yet? Basically, why I come into the do while loop at least four times before returning the first value ?
EDIT 1 :
After reading the documentation HOWTO again I highlight the lines :
The fact that the data-waiting check and the read both block means that, if your application has to deal with other input sources than the GPS, you will probably have to isolate the read loop in a thread with a mutex lock on the gps_data structure.
I am a little bit confusing. What does it really mean ?