2 * monitor_nmea.c - gpsmon support for NMEA devices.
4 * To do: Support for GPGLL, GPGBS, GPZDA, PASHR NMEA sentences.
6 * This file is Copyright (c) 2010 by the GPSD project
7 * BSD terms apply: see the file COPYING in the distribution root for details.
16 #endif /* S_SPLINT_S */
21 #include "gpsd_config.h"
27 #endif /* HAVE_NCURSES_H */
32 #include "gpsdclient.h"
35 extern const struct gps_type_t nmea;
37 static WINDOW *cookedwin, *nmeawin, *satwin, *gprmcwin, *gpggawin, *gpgsawin;
38 static double last_tick, tick_interval;
40 /*****************************************************************************
42 * Generic NMEA support
44 *****************************************************************************/
46 #define SENTENCELINE 1 /* index of sentences line in the NMEA window */
47 #define MAXSATS 12 /* max satellites we can display */
49 static bool nmea_initialize(void)
54 cookedwin = derwin(devicewin, 3, 80, 0, 0);
55 (void)wborder(cookedwin, 0, 0, 0, 0, 0, 0, 0, 0);
56 (void)syncok(cookedwin, true);
57 wattrset(cookedwin, A_BOLD);
58 mvwaddstr(cookedwin, 1, 1, "Time: ");
59 mvwaddstr(cookedwin, 1, 31, "Lat: ");
60 mvwaddstr(cookedwin, 1, 55, "Lon: ");
61 mvwaddstr(cookedwin, 2, 34, " Cooked PVT ");
62 wattrset(cookedwin, A_NORMAL);
64 nmeawin = derwin(devicewin, 3, 80, 3, 0);
65 (void)wborder(nmeawin, 0, 0, 0, 0, 0, 0, 0, 0);
66 (void)syncok(nmeawin, true);
67 wattrset(nmeawin, A_BOLD);
68 mvwaddstr(nmeawin, 2, 34, " Sentences ");
69 wattrset(nmeawin, A_NORMAL);
71 satwin = derwin(devicewin, MAXSATS + 3, 20, 6, 0);
72 (void)wborder(satwin, 0, 0, 0, 0, 0, 0, 0, 0), (void)syncok(satwin, true);
73 (void)wattrset(satwin, A_BOLD);
74 (void)mvwprintw(satwin, 1, 1, "Ch PRN Az El S/N");
75 for (i = 0; i < MAXSATS; i++)
76 (void)mvwprintw(satwin, (int)(i + 2), 1, "%2d", i);
77 (void)mvwprintw(satwin, 14, 7, " GSV ");
78 (void)wattrset(satwin, A_NORMAL);
80 gprmcwin = derwin(devicewin, 9, 30, 6, 20);
81 (void)wborder(gprmcwin, 0, 0, 0, 0, 0, 0, 0, 0),
82 (void)syncok(gprmcwin, true);
83 (void)wattrset(gprmcwin, A_BOLD);
84 (void)mvwprintw(gprmcwin, 1, 1, "Time: ");
85 (void)mvwprintw(gprmcwin, 2, 1, "Latitude: ");
86 (void)mvwprintw(gprmcwin, 3, 1, "Longitude: ");
87 (void)mvwprintw(gprmcwin, 4, 1, "Speed: ");
88 (void)mvwprintw(gprmcwin, 5, 1, "Course: ");
89 (void)mvwprintw(gprmcwin, 6, 1, "Status: FAA: ");
90 (void)mvwprintw(gprmcwin, 7, 1, "MagVar: ");
91 (void)mvwprintw(gprmcwin, 8, 12, " RMC ");
92 (void)wattrset(gprmcwin, A_NORMAL);
94 gpgsawin = derwin(devicewin, 5, 30, 15, 20);
95 (void)wborder(gpgsawin, 0, 0, 0, 0, 0, 0, 0, 0),
96 (void)syncok(gpgsawin, true);
97 (void)wattrset(gpgsawin, A_BOLD);
98 (void)mvwprintw(gpgsawin, 1, 1, "Mode: ");
99 (void)mvwprintw(gpgsawin, 2, 1, "Sats: ");
100 (void)mvwprintw(gpgsawin, 3, 1, "DOP: H= V= P=");
101 (void)mvwprintw(gpgsawin, 4, 12, " GSA ");
102 (void)wattrset(gpgsawin, A_NORMAL);
104 gpggawin = derwin(devicewin, 9, 30, 6, 50);
105 (void)wborder(gpggawin, 0, 0, 0, 0, 0, 0, 0, 0),
106 (void)syncok(gpggawin, true);
107 (void)wattrset(gpggawin, A_BOLD);
108 (void)mvwprintw(gpggawin, 1, 1, "Time: ");
109 (void)mvwprintw(gpggawin, 2, 1, "Latitude: ");
110 (void)mvwprintw(gpggawin, 3, 1, "Longitude: ");
111 (void)mvwprintw(gpggawin, 4, 1, "Altitude: ");
112 (void)mvwprintw(gpggawin, 5, 1, "Quality: Sats: ");
113 (void)mvwprintw(gpggawin, 6, 1, "HDOP: ");
114 (void)mvwprintw(gpggawin, 7, 1, "Geoid: ");
115 (void)mvwprintw(gpggawin, 8, 12, " GGA ");
116 (void)wattrset(gpggawin, A_NORMAL);
119 last_tick = timestamp();
121 return (nmeawin != NULL);
124 static void cooked_pvt(void)
128 if (isnan(session.gpsdata.fix.time) == 0) {
129 (void)unix_to_iso8601(session.gpsdata.fix.time, scr, sizeof(scr));
131 (void)snprintf(scr, sizeof(scr), "n/a");
132 (void)mvwprintw(cookedwin, 1, 7, "%-22s", scr);
135 if (session.gpsdata.fix.mode >= MODE_2D
136 && isnan(session.gpsdata.fix.latitude) == 0) {
137 (void)snprintf(scr, sizeof(scr), "%s %c",
138 deg_to_str(deg_ddmmss,
139 fabs(session.gpsdata.fix.latitude)),
140 (session.gpsdata.fix.latitude < 0) ? 'S' : 'N');
142 (void)snprintf(scr, sizeof(scr), "n/a");
143 (void)mvwprintw(cookedwin, 1, 36, "%-17s", scr);
145 if (session.gpsdata.fix.mode >= MODE_2D
146 && isnan(session.gpsdata.fix.longitude) == 0) {
147 (void)snprintf(scr, sizeof(scr), "%s %c",
148 deg_to_str(deg_ddmmss,
149 fabs(session.gpsdata.fix.longitude)),
150 (session.gpsdata.fix.longitude < 0) ? 'W' : 'E');
152 (void)snprintf(scr, sizeof(scr), "n/a");
153 (void)mvwprintw(cookedwin, 1, 60, "%-17s", scr);
157 /*@ -globstate -nullpass (splint is confused) */
158 static void nmea_update(void)
160 static char sentences[NMEA_MAX];
163 assert(cookedwin != NULL);
164 assert(nmeawin != NULL);
165 assert(gpgsawin != NULL);
166 assert(gpggawin != NULL);
167 assert(gprmcwin != NULL);
169 fields = session.driver.nmea.field;
171 if (session.packet.outbuffer[0] == (unsigned char)'$') {
174 getmaxyx(nmeawin, ymax, xmax);
175 if (strstr(sentences, fields[0]) == NULL) {
176 char *s_end = sentences + strlen(sentences);
177 if ((int)(strlen(sentences) + strlen(fields[0])) < xmax - 2) {
179 (void)strlcpy(s_end, fields[0], NMEA_MAX);
185 mvwaddstr(nmeawin, SENTENCELINE, 1, sentences);
189 * If the interval between this and last update is
190 * the longest we've seen yet, boldify the corresponding
194 if (now > last_tick && (now - last_tick) > tick_interval) {
195 char *findme = strstr(sentences, fields[0]);
197 tick_interval = now - last_tick;
198 if (findme != NULL) {
199 mvwchgat(nmeawin, SENTENCELINE, 1, xmax - 13, A_NORMAL, 0,
201 mvwchgat(nmeawin, SENTENCELINE, 1 + (findme - sentences),
202 (int)strlen(fields[0]), A_BOLD, 0, NULL);
207 if (strcmp(fields[0], "GPGSV") == 0
208 || strcmp(fields[0], "GNGSV") == 0
209 || strcmp(fields[0], "GLGSV") == 0) {
212 (session.gpsdata.satellites_visible <
213 MAXSATS) ? session.gpsdata.satellites_visible : MAXSATS;
215 for (i = 0; i < nsats; i++) {
216 (void)wmove(satwin, i + 2, 3);
217 (void)wprintw(satwin, " %3d %3d%3d %3.0f",
218 session.gpsdata.PRN[i],
219 session.gpsdata.azimuth[i],
220 session.gpsdata.elevation[i],
221 session.gpsdata.ss[i]);
223 /* add overflow mark to the display */
224 if (nsats <= MAXSATS)
225 (void)mvwaddch(satwin, MAXSATS + 2, 18, ACS_HLINE);
227 (void)mvwaddch(satwin, MAXSATS + 2, 18, ACS_DARROW);
230 if (strcmp(fields[0], "GPRMC") == 0
231 || strcmp(fields[0], "GNRMC") == 0
232 || strcmp(fields[0], "GLRMC") == 0) {
233 /* time, lat, lon, course, speed */
234 (void)mvwaddstr(gprmcwin, 1, 12, fields[1]);
235 (void)mvwprintw(gprmcwin, 2, 12, "%12s %s", fields[3], fields[4]);
236 (void)mvwprintw(gprmcwin, 3, 12, "%12s %s", fields[5], fields[6]);
237 (void)mvwaddstr(gprmcwin, 4, 12, fields[7]);
238 (void)mvwaddstr(gprmcwin, 5, 12, fields[8]);
239 /* the status field, FAA code, and magnetic variation */
240 (void)mvwaddstr(gprmcwin, 6, 12, fields[2]);
241 (void)mvwaddstr(gprmcwin, 6, 25, fields[12]);
242 (void)mvwprintw(gprmcwin, 7, 12, "%-5s%s", fields[10],
245 cooked_pvt(); /* cooked version of PVT */
248 if (strcmp(fields[0], "GPGSA") == 0
249 || strcmp(fields[0], "GNGSA") == 0
250 || strcmp(fields[0], "GLGSA") == 0) {
253 (void)mvwprintw(gpgsawin, 1, 7, "%1s %s", fields[1], fields[2]);
254 (void)wmove(gpgsawin, 2, 7);
255 (void)wclrtoeol(gpgsawin);
257 for (i = 0; i < session.gpsdata.satellites_used; i++) {
258 (void)snprintf(scr + strlen(scr), sizeof(scr) - strlen(scr),
259 "%d ", session.gpsdata.used[i]);
261 getmaxyx(gpgsawin, ymax, xmax);
262 (void)mvwaddnstr(gpgsawin, 2, 7, scr, xmax - 2 - 7);
263 if (strlen(scr) >= (size_t) (xmax - 2)) {
264 mvwaddch(gpgsawin, 2, xmax - 2 - 7, (chtype) '.');
265 mvwaddch(gpgsawin, 2, xmax - 3 - 7, (chtype) '.');
266 mvwaddch(gpgsawin, 2, xmax - 4 - 7, (chtype) '.');
268 monitor_fixframe(gpgsawin);
269 (void)mvwprintw(gpgsawin, 3, 8, "%-5s", fields[16]);
270 (void)mvwprintw(gpgsawin, 3, 16, "%-5s", fields[17]);
271 (void)mvwprintw(gpgsawin, 3, 24, "%-5s", fields[15]);
272 monitor_fixframe(gpgsawin);
274 if (strcmp(fields[0], "GPGGA") == 0
275 || strcmp(fields[0], "GNGGA") == 0
276 || strcmp(fields[0], "GLGGA") == 0) {
277 (void)mvwprintw(gpggawin, 1, 12, "%-17s", fields[1]);
278 (void)mvwprintw(gpggawin, 2, 12, "%-17s", fields[2]);
279 (void)mvwprintw(gpggawin, 3, 12, "%-17s", fields[4]);
280 (void)mvwprintw(gpggawin, 4, 12, "%-17s", fields[9]);
281 (void)mvwprintw(gpggawin, 5, 12, "%1.1s", fields[6]);
282 (void)mvwprintw(gpggawin, 5, 22, "%2.2s", fields[7]);
283 (void)mvwprintw(gpggawin, 6, 12, "%-5.5s", fields[8]);
284 (void)mvwprintw(gpggawin, 7, 12, "%-5.5s", fields[11]);
289 /*@ +globstate +nullpass */
293 static void nmea_wrap(void)
295 (void)delwin(nmeawin);
296 (void)delwin(gpgsawin);
297 (void)delwin(gpggawin);
298 (void)delwin(gprmcwin);
301 const struct monitor_object_t nmea_mmt = {
302 .initialize = nmea_initialize,
303 .update = nmea_update,
306 .min_y = 21,.min_x = 80,
310 /*****************************************************************************
312 * Extended NMEA support
314 *****************************************************************************/
316 #ifdef ALLOW_CONTROLSEND
317 static void monitor_nmea_send(const char *fmt, ...)
323 (void)vsnprintf(buf, sizeof(buf) - 5, fmt, ap);
325 (void)monitor_control_send((unsigned char *)buf, strlen(buf));
327 #endif /* ALLOW_CONTROLSEND */
330 * Yes, it's OK for most of these to be clones of the generic NMEA monitor
331 * object except for the pointer to the GPSD driver. That pointer makes
332 * a difference, as it will automatically enable stuff like speed-switcher
333 * and mode-switcher commands. It's really only necessary to write a
334 * separate monitor object if you want to change the device-window
335 * display or implement device-specific commands.
338 #if defined(GARMIN_ENABLE) && defined(NMEA_ENABLE)
339 extern const struct gps_type_t garmin;
341 const struct monitor_object_t garmin_mmt = {
342 .initialize = nmea_initialize,
343 .update = nmea_update,
346 .min_y = 21,.min_x = 80,
349 #endif /* GARMIN_ENABLE && NMEA_ENABLE */
351 #ifdef ASHTECH_ENABLE
352 extern const struct gps_type_t ashtech;
354 #define ASHTECH_SPEED_9600 5
355 #define ASHTECH_SPEED_57600 8
357 #ifdef ALLOW_CONTROLSEND
358 static int ashtech_command(char line[])
361 case 'N': /* normal = 9600, GGA+GSA+GSV+RMC+ZDA */
362 monitor_nmea_send("$PASHS,NME,ALL,A,OFF"); /* silence outbound chatter */
363 monitor_nmea_send("$PASHS,NME,ALL,B,OFF");
364 monitor_nmea_send("$PASHS,NME,GGA,A,ON");
365 monitor_nmea_send("$PASHS,NME,GSA,A,ON");
366 monitor_nmea_send("$PASHS,NME,GSV,A,ON");
367 monitor_nmea_send("$PASHS,NME,RMC,A,ON");
368 monitor_nmea_send("$PASHS,NME,ZDA,A,ON");
370 monitor_nmea_send("$PASHS,INI,%d,%d,,,0,",
371 ASHTECH_SPEED_9600, ASHTECH_SPEED_9600);
372 (void)sleep(6); /* it takes 4-6 sec for the receiver to reboot */
373 monitor_nmea_send("$PASHS,WAS,ON"); /* enable WAAS */
376 case 'R': /* raw = 57600, normal+XPG+POS+SAT+MCA+PBN+SNV */
377 monitor_nmea_send("$PASHS,NME,ALL,A,OFF"); /* silence outbound chatter */
378 monitor_nmea_send("$PASHS,NME,ALL,B,OFF");
379 monitor_nmea_send("$PASHS,NME,GGA,A,ON");
380 monitor_nmea_send("$PASHS,NME,GSA,A,ON");
381 monitor_nmea_send("$PASHS,NME,GSV,A,ON");
382 monitor_nmea_send("$PASHS,NME,RMC,A,ON");
383 monitor_nmea_send("$PASHS,NME,ZDA,A,ON");
385 monitor_nmea_send("$PASHS,INI,%d,%d,,,0,",
386 ASHTECH_SPEED_57600, ASHTECH_SPEED_9600);
387 (void)sleep(6); /* it takes 4-6 sec for the receiver to reboot */
388 monitor_nmea_send("$PASHS,WAS,ON"); /* enable WAAS */
390 monitor_nmea_send("$PASHS,NME,POS,A,ON"); /* Ashtech PVT solution */
391 monitor_nmea_send("$PASHS,NME,SAT,A,ON"); /* Ashtech Satellite status */
392 monitor_nmea_send("$PASHS,NME,MCA,A,ON"); /* MCA measurements */
393 monitor_nmea_send("$PASHS,NME,PBN,A,ON"); /* ECEF PVT solution */
394 monitor_nmea_send("$PASHS,NME,SNV,A,ON,10"); /* Almanac data */
396 monitor_nmea_send("$PASHS,NME,XMG,A,ON"); /* exception messages */
400 return COMMAND_UNKNOWN;
403 return COMMAND_UNKNOWN;
405 #endif /* ALLOW_CONTROLSEND */
407 const struct monitor_object_t ashtech_mmt = {
408 .initialize = nmea_initialize,
409 .update = nmea_update,
410 #ifdef ALLOW_CONTROLSEND
411 .command = ashtech_command,
414 #endif /* ALLOW_CONTROLSEND */
416 .min_y = 21,.min_x = 80,
419 #endif /* ASHTECH_ENABLE */
422 extern const struct gps_type_t fv18;
424 const struct monitor_object_t fv18_mmt = {
425 .initialize = nmea_initialize,
426 .update = nmea_update,
429 .min_y = 21,.min_x = 80,
432 #endif /* FV18_ENABLE */
434 #ifdef GPSCLOCK_ENABLE
435 extern const struct gps_type_t gpsclock;
437 const struct monitor_object_t gpsclock_mmt = {
438 .initialize = nmea_initialize,
439 .update = nmea_update,
442 .min_y = 21,.min_x = 80,
445 #endif /* GPSCLOCK_ENABLE */
447 #ifdef MTK3301_ENABLE
448 extern const struct gps_type_t mtk3301;
450 const struct monitor_object_t mtk3301_mmt = {
451 .initialize = nmea_initialize,
452 .update = nmea_update,
455 .min_y = 21,.min_x = 80,
458 #endif /* MTK3301_ENABLE */
460 #endif /* NMEA_ENABLE */