2 * Copyright (c) 2005 Jeff Francis <jeff@gritch.org>
3 * BSD terms apply: see the filr COPYING in the distribution root for details.
10 Kind of a curses version of xgps for use with gpsd.
14 * The True North compass fails with current gpsd versions for reasons
15 * the dev team has been unable to diagnose due to not having test hardware.
16 * The sup[port for it is conditioned out in order to simplify moving
17 * to the new JSON-based oprotocol and reduce startup time.
21 /* ==================================================================
22 These #defines should be modified if changing the number of fields
24 ================================================================== */
26 /* This defines how much overhead is contained in the 'datawin' window
27 (eg, box around the window takes two lines). */
28 #define DATAWIN_OVERHEAD 2
30 /* This defines how much overhead is contained in the 'satellites'
31 window (eg, box around the window takes two lines, plus the column
32 headers take another line). */
33 #define SATWIN_OVERHEAD 3
35 /* This is how many display fields are output in the 'datawin' window
36 when in GPS mode. Change this value if you add or remove fields
37 from the 'datawin' window for the GPS mode. */
38 #define DATAWIN_GPS_FIELDS 9
40 /* This is how many display fields are output in the 'datawin' window
41 when in COMPASS mode. Change this value if you add or remove fields
42 from the 'datawin' window for the COMPASS mode. */
43 #define DATAWIN_COMPASS_FIELDS 6
45 /* This is how far over in the 'datawin' window to indent the field
47 #define DATAWIN_DESC_OFFSET 5
49 /* This is how far over in the 'datawin' window to indent the field
51 #define DATAWIN_VALUE_OFFSET 17
53 /* This is the width of the 'datawin' window. It's recommended to
54 keep DATAWIN_WIDTH + SATELLITES_WIDTH <= 80 so it'll fit on a
55 "standard" 80x24 screen. */
56 #define DATAWIN_WIDTH 45
58 /* This is the width of the 'satellites' window. It's recommended to
59 keep DATAWIN_WIDTH + SATELLITES_WIDTH <= 80 so it'll fit on a
60 "standard" 80x24 screen. */
61 #define SATELLITES_WIDTH 35
63 /* ================================================================
64 You shouldn't have to modify any #define values below this line.
65 ================================================================ */
67 /* This is the minimum size we'll accept for the 'datawin' window in
69 #define MIN_GPS_DATAWIN_SIZE (DATAWIN_GPS_FIELDS + DATAWIN_OVERHEAD)
71 /* This is the minimum size we'll accept for the 'datawin' window in
73 #define MIN_COMPASS_DATAWIN_SIZE (DATAWIN_COMPASS_FIELDS + DATAWIN_OVERHEAD)
75 /* This is the maximum number of satellites gpsd can track. */
76 #define MAX_POSSIBLE_SATS (MAXCHANNELS - 2)
78 /* This is the maximum size we need for the 'satellites' window. */
79 #define MAX_SATWIN_SIZE (MAX_POSSIBLE_SATS + SATWIN_OVERHEAD)
81 #include <sys/types.h>
82 #include <sys/select.h>
84 #include <sys/socket.h>
86 #endif /* S_SPLINT_S */
97 #include "gpsd_config.h"
102 #endif /* HAVE_NCURSES_H */
105 #include "gpsdclient.h"
106 #include "revision.h"
108 static struct gps_data_t gpsdata;
109 static time_t status_timer; /* Time of last state change. */
110 static int state = 0; /* or MODE_NO_FIX=1, MODE_2D=2, MODE_3D=3 */
111 static float altfactor = METERS_TO_FEET;
112 static float speedfactor = MPS_TO_MPH;
113 static char *altunits = "ft";
114 static char *speedunits = "mph";
115 static struct fixsource_t source;
116 #ifdef CLIENTDEBUG_ENABLE
118 #endif /* CLIENTDEBUG_ENABLE */
120 static WINDOW *datawin, *satellites, *messages;
122 static bool raw_flag = false;
123 static bool silent_flag = false;
124 static bool magnetic_flag = false;
125 static int window_length;
126 static int display_sats;
128 static bool compass_flag = false;
129 #endif /* TRUENORTH */
131 /* pseudo-signals indicating reason for termination */
132 #define CGPS_QUIT 0 /* voluntary yterminastion */
133 #define GPS_GONE -1 /* GPS device went away */
134 #define GPS_ERROR -2 /* low-level failure in GPS read */
136 /* Convert true heading to magnetic. Taken from the Aviation
137 Formulary v1.43. Valid to within two degrees within the
138 continiental USA except for the following airports: MO49 MO86 MO50
139 3K6 02K and KOOA. AK correct to better than one degree. Western
140 Europe correct to within 0.2 deg.
142 If you're not in one of these areas, I apologize, I don't have the
143 math to compute your varation. This is obviously extremely
144 floating-point heavy, so embedded people, beware of using.
146 Note that there are issues with using magnetic heading. This code
147 does not account for the possibility of travelling into or out of
148 an area of valid calculation beyond forcing the magnetic conversion
149 off. A better way to communicate this to the user is probably
150 desirable (in case the don't notice the subtle change from "(mag)"
151 to "(true)" on their display).
153 static float true2magnetic(double lat, double lon, double heading)
156 /*@ -evalorder +relaxtypes @*/
157 if ((lat > 36.0) && (lat < 68.0) && (lon > -10.0) && (lon < 28.0)) {
159 (10.4768771667158 - (0.507385322418858 * lon) +
160 (0.00753170031703826 * pow(lon, 2))
161 - (1.40596203924748e-05 * pow(lon, 3)) -
162 (0.535560699962353 * lat)
163 + (0.0154348808069955 * lat * lon) -
164 (8.07756425110592e-05 * lat * pow(lon, 2))
165 + (0.00976887198864442 * pow(lat, 2)) -
166 (0.000259163929798334 * lon * pow(lat, 2))
167 - (3.69056939266123e-05 * pow(lat, 3)) + heading);
170 else if ((lat > 24.0) && (lat < 50.0) && (lon > 66.0) && (lon < 125.0)) {
173 ((-65.6811) + (0.99 * lat) + (0.0128899 * pow(lat, 2)) -
174 (0.0000905928 * pow(lat, 3)) + (2.87622 * lon)
175 - (0.0116268 * lat * lon) - (0.00000603925 * lon * pow(lat, 2)) -
176 (0.0389806 * pow(lon, 2))
177 - (0.0000403488 * lat * pow(lon, 2)) +
178 (0.000168556 * pow(lon, 3)) + heading);
181 else if ((lat > 54.0) && (lon > 130.0) && (lon < 172.0)) {
184 (618.854 + (2.76049 * lat) - (0.556206 * pow(lat, 2)) +
185 (0.00251582 * pow(lat, 3)) - (12.7974 * lon)
186 + (0.408161 * lat * lon) + (0.000434097 * lon * pow(lat, 2)) -
187 (0.00602173 * pow(lon, 2))
188 - (0.00144712 * lat * pow(lon, 2)) +
189 (0.000222521 * pow(lon, 3)) + heading);
191 /* We don't know how to compute magnetic heading for this
193 magnetic_flag = false;
196 /* No negative headings. */
201 /*@ +evalorder -relaxtypes @*/
204 /* Function to call when we're all done. Does a bit of clean-up. */
205 static void die(int sig)
207 /* Ignore signals. */
208 (void)signal(SIGINT, SIG_IGN);
209 (void)signal(SIGHUP, SIG_IGN);
211 /* Move the cursor to the bottom left corner. */
212 (void)mvcur(0, COLS - 1, LINES - 1, 0);
214 /* Put input attributes back the way they were. */
217 /* Done with curses. */
220 /* We're done talking to gpsd. */
221 (void)gps_close(&gpsdata);
227 (void)fprintf(stderr, "cgps: GPS hung up.\n");
230 (void)fprintf(stderr, "cgps: GPS read returned error\n");
233 (void)fprintf(stderr, "cgps: caught signal %d\n", sig);
241 static enum deg_str_type deg_type = deg_dd;
244 static void windowsetup(void)
246 /* Set the window sizes per the following criteria:
248 * 1. Set the window size to display the maximum number of
249 * satellites possible, but not more than the size required to
250 * display the maximum number of satellites gpsd is capable of
251 * tracking (MAXCHANNELS - 2).
253 * 2. If the screen size will not allow for the full complement of
254 * satellites to be displayed, set the windows sizes smaller, but
255 * not smaller than the number of lines necessary to display all of
256 * the fields in the 'datawin'. The list of displayed satellites
257 * will be truncated to fit the available window size. (TODO: If
258 * the satellite list is truncated, omit the satellites not used to
259 * obtain the current fix.)
261 * 3. If the screen is large enough to display all possible
262 * satellites (MAXCHANNELS - 2) with space still left at the bottom,
263 * add a window at the bottom in which to scroll raw gpsd data.
267 getmaxyx(stdscr, ysize, xsize);
271 if (ysize == MIN_COMPASS_DATAWIN_SIZE) {
273 window_length = MIN_COMPASS_DATAWIN_SIZE;
274 } else if (ysize > MIN_COMPASS_DATAWIN_SIZE) {
276 window_length = MIN_COMPASS_DATAWIN_SIZE;
279 "Your screen must be at least 80x%d to run cgps.",
280 MIN_COMPASS_DATAWIN_SIZE);
288 #endif /* TRUENORTH */
290 if (ysize == MAX_SATWIN_SIZE) {
292 window_length = MAX_SATWIN_SIZE;
293 display_sats = MAX_POSSIBLE_SATS;
294 } else if (ysize == MAX_SATWIN_SIZE + 1) {
296 window_length = MAX_SATWIN_SIZE;
297 display_sats = MAX_POSSIBLE_SATS;
298 } else if (ysize > MAX_SATWIN_SIZE + 2) {
300 window_length = MAX_SATWIN_SIZE;
301 display_sats = MAX_POSSIBLE_SATS;
302 } else if (ysize > MIN_GPS_DATAWIN_SIZE) {
304 window_length = ysize - (int)raw_flag;
305 display_sats = window_length - SATWIN_OVERHEAD - (int)raw_flag;
306 } else if (ysize == MIN_GPS_DATAWIN_SIZE) {
308 window_length = MIN_GPS_DATAWIN_SIZE;
309 display_sats = window_length - SATWIN_OVERHEAD - 1;
312 "Your screen must be at least 80x%d to run cgps.",
313 MIN_GPS_DATAWIN_SIZE);
323 /* Set up the screen for either a compass or a gps receiver. */
325 /* We're a compass, set up accordingly. */
328 datawin = newwin(window_length, DATAWIN_WIDTH, 0, 0);
329 (void)nodelay(datawin, (bool) TRUE);
331 messages = newwin(0, 0, window_length, 0);
334 (void)scrollok(messages, true);
335 (void)wsetscrreg(messages, 0, ysize - (window_length));
342 /* Do the initial field label setup. */
343 (void)mvwprintw(datawin, 1, DATAWIN_DESC_OFFSET, "Time:");
344 (void)mvwprintw(datawin, 2, DATAWIN_DESC_OFFSET, "Heading:");
345 (void)mvwprintw(datawin, 3, DATAWIN_DESC_OFFSET, "Pitch:");
346 (void)mvwprintw(datawin, 4, DATAWIN_DESC_OFFSET, "Roll:");
347 (void)mvwprintw(datawin, 5, DATAWIN_DESC_OFFSET, "Dip:");
348 (void)mvwprintw(datawin, 6, DATAWIN_DESC_OFFSET, "Rcvr Type:");
349 (void)wborder(datawin, 0, 0, 0, 0, 0, 0, 0, 0);
352 #endif /* TRUENORTH */
354 /* We're a GPS, set up accordingly. */
357 datawin = newwin(window_length, DATAWIN_WIDTH, 0, 0);
359 newwin(window_length, SATELLITES_WIDTH, 0, DATAWIN_WIDTH);
360 (void)nodelay(datawin, (bool) TRUE);
363 newwin(ysize - (window_length), xsize, window_length, 0);
366 (void)scrollok(messages, true);
367 (void)wsetscrreg(messages, 0, ysize - (window_length));
374 /* Do the initial field label setup. */
375 (void)mvwprintw(datawin, 1, DATAWIN_DESC_OFFSET, "Time:");
376 (void)mvwprintw(datawin, 2, DATAWIN_DESC_OFFSET, "Latitude:");
377 (void)mvwprintw(datawin, 3, DATAWIN_DESC_OFFSET, "Longitude:");
378 (void)mvwprintw(datawin, 4, DATAWIN_DESC_OFFSET, "Altitude:");
379 (void)mvwprintw(datawin, 5, DATAWIN_DESC_OFFSET, "Speed:");
380 (void)mvwprintw(datawin, 6, DATAWIN_DESC_OFFSET, "Heading:");
381 (void)mvwprintw(datawin, 7, DATAWIN_DESC_OFFSET, "Climb:");
382 (void)mvwprintw(datawin, 8, DATAWIN_DESC_OFFSET, "Status:");
383 (void)mvwprintw(datawin, 9, DATAWIN_DESC_OFFSET, "GPS Type:");
385 /* Note that the following four fields are exceptions to the
386 * sizing rule. The minimum window size does not include these
387 * fields, if the window is too small, they get excluded. This
388 * may or may not change if/when the output for these fields is
389 * fixed and/or people request their permanance. They're only
390 * there in the first place because I arbitrarily thought they
391 * sounded interesting. ;^) */
393 if (window_length >= (MIN_GPS_DATAWIN_SIZE + 5)) {
394 (void)mvwprintw(datawin, 10, DATAWIN_DESC_OFFSET,
396 (void)mvwprintw(datawin, 11, DATAWIN_DESC_OFFSET,
398 (void)mvwprintw(datawin, 12, DATAWIN_DESC_OFFSET,
400 (void)mvwprintw(datawin, 13, DATAWIN_DESC_OFFSET, "Course Err:");
401 (void)mvwprintw(datawin, 14, DATAWIN_DESC_OFFSET, "Speed Err:");
404 (void)wborder(datawin, 0, 0, 0, 0, 0, 0, 0, 0);
405 (void)mvwprintw(satellites, 1, 1, "PRN: Elev: Azim: SNR: Used:");
406 (void)wborder(satellites, 0, 0, 0, 0, 0, 0, 0, 0);
413 /* This gets called once for each new compass sentence. */
414 static void update_compass_panel(struct gps_data_t *gpsdata,
415 char *message, size_t len UNUSED)
418 /* Print time/date. */
419 if (isnan(gpsdata->fix.time) == 0) {
420 (void)unix_to_iso8601(gpsdata->fix.time, scr, sizeof(scr));
422 (void)snprintf(scr, sizeof(scr), "n/a");
423 (void)mvwprintw(datawin, 1, DATAWIN_VALUE_OFFSET, "%-*s", 27, scr);
425 /* Fill in the heading. */
426 if (isnan(gpsdata->fix.track) == 0) {
427 (void)snprintf(scr, sizeof(scr), "%.1f degrees", gpsdata->fix.track);
429 (void)snprintf(scr, sizeof(scr), "n/a");
430 (void)mvwprintw(datawin, 2, DATAWIN_VALUE_OFFSET, "%-*s", 27, scr);
432 /* Fill in the pitch. */
433 if (isnan(gpsdata->fix.climb) == 0) {
434 (void)snprintf(scr, sizeof(scr), "%.1f", gpsdata->fix.climb);
436 (void)snprintf(scr, sizeof(scr), "n/a");
437 (void)mvwprintw(datawin, 3, DATAWIN_VALUE_OFFSET, "%-*s", 27, scr);
439 /* Fill in the roll. */
440 if (isnan(gpsdata->fix.speed) == 0)
441 (void)snprintf(scr, sizeof(scr), "%.1f", gpsdata->fix.speed);
443 (void)snprintf(scr, sizeof(scr), "n/a");
444 (void)mvwprintw(datawin, 4, DATAWIN_VALUE_OFFSET, "%-*s", 27, scr);
446 /* Fill in the speed. */
447 if (isnan(gpsdata->fix.altitude) == 0)
448 (void)snprintf(scr, sizeof(scr), "%.1f", gpsdata->fix.altitude);
450 (void)snprintf(scr, sizeof(scr), "n/a");
451 (void)mvwprintw(datawin, 5, DATAWIN_VALUE_OFFSET, "%-*s", 27, scr);
453 /* When we need to fill in receiver type again, do it here. */
454 (void)mvwprintw(datawin, 6, DATAWIN_VALUE_OFFSET, "%-*s", 27, scr);
456 /* Be quiet if the user requests silence. */
457 if (!silent_flag && raw_flag) {
458 (void)waddstr(messages, message);
461 (void)wrefresh(datawin);
463 (void)wrefresh(messages);
466 #endif /* TRUENORTH */
468 /* This gets called once for each new GPS sentence. */
469 static void update_gps_panel(struct gps_data_t *gpsdata,
470 char *message, size_t len UNUSED)
475 bool usedflags[MAXCHANNELS];
477 /* this is where we implement source-device filtering */
478 if (gpsdata->dev.path[0] != '\0' && source.device != NULL
479 && strcmp(source.device, gpsdata->dev.path) != 0)
482 /* must build bit vector of which statellites are used from list */
483 for (i = 0; i < MAXCHANNELS; i++) {
484 usedflags[i] = false;
485 for (j = 0; j < gpsdata->satellites_used; j++)
486 if (gpsdata->used[j] == gpsdata->PRN[i])
490 /* This is for the satellite status display. Originally lifted from
491 * xgps.c. Note that the satellite list may be truncated based on
492 * available screen size, or may only show satellites used for the
494 if (gpsdata->satellites_visible != 0) {
495 if (display_sats >= MAX_POSSIBLE_SATS) {
496 for (i = 0; i < MAX_POSSIBLE_SATS; i++) {
497 if (i < gpsdata->satellites_visible) {
498 (void)snprintf(scr, sizeof(scr),
499 " %3d %02d %03d %02d %c",
501 gpsdata->elevation[i], gpsdata->azimuth[i],
503 usedflags[i] ? 'Y' : 'N');
505 (void)strlcpy(scr, "", sizeof(scr));
507 (void)mvwprintw(satellites, i + 2, 1, "%-*s",
508 SATELLITES_WIDTH - 3, scr);
512 for (i = 0; i < MAX_POSSIBLE_SATS; i++) {
513 if (n < display_sats) {
514 if ((i < gpsdata->satellites_visible)
515 && ((gpsdata->used[i] != 0)
516 || (gpsdata->satellites_visible <= display_sats))) {
517 (void)snprintf(scr, sizeof(scr),
518 " %3d %02d %03d %02d %c",
519 gpsdata->PRN[i], gpsdata->elevation[i],
522 gpsdata->used[i] ? 'Y' : 'N');
523 (void)mvwprintw(satellites, n + 2, 1, "%-*s",
524 SATELLITES_WIDTH - 3, scr);
530 if (n < display_sats) {
531 for (i = n; i <= display_sats; i++) {
532 (void)mvwprintw(satellites, i + 2, 1, "%-*s",
533 SATELLITES_WIDTH - 3, "");
540 /* Print time/date. */
541 if (isnan(gpsdata->fix.time) == 0) {
542 (void)unix_to_iso8601(gpsdata->fix.time, scr, sizeof(scr));
544 (void)snprintf(scr, sizeof(scr), "n/a");
545 (void)mvwprintw(datawin, 1, DATAWIN_VALUE_OFFSET, "%-*s", 27, scr);
548 /* Fill in the latitude. */
549 if (gpsdata->fix.mode >= MODE_2D && isnan(gpsdata->fix.latitude) == 0) {
550 (void)snprintf(scr, sizeof(scr), "%s %c",
551 deg_to_str(deg_type, fabs(gpsdata->fix.latitude)),
552 (gpsdata->fix.latitude < 0) ? 'S' : 'N');
554 (void)snprintf(scr, sizeof(scr), "n/a");
555 (void)mvwprintw(datawin, 2, DATAWIN_VALUE_OFFSET, "%-*s", 27, scr);
557 /* Fill in the longitude. */
558 if (gpsdata->fix.mode >= MODE_2D && isnan(gpsdata->fix.longitude) == 0) {
559 (void)snprintf(scr, sizeof(scr), "%s %c",
560 deg_to_str(deg_type, fabs(gpsdata->fix.longitude)),
561 (gpsdata->fix.longitude < 0) ? 'W' : 'E');
563 (void)snprintf(scr, sizeof(scr), "n/a");
564 (void)mvwprintw(datawin, 3, DATAWIN_VALUE_OFFSET, "%-*s", 27, scr);
566 /* Fill in the altitude. */
567 if (gpsdata->fix.mode == MODE_3D && isnan(gpsdata->fix.altitude) == 0)
568 (void)snprintf(scr, sizeof(scr), "%.1f %s",
569 gpsdata->fix.altitude * altfactor, altunits);
571 (void)snprintf(scr, sizeof(scr), "n/a");
572 (void)mvwprintw(datawin, 4, DATAWIN_VALUE_OFFSET, "%-*s", 27, scr);
574 /* Fill in the speed. */
575 if (gpsdata->fix.mode >= MODE_2D && isnan(gpsdata->fix.track) == 0)
576 (void)snprintf(scr, sizeof(scr), "%.1f %s",
577 gpsdata->fix.speed * speedfactor, speedunits);
579 (void)snprintf(scr, sizeof(scr), "n/a");
580 (void)mvwprintw(datawin, 5, DATAWIN_VALUE_OFFSET, "%-*s", 27, scr);
582 /* Fill in the heading. */
583 if (gpsdata->fix.mode >= MODE_2D && isnan(gpsdata->fix.track) == 0)
584 if (!magnetic_flag) {
585 (void)snprintf(scr, sizeof(scr), "%.1f deg (true)",
588 (void)snprintf(scr, sizeof(scr), "%.1f deg (mag) ",
589 true2magnetic(gpsdata->fix.latitude,
590 gpsdata->fix.longitude,
591 gpsdata->fix.track));
593 (void)snprintf(scr, sizeof(scr), "n/a");
594 (void)mvwprintw(datawin, 6, DATAWIN_VALUE_OFFSET, "%-*s", 27, scr);
596 /* Fill in the rate of climb. */
597 if (gpsdata->fix.mode == MODE_3D && isnan(gpsdata->fix.climb) == 0)
598 (void)snprintf(scr, sizeof(scr), "%.1f %s/min",
599 gpsdata->fix.climb * altfactor * 60, altunits);
601 (void)snprintf(scr, sizeof(scr), "n/a");
602 (void)mvwprintw(datawin, 7, DATAWIN_VALUE_OFFSET, "%-*s", 27, scr);
604 /* Fill in the GPS status and the time since the last state
606 if (gpsdata->online == 0) {
608 (void)snprintf(scr, sizeof(scr), "OFFLINE");
610 newstate = gpsdata->fix.mode;
611 switch (gpsdata->fix.mode) {
613 (void)snprintf(scr, sizeof(scr), "2D %sFIX (%d secs)",
615 STATUS_DGPS_FIX) ? "DIFF " : "",
616 (int)(time(NULL) - status_timer));
619 (void)snprintf(scr, sizeof(scr), "3D %sFIX (%d secs)",
621 STATUS_DGPS_FIX) ? "DIFF " : "",
622 (int)(time(NULL) - status_timer));
625 (void)snprintf(scr, sizeof(scr), "NO FIX (%d secs)",
626 (int)(time(NULL) - status_timer));
630 (void)mvwprintw(datawin, 8, DATAWIN_VALUE_OFFSET, "%-*s", 27, scr);
632 /* Fill in receiver type. */
633 if (gpsdata->set & (DEVICE_SET | DEVICELIST_SET)) {
634 #ifdef CLIENTDEBUG_ENABLE
636 (void)fprintf(stderr, "Device ID or list set.\n");
638 if (gpsdata->set & DEVICE_SET) {
639 (void)snprintf(scr, sizeof(scr), "%s", gpsdata->dev.driver);
640 } else if (gpsdata->devices.ndevices == 1) {
641 (void)snprintf(scr, sizeof(scr), "%s",
642 gpsdata->devices.list[0].driver);
644 (void)snprintf(scr, sizeof(scr), "%d devices",
645 gpsdata->devices.ndevices);
647 (void)mvwprintw(datawin, 9, DATAWIN_VALUE_OFFSET, "%-*s", 27, scr);
649 /* Note that the following four fields are exceptions to the
650 * sizing rule. The minimum window size does not include these
651 * fields, if the window is too small, they get excluded. This
652 * may or may not change if/when the output for these fields is
653 * fixed and/or people request their permanance. They're only
654 * there in the first place because I arbitrarily thought they
655 * sounded interesting. ;^) */
657 if (window_length >= (MIN_GPS_DATAWIN_SIZE + 4)) {
659 /* Fill in the estimated horizontal position error. */
660 if (isnan(gpsdata->fix.epx) == 0)
661 (void)snprintf(scr, sizeof(scr), "+/- %d %s",
662 (int)(gpsdata->fix.epx * altfactor), altunits);
664 (void)snprintf(scr, sizeof(scr), "n/a");
665 (void)mvwprintw(datawin, 10, DATAWIN_VALUE_OFFSET + 5, "%-*s", 22,
668 if (isnan(gpsdata->fix.epy) == 0)
669 (void)snprintf(scr, sizeof(scr), "+/- %d %s",
670 (int)(gpsdata->fix.epy * altfactor), altunits);
672 (void)snprintf(scr, sizeof(scr), "n/a");
673 (void)mvwprintw(datawin, 11, DATAWIN_VALUE_OFFSET + 5, "%-*s", 22,
676 /* Fill in the estimated vertical position error. */
677 if (isnan(gpsdata->fix.epv) == 0)
678 (void)snprintf(scr, sizeof(scr), "+/- %d %s",
679 (int)(gpsdata->fix.epv * altfactor), altunits);
681 (void)snprintf(scr, sizeof(scr), "n/a");
682 (void)mvwprintw(datawin, 12, DATAWIN_VALUE_OFFSET + 5, "%-*s", 22,
685 /* Fill in the estimated track error. */
686 if (isnan(gpsdata->fix.epd) == 0)
687 (void)snprintf(scr, sizeof(scr), "+/- %d deg",
688 (int)(gpsdata->fix.epd));
690 (void)snprintf(scr, sizeof(scr), "n/a");
691 (void)mvwprintw(datawin, 13, DATAWIN_VALUE_OFFSET + 5, "%-*s", 22,
694 /* Fill in the estimated speed error. */
695 if (isnan(gpsdata->fix.eps) == 0)
696 (void)snprintf(scr, sizeof(scr), "+/- %d %s",
697 (int)(gpsdata->fix.eps * speedfactor), speedunits);
699 (void)snprintf(scr, sizeof(scr), "n/a");
700 (void)mvwprintw(datawin, 14, DATAWIN_VALUE_OFFSET + 5, "%-*s", 22,
704 /* Be quiet if the user requests silence. */
705 if (!silent_flag && raw_flag) {
706 (void)waddstr(messages, message);
709 /* Reset the status_timer if the state has changed. */
710 if (newstate != state) {
711 status_timer = time(NULL);
715 (void)wrefresh(datawin);
716 (void)wrefresh(satellites);
718 (void)wrefresh(messages);
722 static void usage(char *prog)
724 (void)fprintf(stderr,
725 "Usage: %s [-h] [-V] [-l {d|m|s}] [server[:port:[device]]]\n\n"
726 " -h Show this help, then exit\n"
727 " -V Show version, then exit\n"
728 " -s Be silent (don't print raw gpsd data)\n"
729 " -l {d|m|s} Select lat/lon format\n"
732 " s = DD MM' SS.sss\"\n"
733 " -m Display heading as the estimated magnetic heading\n"
734 " Valid only for USA (Lower 48 + AK) and Western Europe.\n",
741 * No protocol dependencies above this line
744 int main(int argc, char *argv[])
749 struct timeval timeout;
753 /*@ -observertrans @*/
754 switch (gpsd_units()) {
756 altfactor = METERS_TO_FEET;
758 speedfactor = MPS_TO_MPH;
762 altfactor = METERS_TO_FEET;
764 speedfactor = MPS_TO_KNOTS;
765 speedunits = "knots";
770 speedfactor = MPS_TO_KPH;
774 /* leave the default alone */
777 /*@ +observertrans @*/
779 /* Process the options. Print help if requested. */
780 while ((option = getopt(argc, argv, "hVl:smuD:")) != -1) {
782 #ifdef CLIENTDEBUG_ENABLE
784 debug = atoi(optarg);
785 gps_enable_debug(debug, stderr);
787 #endif /* CLIENTDEBUG_ENABLE */
789 magnetic_flag = true;
795 /*@ -observertrans @*/
798 altfactor = METERS_TO_FEET;
800 speedfactor = MPS_TO_MPH;
804 altfactor = METERS_TO_FEET;
806 speedfactor = MPS_TO_KNOTS;
807 speedunits = "knots";
812 speedfactor = MPS_TO_KPH;
816 (void)fprintf(stderr, "Unknown -u argument: %s\n", optarg);
819 /*@ +observertrans @*/
821 (void)fprintf(stderr, "cgps: %s (revision %s)\n",
833 deg_type = deg_ddmmss;
836 (void)fprintf(stderr, "Unknown -l argument: %s\n", optarg);
847 /* Grok the server, port, and device. */
849 gpsd_source_spec(argv[optind], &source);
851 gpsd_source_spec(NULL, &source);
853 /* Open the stream to gpsd. */
854 if (gps_open_r(source.server, source.port, &gpsdata) != 0) {
855 (void)fprintf(stderr,
856 "cgps: no gpsd running or network error: %d, %s\n",
857 errno, gps_errstr(errno));
861 /* Fire up curses. */
864 (void)signal(SIGINT, die);
865 (void)signal(SIGHUP, die);
869 /* Here's where updates go now that things are established. */
872 gps_set_raw_hook(&gpsdata, update_compass_panel);
874 #endif /* TRUENORTH */
876 gps_set_raw_hook(&gpsdata, update_gps_panel);
879 status_timer = time(NULL);
881 (void)gps_stream(&gpsdata, WATCH_ENABLE, NULL);
883 /* heart of the client */
886 /* watch to see when it has input */
888 FD_SET(gpsdata.gps_fd, &rfds);
890 /* wait up to five seconds. */
894 /* check if we have new information */
895 data = select(gpsdata.gps_fd + 1, &rfds, NULL, NULL, &timeout);
898 fprintf(stderr, "cgps: socket error 3\n");
902 if (gps_read(&gpsdata) == -1) {
903 fprintf(stderr, "cgps: socket error 4\n");
904 die(errno == 0 ? GPS_GONE : GPS_ERROR);
908 /* Check for user input. */
917 /* Toggle spewage of raw gpsd data. */
919 silent_flag = !silent_flag;
922 /* Clear the spewage area. */
924 (void)werase(messages);