1 /*
2  * SiRF object for the GPS packet monitor.
3  *
4  * This file is Copyright (c) 2010-2018 by the GPSD project
5  * SPDX-License-Identifier: BSD-2-clause
6  *
7  */
8 
9 #include "gpsd_config.h"  /* must be before all includes */
10 
11 #include <stdlib.h>
12 #include <stdbool.h>
13 #include <string.h>
14 #include <math.h>
15 #include <stdlib.h> /* for labs() */
16 #include <assert.h>
17 #include <time.h>
18 #include <sys/time.h>
19 
20 #include "gpsd.h"
21 #include "bits.h"
22 #include "gpsmon.h"
23 #include "strfuncs.h"
24 
25 #if defined(SIRF_ENABLE) && defined(BINARY_ENABLE)
26 extern const struct gps_type_t driver_sirf;
27 
28 static WINDOW *mid2win, *mid4win, *mid6win, *mid7win, *mid9win, *mid13win;
29 static WINDOW *mid19win, *mid27win;
30 static bool dispmode = false, subframe_enabled = false, ppstime_enabled = false;
31 static int leapseconds;
32 
33 static char *verbpat[] = {
34     "#Time:",
35     "@R Time:",
36     "CSTD: New almanac for",
37     "NOTICE: DOP Q Boost",
38     "RTC not set",
39     "numOfSVs = 0",
40     "rtcaj tow ",
41     NULL
42 };
43 
44 
45 static char *dgpsvec[] = {
46     "None",
47     "SBAS",
48     "Serial",
49     "Beacon",
50     "Software",
51 };
52 
53 /* check range of an unsigned quantity */
54 #define CHECK_RANGE(vec, i) ((i) < sizeof(vec)/sizeof(vec[0]))
55 
56 /*****************************************************************************
57  *
58  * SiRF packet-decoding routines
59  *
60  *****************************************************************************/
61 
62 #define display	(void)mvwprintw
63 
64 #define SIRF_CHANNELS 	12	/* max channels allowed in SiRF format */
65 
sirf_initialize(void)66 static bool sirf_initialize(void)
67 {
68     unsigned int i;
69 
70     mid2win = subwin(devicewin, 6, 80, 1, 0);
71     mid4win = subwin(devicewin, SIRF_CHANNELS + 3, 30, 7, 0);
72     mid6win = subwin(devicewin, 3, 50, 7, 30);
73     mid7win = subwin(devicewin, 4, 50, 13, 30);
74     mid9win = subwin(devicewin, 3, 50, 10, 30);
75     mid13win = subwin(devicewin, 3, 50, 17, 30);
76     mid19win = newwin(16, 50, 7, 30);
77     mid27win = subwin(devicewin, 3, 50, 20, 30);
78     if (mid2win == NULL || mid4win == NULL || mid6win == NULL
79 	|| mid9win == NULL || mid13win == NULL || mid19win == NULL
80 	|| mid27win == NULL)
81 	return false;
82 
83     (void)syncok(mid2win, true);
84     (void)syncok(mid4win, true);
85     (void)syncok(mid6win, true);
86     (void)syncok(mid7win, true);
87     (void)syncok(mid9win, true);
88     (void)syncok(mid13win, true);
89     (void)syncok(mid27win, true);
90 
91     (void)wborder(mid2win, 0, 0, 0, 0, 0, 0, 0, 0),
92 	(void)wattrset(mid2win, A_BOLD);
93     (void)wmove(mid2win, 0, 1);
94     display(mid2win, 0, 12, " X ");
95     display(mid2win, 0, 21, " Y ");
96     display(mid2win, 0, 30, " Z ");
97     display(mid2win, 0, 43, " North ");
98     display(mid2win, 0, 54, " East ");
99     display(mid2win, 0, 65, " Alt ");
100 
101     (void)wmove(mid2win, 1, 1);
102     (void)wprintw(mid2win,
103 		  "Pos:                            m                                    m");
104     (void)wmove(mid2win, 2, 1);
105     (void)wprintw(mid2win,
106 		  "Vel:                            m/s                                  climb m/s");
107     (void)wmove(mid2win, 3, 1);
108     (void)wprintw(mid2win,
109 		  "Time:                          Leap:        Heading:                 speed m/s");
110     (void)wmove(mid2win, 4, 1);
111     (void)wprintw(mid2win,
112 		  "Fix:                                                 HDOP:      M1:     M2:   ");
113     display(mid2win, 5, 24, " Packet type 2 (0x02) ");
114     (void)wattrset(mid2win, A_NORMAL);
115 
116     (void)wborder(mid4win, 0, 0, 0, 0, 0, 0, 0, 0),
117 	(void)wattrset(mid4win, A_BOLD);
118     display(mid4win, 1, 1, "Ch PRN  Az El Stat  C/N ? SF");
119     for (i = 0; i < SIRF_CHANNELS; i++) {
120 	display(mid4win, (int)(i + 2), 1, "%2d", i);
121     }
122     display(mid4win, 0, 1, " Measured Tracker ");
123     display(mid4win, 14, 4, " Packet Type 4 (0x04) ");
124     (void)wattrset(mid4win, A_NORMAL);
125 
126     (void)wborder(mid19win, 0, 0, 0, 0, 0, 0, 0, 0),
127 	(void)wattrset(mid19win, A_BOLD);
128     display(mid19win, 1, 1, "Alt. hold mode:");
129     display(mid19win, 2, 1, "Alt. hold source:");
130     display(mid19win, 3, 1, "Alt. source input:");
131     display(mid19win, 4, 1, "Degraded timeout:");
132     display(mid19win, 5, 1, "DR timeout:");
133     display(mid19win, 6, 1, "Track smooth mode:");
134     display(mid19win, 7, 1, "Static Navigation:");
135     display(mid19win, 8, 1, "3SV Least Squares:");
136     display(mid19win, 9, 1, "DOP Mask mode:");
137     display(mid19win, 10, 1, "Nav. Elev. mask:");
138     display(mid19win, 11, 1, "Nav. Power mask:");
139     display(mid19win, 12, 1, "DGPS Source:");
140     display(mid19win, 13, 1, "DGPS Mode:");
141     display(mid19win, 14, 1, "DGPS Timeout:");
142     display(mid19win, 1, 26, "LP Push-to-Fix:");
143     display(mid19win, 2, 26, "LP On Time:");
144     display(mid19win, 3, 26, "LP Interval:");
145     display(mid19win, 4, 26, "U. Tasks Enab.:");
146     display(mid19win, 5, 26, "U. Task Inter.:");
147     display(mid19win, 6, 26, "LP Pwr Cyc En:");
148     display(mid19win, 7, 26, "LP Max Acq Srch:");
149     display(mid19win, 8, 26, "LP Max Off Time:");
150     display(mid19win, 9, 26, "APM enabled:");
151     display(mid19win, 10, 26, "# of Fixes:");
152     display(mid19win, 11, 26, "Time btw Fixes:");
153     display(mid19win, 12, 26, "H/V Error Max:");
154     display(mid19win, 13, 26, "Rsp Time Max:");
155     display(mid19win, 14, 26, "Time/Accu:");
156 
157     display(mid19win, 15, 8, " Packet type 19 (0x13) ");
158     (void)wattrset(mid19win, A_NORMAL);
159 
160     (void)wborder(mid6win, 0, 0, 0, 0, 0, 0, 0, 0),
161 	(void)wattrset(mid6win, A_BOLD);
162     display(mid6win, 0, 1, " Firmware Version ");
163     display(mid6win, 2, 8, " Packet Type 6 (0x06) ");
164     (void)wattrset(mid6win, A_NORMAL);
165 
166     (void)wborder(mid7win, 0, 0, 0, 0, 0, 0, 0, 0),
167 	(void)wattrset(mid7win, A_BOLD);
168     display(mid7win, 0, 1, " Clock Status ");
169     display(mid7win, 1, 1, "SVs: ");
170     display(mid7win, 1, 9, "Drift: ");
171     display(mid7win, 1, 23, "Bias: ");
172     display(mid7win, 2, 1, "GPS Time: ");
173     display(mid7win, 2, 23, "PPS: ");
174     (void)syncok(mid7win, true);
175     (void)mvwaddstr(mid7win, 2, 40, "N/A");
176     display(mid7win, 3, 8, " Packet type 7 (0x07) ");
177     (void)wattrset(mid7win, A_NORMAL);
178 
179     (void)wborder(mid9win, 0, 0, 0, 0, 0, 0, 0, 0),
180 	(void)wattrset(mid9win, A_BOLD);
181     display(mid9win, 0, 1, " CPU Throughput ");
182     display(mid9win, 1, 1, "Max: ");
183     display(mid9win, 1, 13, "Lat: ");
184     display(mid9win, 1, 25, "Time: ");
185     display(mid9win, 1, 39, "MS: ");
186     display(mid9win, 2, 8, " Packet type 9 (0x09) ");
187     (void)wattrset(mid9win, A_NORMAL);
188 
189     (void)wborder(mid13win, 0, 0, 0, 0, 0, 0, 0, 0),
190 	(void)wattrset(mid13win, A_BOLD);
191     display(mid13win, 0, 1, " Visible List ");
192     display(mid13win, 2, 8, " Packet type 13 (0x0D) ");
193     (void)wattrset(mid13win, A_NORMAL);
194 
195     (void)wborder(mid27win, 0, 0, 0, 0, 0, 0, 0, 0),
196 	(void)wattrset(mid27win, A_BOLD);
197     display(mid27win, 0, 1, " DGPS Status ");
198     display(mid27win, 2, 8, " Packet type 27 (0x1B) ");
199     (void)wattrset(mid27win, A_NORMAL);
200 
201 #ifdef CONTROLSEND_ENABLE
202     /* probe for version */
203     (void)monitor_control_send((unsigned char *)"\x84\x00", 2);
204 #endif /* CONTROLSEND_ENABLE */
205 
206     /* initialize the GPS context's time fields */
207     gpsd_time_init(session.context, time(NULL));
208 
209     return true;
210 }
211 
decode_ecef(double x,double y,double z,double vx,double vy,double vz)212 static void decode_ecef(double x, double y, double z,
213 			double vx, double vy, double vz)
214 {
215     const double a = WGS84A;
216     const double b = WGS84B;
217     const double e2 = (a * a - b * b) / (a * a);
218     const double e_2 = (a * a - b * b) / (b * b);
219     double lambda, p, theta, phi, n, h, vnorth, veast, vup, speed, heading;
220 
221     lambda = atan2(y, x);
222     p = sqrt(pow(x, 2) + pow(y, 2));
223     theta = atan2(z * a, p * b);
224     phi =
225 	atan2(z + e_2 * b * pow(sin(theta), 3),
226 	      p - e2 * a * pow(cos(theta), 3));
227     n = a / sqrt(1.0 - e2 * pow(sin(phi), 2));
228     h = p / cos(phi) - n;
229     h -= wgs84_separation((double)(RAD_2_DEG * phi),
230 			  (double)(RAD_2_DEG * lambda));
231     vnorth =
232 	-vx * sin(phi) * cos(lambda) - vy * sin(phi) * sin(lambda) +
233 	vz * cos(phi);
234     veast = -vx * sin(lambda) + vy * cos(lambda);
235     vup =
236 	vx * cos(phi) * cos(lambda) + vy * cos(phi) * sin(lambda) +
237 	vz * sin(phi);
238     speed = sqrt(pow(vnorth, 2) + pow(veast, 2));
239     heading = atan2(veast, vnorth);
240     if (heading < 0)
241 	heading += 2 * GPS_PI;
242 
243     /* North and East position fields */
244     (void)wattrset(mid2win, A_UNDERLINE);
245     (void)wmove(mid2win, 1, 40);
246     (void)wprintw(mid2win, "%9.5f %9.5f %9d",
247 			(double)(RAD_2_DEG * phi),
248 			(double)(RAD_2_DEG * lambda),
249 			(int)h);
250     (void)mvwaddch(mid2win, 1, 49, ACS_DEGREE);
251     (void)mvwaddch(mid2win, 1, 59, ACS_DEGREE);
252 
253     /* North and East velocity fields */
254     (void)wmove(mid2win, 2, 40);
255     (void)wprintw(mid2win, "%9.1f %9.1f %9.1f", vnorth, veast, vup);
256 
257     /* heading and speed fields */
258     (void)wmove(mid2win, 3, 54);
259     (void)wprintw(mid2win, "%5.1f %9.1f", (double)(RAD_2_DEG * heading), speed);
260     (void)mvwaddch(mid2win, 3, 59, ACS_DEGREE);
261     (void)wattrset(mid2win, A_NORMAL);
262 }
263 
sirf_update(void)264 static void sirf_update(void)
265 {
266     int i, j, ch, sv;
267     unsigned char *buf;
268     size_t len;
269     uint8_t dgps;
270     char tbuf[JSON_DATE_MAX+1];
271 
272     buf = session.lexer.outbuffer + 4;
273     len = session.lexer.outbuflen - 8;
274     switch (buf[0]) {
275     case 0x02:			/* Measured Navigation Data */
276 	(void)wmove(mid2win, 1, 6);	/* ECEF position */
277 	(void)wprintw(mid2win, "%8d %8d %8d", getbes32(buf, 1),
278 		      getbes32(buf, 5), getbes32(buf, 9));
279 	(void)wmove(mid2win, 2, 6);	/* ECEF velocity */
280 	(void)wprintw(mid2win, "%8.1f %8.1f %8.1f",
281 		      (double)getbes16(buf, 13) / 8, (double)getbes16(buf,
282 								    15) / 8,
283 		      (double)getbes16(buf, 17) / 8);
284 	decode_ecef((double)getbes32(buf, 1), (double)getbes32(buf, 5),
285 		    (double)getbes32(buf, 9), (double)getbes16(buf, 13) / 8,
286 		    (double)getbes16(buf, 15) / 8, (double)getbes16(buf,
287 								  17) / 8);
288 	/* line 3 */
289 	(void)wmove(mid2win, 3, 7);
290         if (0 < session.gpsdata.fix.time.tv_sec) {
291             (void)wprintw(mid2win, "%-24s",
292 			  timespec_to_iso8601(session.gpsdata.fix.time, tbuf,
293                           sizeof(tbuf)));
294         }
295 	(void)wmove(mid2win, 3, 38);
296 	(void)wattrset(mid2win, A_UNDERLINE);
297 	if (ppstime_enabled)
298 	   (void)wprintw(mid2win, "%02d", leapseconds);
299 	else
300 	   (void)wprintw(mid2win, "??");
301 	(void)wattrset(mid2win, A_NORMAL);
302 	/* line 4 */
303 	/* HDOP */
304 	(void)wmove(mid2win, 4, 59);
305 	(void)wprintw(mid2win, "%4.1f", (double)getub(buf, 20) / 5);
306 	/* Mode 1 */
307 	(void)wmove(mid2win, 4, 69);
308 	(void)wprintw(mid2win, "%02x", getub(buf, 19));
309 	/* Mode 2 */
310 	(void)wmove(mid2win, 4, 77);
311 	(void)wprintw(mid2win, "%02x", getub(buf, 21));
312 	/* SVs in fix */
313 	(void)wmove(mid2win, 4, 6);
314 	(void)wprintw(mid2win, "%2d =                                     ",
315 		      (int)getub(buf, 28));
316 	/* SV list */
317 	(void)wmove(mid2win, 4, 10);
318 	/* coverity_submit[tainted_data] */
319 	for (i = 0; i < (int)getub(buf, 28); i++)
320 	   (void)wprintw(mid2win, " %2d", (int)getub(buf, 29 + i));
321 	monitor_log("MND 0x02=");
322 	break;
323 
324     case 0x04:			/* Measured Tracking Data */
325 	ch = (int)getub(buf, 7);
326 	for (i = 0; i < ch; i++) {
327 	    int az, el, state, off;
328 	    double cn;
329 
330 	    off = 8 + 15 * i;
331 	    (void)wmove(mid4win, i + 2, 3);
332 
333 	    sv = (int)getub(buf, off);
334 	    az = (int)getub(buf, off + 1) * 3 / 2;
335 	    el = (int)getub(buf, off + 2) / 2;
336 	    state = (int)getbeu16(buf, off + 3);
337 	    cn = 0;
338 	    for (j = 0; j < 10; j++)
339 		cn += (int)getub(buf, off + 5 + j);
340 	    cn /= 10;
341 
342 	    (void)wprintw(mid4win, " %3d %3d %2d %04x %4.1f %c",
343 			   sv, az, el, state, cn, state == 0xbf ? 'T' : ' ');
344 	}
345 	monitor_log("MTD 0x04=");
346 	break;
347 
348 #ifdef __UNUSED__
349     case 0x05:			/* raw track data */
350 	for (off = 1; off < len; off += 51) {
351 	    ch = getbeu32(buf, off);
352 	    (void)wmove(mid4win, ch + 2, 19);
353 	    cn = 0;
354 
355 	    for (j = 0; j < 10; j++)
356 		cn += getub(buf, off + 34 + j);
357 
358 	    printw("%5.1f", (double)cn / 10);
359 
360 	    printw("%9d%3d%5d", getbeu32(buf, off + 8),
361 		   (int)getbeu16(buf, off + 12), (int)getbeu16(buf, off + 14));
362 	    printw("%8.5f %10.5f", (double)getbeu32(buf, off + 16) / 65536,
363 		   (double)getbeu32(buf, off + 20) / 1024);
364 	}
365 	monitor_log("RTD 0x05=");
366 	break;
367 #endif /* __UNUSED */
368 
369     case 0x06:			/* firmware version */
370 	display(mid6win, 1, 1, "%s", buf + 1);
371 	monitor_log("FV  0x06=");
372 	break;
373 
374     case 0x07:			/* Response - Clock Status Data */
375         display(mid7win, 1, 5, "%2d", getub(buf, 7));	/* SVs */
376         /* Clock ppstimes */
377         display(mid7win, 1, 16, "%lu", (unsigned long)getbeu32(buf, 8));
378         display(mid7win, 1, 29, "%lu", (unsigned long)getbeu32(buf, 12));
379         /* Clock Bias */
380         display(mid7win, 2, 11, "%lu", (unsigned long)getbeu32(buf, 16));
381         /* Estimated Time */
382         monitor_log("CSD 0x07=");
383         break;
384 
385     case 0x08:			/* 50 BPS data */
386 	ch = (int)getub(buf, 1);
387 	sv = (int)getub(buf, 2);
388 	display(mid4win, ch + 2, 27, "%2d", sv);
389 	subframe_enabled = true;
390 	monitor_log("50B 0x08=");
391 	break;
392 
393     case 0x09:			/* Throughput */
394 	display(mid9win, 1, 6, "%.3f", (double)getbeu16(buf, 1) / 186);	/*SegStatMax */
395 	display(mid9win, 1, 18, "%.3f", (double)getbeu16(buf, 3) / 186);	/*SegStatLat */
396 	display(mid9win, 1, 31, "%.3f", (double)getbeu16(buf, 5) / 186);	/*SegStatTime */
397 	display(mid9win, 1, 42, "%3d", (int)getbeu16(buf, 7));	/* Last Millisecond */
398 	monitor_log("THR 0x09=");
399 	break;
400 
401     case 0x0b:			/* Command Acknowledgement */
402 	monitor_log("ACK 0x0b=");
403 	break;
404 
405     case 0x0c:			/* Command NAcknowledgement */
406 	monitor_log("NAK 0x0c=");
407 	break;
408 
409     case 0x0d:			/* Visible List */
410 	display(mid13win, 1, 1, "%02d =                                            ",
411 				 getub(buf, 1));
412 	(void)wmove(mid13win, 1, 5);
413 	for (i = 0; i < (int)getub(buf, 1); i++)
414 	    (void)wprintw(mid13win, " %d", getub(buf, 2 + 5 * i));
415 	monitor_log("VL  0x0d=");
416 	break;
417 
418     case 0x13:
419 #define YESNO(n)	(((int)getub(buf, n) != 0)?'Y':'N')
420 	display(mid19win, 1, 20, "%d", getub(buf, 5));	/* Alt. hold mode */
421 	display(mid19win, 2, 20, "%d", getub(buf, 6));	/* Alt. hold source */
422 	display(mid19win, 3, 20, "%dm", (int)getbeu16(buf, 7));	/* Alt. source input */
423 	if (getub(buf, 9) != (uint8_t) '\0')
424 	    display(mid19win, 4, 20, "%dsec", getub(buf, 10));	/* Degraded timeout */
425 	else
426 	    display(mid19win, 4, 20, "N/A   ");
427 	display(mid19win, 5, 20, "%dsec", getub(buf, 11));	/* DR timeout */
428 	display(mid19win, 6, 20, "%c", YESNO(12));	/* Track smooth mode */
429 	display(mid19win, 7, 20, "%c", YESNO(13));	/* Static Nav. */
430 	display(mid19win, 8, 20, "0x%x", getub(buf, 14));	/* 3SV Least Squares */
431 	display(mid19win, 9, 20, "0x%x", getub(buf, 19));	/* DOP Mask mode */
432 	display(mid19win, 10, 20, "0x%x", (int)getbeu16(buf, 20));	/* Nav. Elev. mask */
433 	display(mid19win, 11, 20, "0x%x", getub(buf, 22));	/* Nav. Power mask */
434 	display(mid19win, 12, 20, "0x%x", getub(buf, 27));	/* DGPS Source */
435 	display(mid19win, 13, 20, "0x%x", getub(buf, 28));	/* DGPS Mode */
436 	display(mid19win, 14, 20, "%dsec", getub(buf, 29));	/* DGPS Timeout */
437 	display(mid19win, 1, 42, "%c", YESNO(34));	/* LP Push-to-Fix */
438 	display(mid19win, 2, 42, "%dms", getbeu32(buf, 35));	/* LP On Time */
439 	display(mid19win, 3, 42, "%d", getbeu32(buf, 39));	/* LP Interval */
440 	display(mid19win, 4, 42, "%c", YESNO(43));	/* User Tasks enabled */
441 	display(mid19win, 5, 42, "%d", getbeu32(buf, 44));	/* User Task Interval */
442 	display(mid19win, 6, 42, "%c", YESNO(48));	/* LP Power Cycling Enabled */
443 	display(mid19win, 7, 42, "%d", getbeu32(buf, 49));	/* LP Max Acq Search Time */
444 	display(mid19win, 8, 42, "%d", getbeu32(buf, 53));	/* LP Max Off Time */
445 	display(mid19win, 9, 42, "%c", YESNO(57));	/* APM Enabled */
446 	display(mid19win, 10, 42, "%d", (int)getbeu16(buf, 58));	/* # of fixes */
447 	display(mid19win, 11, 42, "%d", (int)getbeu16(buf, 60));	/* Time Between fixes */
448 	display(mid19win, 12, 42, "%d", getub(buf, 62));	/* H/V Error Max */
449 	display(mid19win, 13, 42, "%d", getub(buf, 63));	/* Response Time Max */
450 	display(mid19win, 14, 42, "%d", getub(buf, 64));	/* Time/Accu & Duty Cycle Priority */
451 #undef YESNO
452 	monitor_log("NP  0x13=");
453 	break;
454 
455     case 0x1b:
456 	/******************************************************************
457 	 Not actually documented in any published materials before the
458 	 1.6 version of the SiRF binary protocol manual.
459 	 Here is what Chris Kuethe got from the SiRF folks,
460 	 (plus some corrections from the GpsPaSsion forums):
461 
462 	Start of message
463 	----------------
464 	Message ID          1 byte    27
465 	Correction Source   1 byte    0=None, 1=SBAS, 2=Serial, 3=Beacon,
466 	4=Software
467 
468 	total:              2 bytes
469 
470 	Middle part of message varies if using beacon or other:
471 	-------------------------------------------------------
472 	If Beacon:
473 	Receiver Freq Hz    4 bytes
474 	Bit rate BPS        1 byte
475 	Status bit map      1 byte    01=Signal Valid,
476 				      02=Auto frequency detect
477 				      04=Auto bit rate detect
478 	Signal Magnitude    4 bytes   Note: in internal units
479 	Signal Strength dB  2 bytes   derived from Signal Magnitude
480 	SNR  dB             2 bytes
481 
482 	total:             14 bytes
483 
484 	If Not Beacon:
485 	Correction Age[12]  1 byte x 12  Age in seconds in same order as follows
486 	Reserved            2 bytes
487 
488 	total:             14 bytes
489 
490 	End of Message
491 	--------------
492 	Repeated 12 times (pad with 0 if less than 12 SV corrections):
493 	SVID                1 byte
494 	Correction (cm)     2 bytes (signed short)
495 
496 	total               3 x 12 = 36 bytes
497 	******************************************************************/
498 	dgps = getub(buf, 1);
499 	display(mid27win, 1, 1, "%8s =                                      ",
500 		(CHECK_RANGE(dgpsvec, dgps) ? dgpsvec[dgps] : "???"));
501 	(void)wmove(mid27win, 1, 11);
502 	for (ch = 0; ch < SIRF_CHANNELS; ch++)
503 	    if (getub(buf, 16 + 3 * ch) != '\0')
504 		(void)wprintw(mid27win, " %d", getub(buf, 16 + 3 * ch));
505 	monitor_log("DST 0x1b=");
506 	break;
507 
508     case 0x1c:			/* NL Measurement Data */
509     case 0x1d:			/* NL DGPS Data */
510     case 0x1e:			/* NL SV State Data */
511     case 0x1f:			/* NL Initialized Data */
512 	subframe_enabled = true;
513 	monitor_log("NL  0x%02x=", buf[0]);
514 	break;
515 
516     case 0x29:			/* Geodetic Navigation Data */
517 	monitor_log("GND 0x29=");
518 	break;
519 
520     case 0x32:			/* SBAS Parameters */
521 	monitor_log("SBP 0x32=");
522 	break;
523 
524     case 0x34:			/* PPS Time */
525 	ppstime_enabled = true;
526 	leapseconds = (int)getbeu16(buf, 8);
527 	monitor_log("PPS 0x34=");
528 	break;
529 
530     case 0xff:			/* Development Data */
531 	while (len > 0 && buf[len - 1] == '\n')
532 	    len--;
533 	while (len > 0 && buf[len - 1] == ' ')
534 	    len--;
535 	buf[len] = '\0';
536 	j = 1;
537 	for (i = 0; verbpat[i] != NULL; i++)
538 	    if (str_starts_with((char *)(buf + 1), verbpat[i])) {
539 		j = 0;
540 		break;
541 	    }
542 	if (j != 0)
543 	    monitor_log("%s\n", buf + 1);
544 	monitor_log("DD  0xff=");
545 	break;
546 
547     default:
548 	monitor_log("UNK 0x%02x=", buf[0]);
549 	break;
550     }
551 
552 #ifdef CONTROLSEND_ENABLE
553     /* elicit navigation parameters */
554     if (dispmode && (time(NULL) % 10 == 0)) {
555 	(void)monitor_control_send((unsigned char *)"\x98\x00", 2);
556     }
557 #endif /* CONTROLSEND_ENABLE */
558 
559     /* clear the 50bps data field every 6 seconds */
560     if (subframe_enabled && (time(NULL) % 6 == 0)) {
561 	for (ch = 0; ch < SIRF_CHANNELS; ch++)
562 	   display(mid4win, ch + 2, 27, "  ");
563     }
564 
565     if (dispmode) {
566 	(void)touchwin(mid19win);
567 	(void)wnoutrefresh(mid19win);
568     }
569 
570     pps_update(mid7win, 2, 32);
571 }
572 
573 #ifdef CONTROLSEND_ENABLE
sirf_command(char line[])574 static int sirf_command(char line[])
575 {
576     unsigned char buf[BUFSIZ];
577     int v;
578 
579     switch (line[0]) {
580     case 'A':			/* toggle 50bps subframe data */
581 	(void)memset(buf, '\0', sizeof(buf));
582 	putbyte(buf, 0, 0x80);
583 	putbyte(buf, 23, 0x0c);
584 	putbyte(buf, 24, subframe_enabled ? 0x00 : 0x10);
585 	(void)monitor_control_send(buf, 25);
586 	/*
587 	 * The subframe_enabled off because we're counting on receipt of
588 	 * a subframe packet to turn it on if the command succeeds.
589 	 */
590 	subframe_enabled = false;
591 	return COMMAND_MATCH;
592 
593     case 'M':			/* static navigation */
594 	putbyte(buf, 0, 0x8f);	/* id */
595 	putbyte(buf, 1, atoi(line + 1));
596 	(void)monitor_control_send(buf, 2);
597 	return COMMAND_MATCH;
598 
599     case 'D':			/* MID 4 rate change (undocumented) */
600 	v = atoi(line + 1);
601 	if (v > 30)
602 	    return COMMAND_MATCH;
603 	putbyte(buf, 0, 0xa6);
604 	putbyte(buf, 1, 0);
605 	putbyte(buf, 2, 4);	/* satellite picture */
606 	putbyte(buf, 3, v);
607 	putbyte(buf, 4, 0);
608 	putbyte(buf, 5, 0);
609 	putbyte(buf, 6, 0);
610 	putbyte(buf, 7, 0);
611 	(void)monitor_control_send(buf, 8);
612 	return COMMAND_MATCH;
613 
614     case 'P':			/* poll navigation params */
615 	dispmode = !dispmode;
616 	if (dispmode) {
617 	    (void)syncok(mid6win, false);
618 	    (void)syncok(mid7win, false);
619 	    (void)syncok(mid9win, false);
620 	    (void)syncok(mid13win, false);
621 	    (void)syncok(mid27win, false);
622 	} else {
623 	    (void)syncok(mid6win, true);
624 	    (void)wsyncup(mid6win);
625 	    (void)syncok(mid7win, true);
626 	    (void)wsyncup(mid7win);
627 	    (void)syncok(mid9win, true);
628 	    (void)wsyncup(mid9win);
629 	    (void)syncok(mid13win, true);
630 	    (void)wsyncup(mid13win);
631 	    (void)syncok(mid27win, true);
632 	    (void)wsyncup(mid27win);
633 	}
634 	return COMMAND_MATCH;
635     }
636 
637     return COMMAND_UNKNOWN;	/* no match */
638 }
639 #endif /* CONTROLSEND_ENABLE */
640 
sirf_wrap(void)641 static void sirf_wrap(void)
642 {
643     (void)delwin(mid2win);
644     (void)delwin(mid4win);
645     (void)delwin(mid6win);
646     (void)delwin(mid7win);
647     (void)delwin(mid9win);
648     (void)delwin(mid13win);
649     (void)delwin(mid19win);
650     (void)delwin(mid27win);
651 }
652 
653 const struct monitor_object_t sirf_mmt = {
654     .initialize = sirf_initialize,
655     .update = sirf_update,
656 #ifdef CONTROLSEND_ENABLE
657     .command = sirf_command,
658 #else
659     .command = NULL,
660 #endif /* CONTROLSEND_ENABLE */
661     .wrap = sirf_wrap,
662     .min_y = 22,.min_x = 80,
663     .driver = &driver_sirf,
664 };
665 #endif /* defined(SIRF_ENABLE) && defined(BINARY_ENABLE) */
666 
667 /* sirfmon.c ends here */
668