GPS установка точного времени на Raspberry Pi

Немного решил затронуть интересную тему с работой с GPS приемниками под Linux и в частности на Raspberry Pi, как наиболее популярной IoT платформе с Linux. Не всегда есть возможность подключить Raspberry Pi к интернету, чтобы синхронизировать время с NTP серверами точного времени, которые в свою очередь транслируют это время со спутников GPS, но иногда в проект бывает заложен собственно GPS приемник и почему бы не брать время напрямую со спутника и не записывать его в Raspberry Pi, так мы и сделаем, будем использовать стандартный C и демон gpsd, про который наверное не много, кто слышал.

Итак, что же такое gpsd? А это небольшой демон, который мониторит данные, поступающие с одного или нескольких приемников GPS, подключенных по USB к Linux компьютеру и отдает эти данные по запросу в порт 2947 хост машины. Из хорошего, формат. отдаваемых демоном данных, значительно проще и понятней для анализа нежели тот же NMEA 0183. Работать с ним не просто, а очень просто, существует C библиотека для линковки с приложением, также есть C++ класс-обертка для этой же библиотеки и даже Python модуль, для любителей скриптов. Все это и немного документации можно найти на официальной страничке gpsd.

Система GPSD также поддерживает хранение точного времени; она может выступать в качестве источника времени для ntpd (демона протокола сетевого сервиса времени), если в каком-нибудь из подключенных к нему датчиков имеется возможность выдавать сигналы PPS (секундные сигналы). Разработчики проекта GPSD тесно сотрудничают с проектом ntpd с целью улучшения сетевого сервиса времени.

Вот что интересного говорит нам документация к gpsd. Для тех, кто хотел бы немного глубже копнуть по сабжу, вот перевод из книги «Архитектура приложений с открытым исходным кодом» главы 7, которая как раз затрагивает gpsd.

А теперь попробуем использовать это для решения задачи получения точного времени и установки оного в Raspberry Pi.

Я использовал небольшой GPS приемник на базе Quectel L76, который сам же и сваял на коленке:

quectel l76

Подключение к Raspberry Pi по USB через, встроенный в плату, конвертер Serial-To-Usb CP2102.

Далее приведу отрывок программы, который как раз касается приема данных от демона и установки NTP времени на Raspberry Pi:

#include <stdio.h>
#include <gps.h>
#include <time.h>

int rc;
struct timeval tv;
struct gps_data_t gpsData;

int main(void) {
    if ((rc = gps_open ("localhost", "2947", &gpsData)) == -1) {
        printf ("[ERROR] No GPS receiver found. code: %d, reason: %s\n", rc, gps_errstr(rc));
        return EXIT_FAILURE;
    }

    gps_stream(&gpsData, WATCH_ENABLE | WATCH_JSON, NULL);
    // Ждем пару секунд пока будут приняты данные GPS
    if (gps_waiting (&gpsData, 1000000)) {
    // Читаем данные с GPS приемника
        if ((rc = gps_read(&gpsData)) == -1) {
            printf ("[ERROR] Error occured reading gps data. Code: %d, reason: %s\n", rc, gps_errstr(rc));
        } else {
            // Показать данные GPS приемника
            if ((gpsData.status == STATUS_FIX) && 
               (gpsData.fix.mode == MODE_2D || gpsData.fix.mode == MODE_3D) &&
                !isnan(gpsData.fix.latitude) && 
                !isnan(gpsData.fix.longitude) &&
                !isnan(gpsData.fix.altitude)) {
                printf ("[INFO] Latitude: %.2f Longitude: %.2f Altitude: %.1f Timestamp: %ld\n", gpsData.fix.latitude, gpsData.fix.longitude, gpsData.fix.altitude, (time_t)gpsData.fix.time);
                struct timeval gpstv;
                gpstv.tv_sec = (time_t)gpsData.fix.time;
                gpstv.tv_usec = 0;
                settimeofday (&gpstv, NULL);
            }
        }
    
    while (1) {}

    return 0;
}

Создаем сперва экземпляр структуры gps_data_t:

struct gps_data_t gpsData;

Далее, открываем порт 2947 на чтение данных GPS:

if ((rc = gps_open ("localhost", "2947", &gpsData)) == -1)

Читаем данные:

if ((rc = gps_read(&gpsData)) == -1)

И устанавливаем время:

settimeofday (&gpstv, NULL);

Попутно программа выводит в консоль координаты, принятые от приемника, и время в формате unixtime:

printf ("[INFO] Latitude: %.2f Longitude: %.2f Altitude: %.1f Timestamp: %ld\n", gpsData.fix.latitude, gpsData.fix.longitude, gpsData.fix.altitude, (time_t)gpsData.fix.time);

Координаты далее можно использовать как угодно в программе, опрос порта 2947 можно вести непрерывно через равные промежутки времени в цикле while (1) и далее математически пересчитывать координаты для определения местоположения каких либо объектов относительно вашего местоположения, например, самолетов, что очень даже используется в ADSB приеме (азимут, угол места, дальность и т.д.).

Вот и все, репостните, пожалуйста, ссылку на данный материал, кнопочки слева)