123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521 |
- /*
- * monitor_nmea0183.c - gpsmon support for NMEA devices.
- *
- * To do: Support for GPGLL, GPGBS, GPZDA, PASHR NMEA sentences.
- *
- * This file is Copyright (c) 2010-2018 by the GPSD project
- * SPDX-License-Identifier: BSD-2-clause
- */
- #include "gpsd_config.h" /* must be before all includes */
- #include <stdio.h>
- #include <string.h>
- #include <math.h>
- #include <stdlib.h> /* for labs() */
- #include <assert.h>
- #include <stdarg.h>
- #include <unistd.h>
- #include "gpsd.h"
- #include "gpsmon.h"
- #include "gpsdclient.h"
- #include "strfuncs.h"
- #ifdef NMEA0183_ENABLE
- extern const struct gps_type_t driver_nmea0183;
- static WINDOW *cookedwin, *nmeawin, *satwin, *gprmcwin, *gpggawin, *gpgsawin, *gpgstwin;
- static timespec_t last_tick, tick_interval;
- static char sentences[NMEA_MAX * 2];
- /*****************************************************************************
- *
- * NMEA0183 support
- *
- *****************************************************************************/
- #define SENTENCELINE 1 /* index of sentences line in the NMEA window */
- #define MAXSATS 12 /* max satellites we can display */
- static bool nmea_initialize(void)
- {
- int i;
- cookedwin = derwin(devicewin, 3, 80, 0, 0);
- assert(cookedwin !=NULL);
- (void)wborder(cookedwin, 0, 0, 0, 0, 0, 0, 0, 0);
- (void)syncok(cookedwin, true);
- (void)wattrset(cookedwin, A_BOLD);
- (void)mvwaddstr(cookedwin, 1, 1, "Time: ");
- (void)mvwaddstr(cookedwin, 1, 32, "Lat: ");
- (void)mvwaddstr(cookedwin, 1, 55, "Lon: ");
- (void)mvwaddstr(cookedwin, 2, 34, " Cooked TPV ");
- (void)wattrset(cookedwin, A_NORMAL);
- nmeawin = derwin(devicewin, 3, 80, 3, 0);
- assert(nmeawin !=NULL);
- (void)wborder(nmeawin, 0, 0, 0, 0, 0, 0, 0, 0);
- (void)syncok(nmeawin, true);
- (void)wattrset(nmeawin, A_BOLD);
- (void)mvwaddstr(nmeawin, 2, 34, " Sentences ");
- (void)wattrset(nmeawin, A_NORMAL);
- satwin = derwin(devicewin, MAXSATS + 3, 20, 6, 0);
- assert(satwin !=NULL);
- (void)wborder(satwin, 0, 0, 0, 0, 0, 0, 0, 0), (void)syncok(satwin, true);
- (void)wattrset(satwin, A_BOLD);
- (void)mvwprintw(satwin, 1, 1, "Ch PRN Az El S/N");
- for (i = 0; i < MAXSATS; i++)
- (void)mvwprintw(satwin, (int)(i + 2), 1, "%2d", i);
- (void)mvwprintw(satwin, 14, 7, " GSV ");
- (void)wattrset(satwin, A_NORMAL);
- gprmcwin = derwin(devicewin, 9, 30, 6, 20);
- assert(gprmcwin !=NULL);
- (void)wborder(gprmcwin, 0, 0, 0, 0, 0, 0, 0, 0),
- (void)syncok(gprmcwin, true);
- (void)wattrset(gprmcwin, A_BOLD);
- (void)mvwprintw(gprmcwin, 1, 1, "Time: ");
- (void)mvwprintw(gprmcwin, 2, 1, "Latitude: ");
- (void)mvwprintw(gprmcwin, 3, 1, "Longitude: ");
- (void)mvwprintw(gprmcwin, 4, 1, "Speed: ");
- (void)mvwprintw(gprmcwin, 5, 1, "Course: ");
- (void)mvwprintw(gprmcwin, 6, 1, "Status: FAA: ");
- (void)mvwprintw(gprmcwin, 7, 1, "MagVar: ");
- (void)mvwprintw(gprmcwin, 8, 12, " RMC ");
- (void)wattrset(gprmcwin, A_NORMAL);
- gpgsawin = derwin(devicewin, 6, 30, 15, 20);
- assert(gpgsawin !=NULL);
- (void)wborder(gpgsawin, 0, 0, 0, 0, 0, 0, 0, 0);
- (void)syncok(gpgsawin, true);
- (void)wattrset(gpgsawin, A_BOLD);
- #define MODE_LINE 1
- (void)mvwprintw(gpgsawin, MODE_LINE, 1, "Mode: ");
- #define SATS_LINE 1
- #define SATS_COL 10
- (void)mvwprintw(gpgsawin, SATS_LINE, SATS_COL, "Sats: ");
- #define DOP_LINE 2
- (void)mvwprintw(gpgsawin, DOP_LINE, 1, "DOP: H= V= P=");
- #define TOFF_LINE 3
- (void)mvwprintw(gpgsawin, TOFF_LINE, 1, "TOFF: ");
- (void)mvwaddstr(gpgsawin, TOFF_LINE, 7, "N/A");
- #define PPS_LINE 4
- (void)mvwprintw(gpgsawin, PPS_LINE, 1, "PPS: ");
- (void)mvwaddstr(gpgsawin, PPS_LINE, 6, "N/A");
- (void)mvwprintw(gpgsawin, 5, 9, " GSA + PPS ");
- (void)wattrset(gpgsawin, A_NORMAL);
- (void)syncok(gpgsawin, true);
- gpggawin = derwin(devicewin, 9, 30, 6, 50);
- assert(gpggawin !=NULL);
- (void)wborder(gpggawin, 0, 0, 0, 0, 0, 0, 0, 0);
- (void)syncok(gpggawin, true);
- (void)wattrset(gpggawin, A_BOLD);
- (void)mvwprintw(gpggawin, 1, 1, "Time: ");
- (void)mvwprintw(gpggawin, 2, 1, "Latitude: ");
- (void)mvwprintw(gpggawin, 3, 1, "Longitude: ");
- (void)mvwprintw(gpggawin, 4, 1, "Altitude: ");
- (void)mvwprintw(gpggawin, 5, 1, "Quality: Sats: ");
- (void)mvwprintw(gpggawin, 6, 1, "HDOP: ");
- (void)mvwprintw(gpggawin, 7, 1, "Geoid: ");
- (void)mvwprintw(gpggawin, 8, 12, " GGA ");
- (void)wattrset(gpggawin, A_NORMAL);
- gpgstwin = derwin(devicewin, 6, 30, 15, 50);
- assert(gpgstwin !=NULL);
- (void)wborder(gpgstwin, 0, 0, 0, 0, 0, 0, 0, 0);
- (void)syncok(gpgstwin, true);
- (void)wattrset(gpgstwin, A_BOLD);
- (void)mvwprintw(gpgstwin, 1, 1, "UTC: ");
- (void)mvwprintw(gpgstwin, 1, 16, "RMS: ");
- (void)mvwprintw(gpgstwin, 2, 1, "MAJ: ");
- (void)mvwprintw(gpgstwin, 2, 16, "MIN: ");
- (void)mvwprintw(gpgstwin, 3, 1, "ORI: ");
- (void)mvwprintw(gpgstwin, 3, 16, "LAT: ");
- (void)mvwprintw(gpgstwin, 4, 1, "LON: ");
- (void)mvwprintw(gpgstwin, 4, 16, "ALT: ");
- (void)mvwprintw(gpgstwin, 5, 12, " GST ");
- (void)wattrset(gpgstwin, A_NORMAL);
- (void)clock_gettime(CLOCK_REALTIME, &last_tick);
- sentences[0] = '\0';
- return (nmeawin != NULL);
- }
- static void cooked_pvt(void)
- {
- char scr[128];
- if (0 < session.gpsdata.fix.time.tv_sec) {
- (void)timespec_to_iso8601(session.gpsdata.fix.time, scr, sizeof(scr));
- } else
- (void)snprintf(scr, sizeof(scr), "n/a");
- (void)mvwprintw(cookedwin, 1, 7, "%-24s", scr);
- if (session.gpsdata.fix.mode >= MODE_2D) {
- deg_to_str2(deg_ddmm, session.gpsdata.fix.latitude,
- scr, sizeof(scr), " N", " S");
- } else
- (void)strncpy(scr, "n/a", sizeof(scr));
- (void)mvwprintw(cookedwin, 1, 37, "%-17s", scr);
- if (session.gpsdata.fix.mode >= MODE_2D) {
- deg_to_str2(deg_ddmm, session.gpsdata.fix.longitude,
- scr, sizeof(scr), " E", " W");
- } else
- (void)strncpy(scr, "n/a", sizeof(scr));
- (void)mvwprintw(cookedwin, 1, 60, "%-17s", scr);
- }
- static void monitor_satlist(WINDOW *win, int y, int x)
- /* display as much as we can of a satlist in a specified window */
- {
- int ymax, xmax;
- char scr[128];
- int i;
- assert(win != NULL);
- (void)wmove(win, y, x);
- (void)wclrtoeol(win);
- scr[0] = '\0';
- for (i = 0; i < MAXCHANNELS; i++) {
- if (session.gpsdata.skyview[i].used)
- str_appendf(scr, sizeof(scr),
- "%d ", session.gpsdata.skyview[i].PRN);
- }
- getmaxyx(win, ymax, xmax);
- assert(ymax != 0); /* suppress compiler warning */
- (void)mvwaddnstr(win, y, x, scr, xmax - 2 - x);
- if (strlen(scr) >= (size_t) (xmax - 2)) {
- (void)mvwaddch(win, y, xmax - 2 - x, (chtype) '.');
- (void)mvwaddch(win, y, xmax - 3 - x, (chtype) '.');
- (void)mvwaddch(win, y, xmax - 4 - x, (chtype) '.');
- }
- monitor_fixframe(win);
- }
- static void nmea_update(void)
- {
- char **fields;
- assert(cookedwin != NULL);
- assert(nmeawin != NULL);
- assert(gpgsawin != NULL);
- assert(gpggawin != NULL);
- assert(gprmcwin != NULL);
- assert(gpgstwin != NULL);
- /* can be NULL if packet was overlong */
- fields = session.nmea.field;
- if (session.lexer.outbuffer[0] == (unsigned char)'$'
- && fields != NULL && fields[0] != NULL) {
- int ymax, xmax;
- timespec_t now;
- timespec_t ts_diff;
- getmaxyx(nmeawin, ymax, xmax);
- assert(ymax > 0);
- if (strstr(sentences, fields[0]) == NULL) {
- char *s_end = sentences + strlen(sentences);
- if ((int)(strlen(sentences) + strlen(fields[0])) < xmax - 2) {
- *s_end++ = ' ';
- (void)strlcpy(s_end, fields[0], NMEA_MAX);
- } else {
- *--s_end = '.';
- *--s_end = '.';
- *--s_end = '.';
- }
- (void)mvwaddstr(nmeawin, SENTENCELINE, 1, sentences);
- }
- /*
- * If the interval between this and last update is
- * the longest we've seen yet, boldify the corresponding
- * tag.
- */
- (void)clock_gettime(CLOCK_REALTIME, &now);
- TS_SUB(&ts_diff, &now, &last_tick);
- if (TS_GZ(&ts_diff) && TS_GT(&ts_diff, &tick_interval)) {
- char *findme = strstr(sentences, fields[0]);
- tick_interval = ts_diff;
- if (findme != NULL) {
- (void)mvwchgat(nmeawin, SENTENCELINE, 1, xmax - 13, A_NORMAL, 0,
- NULL);
- (void)mvwchgat(nmeawin, SENTENCELINE, 1 + (findme - sentences),
- (int)strlen(fields[0]), A_BOLD, 0, NULL);
- }
- }
- last_tick = now;
- if (strcmp(fields[0], "GPGSV") == 0
- || strcmp(fields[0], "GNGSV") == 0
- || strcmp(fields[0], "GLGSV") == 0) {
- int i;
- int nsats =
- (session.gpsdata.satellites_visible <
- MAXSATS) ? session.gpsdata.satellites_visible : MAXSATS;
- for (i = 0; i < nsats; i++) {
- (void)wmove(satwin, i + 2, 3);
- (void)wprintw(satwin, " %3d %3d%3d %3.0f",
- session.gpsdata.skyview[i].PRN,
- session.gpsdata.skyview[i].azimuth,
- session.gpsdata.skyview[i].elevation,
- session.gpsdata.skyview[i].ss);
- }
- /* add overflow mark to the display */
- if (nsats <= MAXSATS)
- (void)mvwaddch(satwin, MAXSATS + 2, 18, ACS_HLINE);
- else
- (void)mvwaddch(satwin, MAXSATS + 2, 18, ACS_DARROW);
- }
- if (strcmp(fields[0], "GPRMC") == 0
- || strcmp(fields[0], "GNRMC") == 0
- || strcmp(fields[0], "GLRMC") == 0) {
- /* time, lat, lon, course, speed */
- (void)mvwaddstr(gprmcwin, 1, 12, fields[1]);
- (void)mvwprintw(gprmcwin, 2, 12, "%12s %s", fields[3], fields[4]);
- (void)mvwprintw(gprmcwin, 3, 12, "%12s %s", fields[5], fields[6]);
- (void)mvwaddstr(gprmcwin, 4, 12, fields[7]);
- (void)mvwaddstr(gprmcwin, 5, 12, fields[8]);
- /* the status field, FAA code, and magnetic variation */
- (void)mvwaddstr(gprmcwin, 6, 12, fields[2]);
- (void)mvwaddstr(gprmcwin, 6, 25, fields[12]);
- (void)mvwprintw(gprmcwin, 7, 12, "%-5s%s", fields[10],
- fields[11]);
- cooked_pvt(); /* cooked version of TPV */
- }
- if (strcmp(fields[0], "GPGSA") == 0
- || strcmp(fields[0], "GNGSA") == 0
- || strcmp(fields[0], "GLGSA") == 0) {
- (void)mvwprintw(gpgsawin, MODE_LINE, 7, "%1s%s", fields[1], fields[2]);
- monitor_satlist(gpgsawin, SATS_LINE, SATS_COL+6);
- (void)mvwprintw(gpgsawin, DOP_LINE, 8, "%-5s", fields[16]);
- (void)mvwprintw(gpgsawin, DOP_LINE, 16, "%-5s", fields[17]);
- (void)mvwprintw(gpgsawin, DOP_LINE, 24, "%-5s", fields[15]);
- monitor_fixframe(gpgsawin);
- }
- toff_update(gpgsawin, TOFF_LINE, 7);
- if (strcmp(fields[0], "GPGGA") == 0
- || strcmp(fields[0], "GNGGA") == 0
- || strcmp(fields[0], "GLGGA") == 0) {
- (void)mvwprintw(gpggawin, 1, 12, "%-17s", fields[1]);
- (void)mvwprintw(gpggawin, 2, 12, "%-17s", fields[2]);
- (void)mvwprintw(gpggawin, 3, 12, "%-17s", fields[4]);
- (void)mvwprintw(gpggawin, 4, 12, "%-17s", fields[9]);
- (void)mvwprintw(gpggawin, 5, 12, "%1.1s", fields[6]);
- (void)mvwprintw(gpggawin, 5, 22, "%2.2s", fields[7]);
- (void)mvwprintw(gpggawin, 6, 12, "%-5.5s", fields[8]);
- (void)mvwprintw(gpggawin, 7, 12, "%-5.5s", fields[11]);
- }
- if (strcmp(fields[0], "GPGST") == 0) {
- (void)mvwprintw(gpgstwin, 1, 6, "%-10s", fields[1]);
- (void)mvwprintw(gpgstwin, 1, 21, "%-8s", fields[2]);
- (void)mvwprintw(gpgstwin, 2, 6, "%-10s", fields[3]);
- (void)mvwprintw(gpgstwin, 2, 21, "%-8s", fields[4]);
- (void)mvwprintw(gpgstwin, 3, 6, "%-10s", fields[5]);
- (void)mvwprintw(gpgstwin, 3, 21, "%-8s", fields[6]);
- (void)mvwprintw(gpgstwin, 4, 6, "%-10s", fields[7]);
- (void)mvwprintw(gpgstwin, 4, 21, "%-8s", fields[8]);
- }
- }
- pps_update(gpgsawin, PPS_LINE, 6);
- }
- #undef SENTENCELINE
- static void nmea_wrap(void)
- {
- (void)delwin(nmeawin);
- (void)delwin(gpgsawin);
- (void)delwin(gpggawin);
- (void)delwin(gprmcwin);
- }
- const struct monitor_object_t nmea_mmt = {
- .initialize = nmea_initialize,
- .update = nmea_update,
- .command = NULL,
- .wrap = nmea_wrap,
- .min_y = 21,.min_x = 80,
- .driver = &driver_nmea0183,
- };
- /*****************************************************************************
- *
- * Extended NMEA support
- *
- *****************************************************************************/
- #if defined(CONTROLSEND_ENABLE) && defined(ASHTECH_ENABLE)
- static void monitor_nmea_send(const char *fmt, ...)
- {
- char buf[BUFSIZ];
- va_list ap;
- va_start(ap, fmt);
- (void)vsnprintf(buf, sizeof(buf) - 5, fmt, ap);
- va_end(ap);
- (void)monitor_control_send((unsigned char *)buf, strlen(buf));
- }
- #endif /* defined(CONTROLSEND_ENABLE) && defined(ASHTECH_ENABLE) */
- /*
- * Yes, it's OK for most of these to be clones of the generic NMEA monitor
- * object except for the pointer to the GPSD driver. That pointer makes
- * a difference, as it will automatically enable stuff like speed-switcher
- * and mode-switcher commands. It's really only necessary to write a
- * separate monitor object if you want to change the device-window
- * display or implement device-specific commands.
- */
- #if defined(GARMIN_ENABLE) && defined(NMEA0183_ENABLE)
- extern const struct gps_type_t driver_garmin;
- const struct monitor_object_t garmin_mmt = {
- .initialize = nmea_initialize,
- .update = nmea_update,
- .command = NULL,
- .wrap = nmea_wrap,
- .min_y = 21,.min_x = 80,
- .driver = &driver_garmin,
- };
- #endif /* GARMIN_ENABLE && NMEA0183_ENABLE */
- #ifdef ASHTECH_ENABLE
- extern const struct gps_type_t driver_ashtech;
- #define ASHTECH_SPEED_9600 5
- #define ASHTECH_SPEED_57600 8
- #ifdef CONTROLSEND_ENABLE
- static int ashtech_command(char line[])
- {
- switch (line[0]) {
- case 'N': /* normal = 9600, GGA+GSA+GSV+RMC+ZDA */
- monitor_nmea_send("$PASHS,NME,ALL,A,OFF"); /* silence outbound chatter */
- monitor_nmea_send("$PASHS,NME,ALL,B,OFF");
- monitor_nmea_send("$PASHS,NME,GGA,A,ON");
- monitor_nmea_send("$PASHS,NME,GSA,A,ON");
- monitor_nmea_send("$PASHS,NME,GSV,A,ON");
- monitor_nmea_send("$PASHS,NME,RMC,A,ON");
- monitor_nmea_send("$PASHS,NME,ZDA,A,ON");
- monitor_nmea_send("$PASHS,INI,%d,%d,,,0,",
- ASHTECH_SPEED_9600, ASHTECH_SPEED_9600);
- (void)sleep(6); /* it takes 4-6 sec for the receiver to reboot */
- monitor_nmea_send("$PASHS,WAS,ON"); /* enable WAAS */
- break;
- case 'R': /* raw = 57600, normal+XPG+POS+SAT+MCA+PBN+SNV */
- monitor_nmea_send("$PASHS,NME,ALL,A,OFF"); /* silence outbound chatter */
- monitor_nmea_send("$PASHS,NME,ALL,B,OFF");
- monitor_nmea_send("$PASHS,NME,GGA,A,ON");
- monitor_nmea_send("$PASHS,NME,GSA,A,ON");
- monitor_nmea_send("$PASHS,NME,GSV,A,ON");
- monitor_nmea_send("$PASHS,NME,RMC,A,ON");
- monitor_nmea_send("$PASHS,NME,ZDA,A,ON");
- monitor_nmea_send("$PASHS,INI,%d,%d,,,0,",
- ASHTECH_SPEED_57600, ASHTECH_SPEED_9600);
- (void)sleep(6); /* it takes 4-6 sec for the receiver to reboot */
- monitor_nmea_send("$PASHS,WAS,ON"); /* enable WAAS */
- monitor_nmea_send("$PASHS,NME,POS,A,ON"); /* Ashtech TPV solution */
- monitor_nmea_send("$PASHS,NME,SAT,A,ON"); /* Ashtech Satellite status */
- monitor_nmea_send("$PASHS,NME,MCA,A,ON"); /* MCA measurements */
- monitor_nmea_send("$PASHS,NME,PBN,A,ON"); /* ECEF TPV solution */
- monitor_nmea_send("$PASHS,NME,SNV,A,ON,10"); /* Almanac data */
- monitor_nmea_send("$PASHS,NME,XMG,A,ON"); /* exception messages */
- break;
- default:
- return COMMAND_UNKNOWN;
- }
- return COMMAND_UNKNOWN;
- }
- #endif /* CONTROLSEND_ENABLE */
- const struct monitor_object_t ashtech_mmt = {
- .initialize = nmea_initialize,
- .update = nmea_update,
- #ifdef CONTROLSEND_ENABLE
- .command = ashtech_command,
- #else
- .command = NULL,
- #endif /* CONTROLSEND_ENABLE */
- .wrap = nmea_wrap,
- .min_y = 21,.min_x = 80,
- .driver = &driver_ashtech,
- };
- #endif /* ASHTECH_ENABLE */
- #ifdef FV18_ENABLE
- extern const struct gps_type_t driver_fv18;
- const struct monitor_object_t fv18_mmt = {
- .initialize = nmea_initialize,
- .update = nmea_update,
- .command = NULL,
- .wrap = nmea_wrap,
- .min_y = 21,.min_x = 80,
- .driver = &driver_fv18,
- };
- #endif /* FV18_ENABLE */
- #ifdef GPSCLOCK_ENABLE
- extern const struct gps_type_t driver_gpsclock;
- const struct monitor_object_t gpsclock_mmt = {
- .initialize = nmea_initialize,
- .update = nmea_update,
- .command = NULL,
- .wrap = nmea_wrap,
- .min_y = 21,.min_x = 80,
- .driver = &driver_gpsclock,
- };
- #endif /* GPSCLOCK_ENABLE */
- #ifdef MTK3301_ENABLE
- extern const struct gps_type_t driver_mtk3301;
- const struct monitor_object_t mtk3301_mmt = {
- .initialize = nmea_initialize,
- .update = nmea_update,
- .command = NULL,
- .wrap = nmea_wrap,
- .min_y = 21,.min_x = 80,
- .driver = &driver_mtk3301,
- };
- #endif /* MTK3301_ENABLE */
- #ifdef AIVDM_ENABLE
- extern const struct gps_type_t driver_aivdm;
- const struct monitor_object_t aivdm_mmt = {
- .initialize = nmea_initialize,
- .update = nmea_update,
- .command = NULL,
- .wrap = nmea_wrap,
- .min_y = 21,.min_x = 80,
- .driver = &driver_aivdm,
- };
- #endif /* AIVDM_ENABLE */
- #endif /* NMEA0183_ENABLE */
|