用于从 GPSD 守护程序中提取数据的 libgps

libgps for extract data from the gpsd daemon

提问人:ogs 提问时间:2/25/2016 最后编辑:ogs 更新时间:4/5/2016 访问量:2157

问:

我想使用 libgps 与 gpsd 守护程序连接。这就是为什么我实现了一个小的测试应用程序,以便从特定卫星中提取值。

其 HOWTO 页面上的文档告诉我们

棘手的部分是解释你从阻塞读取中得到什么。 它很棘手的原因是你不能保证每次阅读 将从守护进程中获取一个完整的 JSON 对象。它可能 抓取一个响应对象,或多个响应对象,或一个响应对象的一部分,或一个响应对象或 更多,后跟一个片段。

按照文档的建议,在执行任何其他操作之前检查掩码位。PACKET_SET

#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);
}

正如文档中所建议的那样,我查看了 cgps 和 gpxlogger 的示例代码,但 libgps 的微妙之处让我无法理解。之前已经添加了一个 while 循环,以便至少获得一个完整的响应对象。在介绍 pthread 之前,我注意到在返回答案之前需要几秒钟后调用函数。通过使用线程,我认为它会立即返回,然后或..但事实并非如此!我仍然在失去几秒钟。此外,我自愿使用,因为它的手册页说gps_waiting()test_result()start_test()334pthread_tryjoin_np()

pthread_tryjoin_np() 函数对线程执行非阻塞联接

谁能给我他的帮助,我想我理解错了什么,但我还不能说是哪一部分?基本上,为什么在返回第一个值之前我至少进入 do while 循环四次?

编辑 1 :

再次阅读文档HOWTO后,我突出显示了以下几行:

data-waiting check 和 read both 块这一事实意味着,如果您的应用程序必须处理 GPS 以外的其他输入源,您可能必须在线程中隔离读取循环,并在 gps_data 结构上使用互斥锁。

我有点困惑。这到底是什么意思?

C JSON 解析 GPS、 GPSD

评论

0赞 yano 3/4/2016
我不熟悉从 GPS 读取数据,但您发布的线程看起来很麻烦。在其他地方的代码中,您是否在下一行调用?你到底想做什么?从 GPS 卫星读取高程数据 10?我开始寻找答案,但事实证明我有太多问题。您的 EDIT1 引用文档仅表示对 和 将要阻止的调用。如果进程中只有一个线程,这意味着整个进程将戛然而止,直到阻塞函数调用返回。(续)start_testtest_resultgps_waiting()gps_read()
0赞 yano 3/4/2016
(续)因此,如果您的进程正在等待其他输入源,则在单个线程阻塞和/或 .这就是为什么它建议将这些调用设置为一个单独的线程,其唯一工作只是阻止这些调用并从中检索数据。同时,进程中的其他线程可以自由用于进程可能想要执行的任何其他操作。建议使用结构的互斥锁,以保护对它的访问,以防其他线程修改和/或读取它。互斥锁确保数据并发性和完整性 (cont)gps_waiting()gps_read()gps_data
0赞 yano 3/4/2016
(续) 多线程环境。如果所有这些对你来说都是新的,那么我建议你阅读一个pthread教程。这是一个很好的:computing.llnl.gov/tutorials/pthreads .但根据你在这里尝试做的事情,你甚至可能不需要线程。如果这只是一个测试/概念验证,您实际上可以读取 GPS 数据,我不会弄乱线程。如果使用不当,线程总是会增加复杂性,并为奇怪的错误打开大门。对不起,长篇大论;希望 SO 有一个答案页面和一个讨论页面。
0赞 ogs 3/4/2016
@yano感谢您提供这些信息!我在另一个文件(仅包含)的 main 中调用 和 函数,其中包括问题中定义的文件。基本上,我想实现一个过程,该过程允许用户通过调用来调用并在他想要时获得结果。它可以在第一次启动请求后立即或几分钟。因此,如果测试当前尚未完全完成,我想将 1 返回给用户,否则返回 3 或 4。start_testtest_resultmain()start_test()test_result()
0赞 ogs 3/4/2016
@yano 正如你所确定的,我应该被阻止,直到阻止函数调用返回,不幸的是,我将丢失用户提供的其他输入数据......这就是为什么我坚定地将我的实现定向到线程使用

答:

0赞 Seth 4/5/2016 #1

您的循环在返回完整数据包之前执行了多次,因为您没有睡眠条件。因此,每次守护程序注册数据包(即使不是完整的 NMEA 消息)时,该函数都会返回。我建议至少睡一觉,只要你的GPS需要记录完整的信息。gps_waiting()

例如,如果您期望消息,则可以合理地期望消息中有 12 个字符。因此,在 9600 波特率下,这将需要 1/17.5 秒或大约 57 毫秒。在这种情况下,您的代码可能如下所示:GPPAT

#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);
}

或者,您可以将计数设置得更高,然后等待完整的消息。