/src/gpsd/gpsd-3.27.6~dev/drivers/driver_navcom.c
Line | Count | Source |
1 | | /* |
2 | | * Driver for Navcom receivers using proprietary NCT messages, |
3 | | * a binary protocol. |
4 | | * |
5 | | * Vendor website: http://www.navcomtech.com/ |
6 | | * Technical references: Technical Reference Manual P/N 96-3120001-3001 |
7 | | * |
8 | | * Tested with two SF-2040G models |
9 | | * |
10 | | * At this stage, this driver implements the following commands: |
11 | | * |
12 | | * 0x20: Data Request (tell the unit which responses you want) |
13 | | * 0x3f: LED Configuration (controls the front panel LEDs -- for testing) |
14 | | * 0x1c: Test Support Block (again, blinks the front panel lights) |
15 | | * |
16 | | * and it understands the following responses: |
17 | | * |
18 | | * 0x06: Acknowledgement (without error) |
19 | | * 0x15: Negative Acknowledge |
20 | | * 0x86: Channel Status |
21 | | * 0xae: Identification Block |
22 | | * 0xb0: Raw Meas. Data Block |
23 | | * 0xb1: PVT Block |
24 | | * 0xb5: Pseudorange Noise Statistics |
25 | | * 0xd3: LBM DSP Status Block |
26 | | * 0xef: Clock Drift and Offset |
27 | | * |
28 | | * By Diego Berge. Contact via web form at http://www.navlost.eu/contact |
29 | | * |
30 | | * Week counters are not limited to 10 bits. It's unknown what |
31 | | * the firmware is doing to disambiguate them, if anything; it might just |
32 | | * be adding a fixed offset based on a hidden epoch value, in which case |
33 | | * unhappy things will occur on the next rollover. |
34 | | * |
35 | | * This file is Copyright 2010 by the GPSD project |
36 | | * SPDX-License-Identifier: BSD-2-clause |
37 | | */ |
38 | | |
39 | | #include "../include/gpsd_config.h" // must be before all includes |
40 | | |
41 | | #include <math.h> |
42 | | #include <stdbool.h> |
43 | | #include <stdio.h> |
44 | | #include <string.h> |
45 | | |
46 | | #include "../include/gpsd.h" |
47 | | |
48 | | #if defined(NAVCOM_ENABLE) |
49 | | #include "../include/bits.h" |
50 | | #include "../include/timespec.h" |
51 | | |
52 | | // Have data which is 24 bits long |
53 | 8 | #define getles3224(buf,off) (int32_t)(((uint32_t)getub((buf), (off)+2)<<24 | (uint32_t)getub((buf), (off)+1)<<16 | (uint32_t)getub((buf), (off))<<8)>>8) |
54 | | #define getleu3224(buf,off) (uint32_t)(((uint32_t)getub((buf), (off)+2)<<24 | (uint32_t)getub((buf), (off)+1)<<16 | (uint32_t)getub((buf), (off))<<8)>>8) |
55 | | |
56 | | /* And just to be difficult, Navcom is little endian but the GPS data stream |
57 | | is big endian. Some messages contain raw GPS data */ |
58 | 36 | #define getles16_be(buf, off) (int16_t)((((uint16_t)getub(buf, (off)) << 8) \ |
59 | 36 | | (uint16_t)getub(buf, (off)+1))) |
60 | 60 | #define getleu16_be(buf, off) (uint16_t)((((uint16_t)getub(buf, (off)) << 8) \ |
61 | 60 | | (uint16_t)getub(buf, (off)+1))) |
62 | 16 | #define getles32_be(buf, off) (int32_t)((((uint16_t)getleu16_be(buf, (off)) << 16) \ |
63 | 16 | | getleu16_be(buf, (off)+2))) |
64 | 8 | #define getleu32_be(buf, off) (uint32_t)((((uint16_t)getleu16_be(buf, (off)) << 16) \ |
65 | 8 | | getleu16_be(buf, (off)+2))) |
66 | 8 | #define getles3224_be(buf,off) (int32_t)(((uint32_t)getub((buf), (off))<<24 \ |
67 | 8 | | (uint32_t)getub((buf), (off)+1)<<16 \ |
68 | 8 | | (uint32_t)getub((buf), (off)+2)<<8)>>8) |
69 | | |
70 | | #define NAVCOM_CHANNELS 12 |
71 | | |
72 | | static uint8_t checksum(unsigned char *buf, size_t len) |
73 | 1.53k | { |
74 | 1.53k | size_t n; |
75 | 1.53k | uint8_t csum = (uint8_t)0x00; |
76 | 20.0k | for (n = 0; n < len; n++) { |
77 | 18.5k | csum ^= buf[n]; |
78 | 18.5k | } |
79 | 1.53k | return csum; |
80 | 1.53k | } |
81 | | |
82 | | static bool navcom_send_cmd(struct gps_device_t *session, unsigned char *cmd, |
83 | | size_t len) |
84 | 1.53k | { |
85 | 1.53k | return (gpsd_write(session, (const char *)cmd, len) == (ssize_t) len); |
86 | 1.53k | } |
87 | | |
88 | | // Data Request |
89 | | static void navcom_cmd_0x20(struct gps_device_t *session, uint8_t block_id, |
90 | | uint16_t rate) |
91 | 1.29k | { |
92 | 1.29k | unsigned char msg[18]; |
93 | 1.29k | putbyte(msg, 0, 0x02); |
94 | 1.29k | putbyte(msg, 1, 0x99); |
95 | 1.29k | putbyte(msg, 2, 0x66); |
96 | 1.29k | putbyte(msg, 3, 0x20); // Cmd ID |
97 | 1.29k | putle16(msg, 4, 0x000e); // Length |
98 | 1.29k | putbyte(msg, 6, 0x00); // Action |
99 | 1.29k | putbyte(msg, 7, 0x01); // Count of blocks |
100 | 1.29k | putbyte(msg, 8, block_id); // Data Block ID |
101 | 1.29k | putbyte(msg, 9, 0x02); // Logical Ports |
102 | 1.29k | putle16(msg, 10, rate); // Data rate |
103 | 1.29k | putbyte(msg, 12, 0x71); |
104 | 1.29k | putbyte(msg, 13, 0x00); |
105 | 1.29k | putle16(msg, 14, 0x0000); |
106 | 1.29k | putbyte(msg, 16, checksum(msg + 3, 13)); |
107 | 1.29k | putbyte(msg, 17, 0x03); |
108 | 1.29k | (void)navcom_send_cmd(session, msg, 18); |
109 | 1.29k | GPSD_LOG(LOG_PROG, &session->context->errout, |
110 | 1.29k | "Navcom: sent command 0x20 (Data Request) " |
111 | 1.29k | "- data block id = %02x at rate %02x\n", block_id, rate); |
112 | 1.29k | } |
113 | | |
114 | | // cppcheck-suppress unusedFunction |
115 | | static void UNUSED navcom_cmd_0x3f(struct gps_device_t *session) |
116 | | // Changes the LED settings in the receiver |
117 | 0 | { |
118 | 0 | unsigned char msg[12]; |
119 | 0 | putbyte(msg, 0, 0x02); |
120 | 0 | putbyte(msg, 1, 0x99); |
121 | 0 | putbyte(msg, 2, 0x66); |
122 | 0 | putbyte(msg, 3, 0x3f); // Cmd ID |
123 | 0 | putle16(msg, 4, 0x0008); |
124 | 0 | putbyte(msg, 6, 0x01); // Action |
125 | 0 | putbyte(msg, 7, 0x00); // Reserved |
126 | 0 | putbyte(msg, 8, 0x02); // Link LED setting |
127 | 0 | putbyte(msg, 9, 0x0a); // Battery LED setting |
128 | 0 | putbyte(msg, 10, checksum(msg + 3, 7)); |
129 | 0 | putbyte(msg, 11, 0x03); |
130 | 0 | (void)navcom_send_cmd(session, msg, 12); |
131 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
132 | 0 | "Navcom: sent command 0x3f (LED Configuration Block)\n"); |
133 | 0 | } |
134 | | |
135 | | // Test Support Block - Blinks the LEDs |
136 | | static void navcom_cmd_0x1c(struct gps_device_t *session, uint8_t mode, |
137 | | uint8_t length) |
138 | 236 | { |
139 | 236 | unsigned char msg[12]; |
140 | 236 | putbyte(msg, 0, 0x02); |
141 | 236 | putbyte(msg, 1, 0x99); |
142 | 236 | putbyte(msg, 2, 0x66); |
143 | 236 | putbyte(msg, 3, 0x1c); // Cmd ID |
144 | 236 | putle16(msg, 4, 0x0008); |
145 | 236 | putbyte(msg, 6, 0x04); // Use ACK/NAK |
146 | 236 | putbyte(msg, 7, mode); // 0x01 or 0x02 |
147 | 236 | putbyte(msg, 8, length); // Only if mode == 0x01 |
148 | 236 | putbyte(msg, 9, 0x00); |
149 | 236 | putbyte(msg, 10, checksum(msg + 3, 7)); |
150 | 236 | putbyte(msg, 11, 0x03); |
151 | 236 | (void)navcom_send_cmd(session, msg, 12); |
152 | 236 | GPSD_LOG(LOG_PROG, &session->context->errout, |
153 | 236 | "Navcom: sent command 0x1c (Test Support Block)\n"); |
154 | 236 | GPSD_LOG(LOG_DATA, &session->context->errout, |
155 | 236 | "Navcom: command 0x1c mode = %02x, length = %u\n", |
156 | 236 | mode, length); |
157 | 236 | } |
158 | | |
159 | | // Serial Port Configuration |
160 | | static void navcom_cmd_0x11(struct gps_device_t *session, |
161 | | uint8_t port_selection) |
162 | 0 | { |
163 | | /* NOTE - We only allow changing one port at a time, |
164 | | * although the message supports doing both at once. */ |
165 | 0 | unsigned char msg[12]; |
166 | 0 | putbyte(msg, 0, 0x02); |
167 | 0 | putbyte(msg, 1, 0x99); |
168 | 0 | putbyte(msg, 2, 0x66); |
169 | 0 | putbyte(msg, 3, 0x11); // Cmd ID |
170 | 0 | putle16(msg, 4, 0x0008); // Length |
171 | 0 | putbyte(msg, 6, 0x04); // Action - Use ACK/NAK) |
172 | 0 | putbyte(msg, 7, port_selection); |
173 | 0 | putbyte(msg, 8, 0x00); // Reserved |
174 | 0 | putbyte(msg, 9, 0x00); // Reserved |
175 | 0 | putbyte(msg, 10, checksum(msg + 3, 7)); |
176 | 0 | putbyte(msg, 11, 0x03); |
177 | 0 | (void)navcom_send_cmd(session, msg, 12); |
178 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
179 | 0 | "Navcom: sent command 0x11 (Serial Port Configuration)\n"); |
180 | 0 | GPSD_LOG(LOG_DATA, &session->context->errout, |
181 | 0 | "Navcom: serial port selection: 0x%02x\n", port_selection); |
182 | 0 | } |
183 | | |
184 | | static void navcom_event_hook(struct gps_device_t *session, event_t event) |
185 | 6.74k | { |
186 | 6.74k | if (session->context->readonly) { |
187 | 2.52k | return; |
188 | 2.52k | } |
189 | | |
190 | | // Request the following messages: |
191 | 4.21k | if (event == EVENT_IDENTIFIED) { |
192 | | /* NOTE - Channel Status allows us to know into which of the |
193 | | * unit's various serial ports we are connected. |
194 | | * Its value gets updated every time we receive a 0x06 (Ack) |
195 | | * message. Note that if commands are being fed into the |
196 | | * unit from more than one port (which is entirely possible |
197 | | * although not necessarily a bright idea), there is a good |
198 | | * chance that we might misidentify our port */ |
199 | 118 | navcom_cmd_0x1c(session, 0x02, 0); // Test Support Block |
200 | 118 | navcom_cmd_0x20(session, 0xae, 0x0000); // Identification Block |
201 | 118 | navcom_cmd_0x20(session, 0x86, 0x000a); // Channel Status |
202 | 118 | navcom_cmd_0x1c(session, 0x01, 5); // Blink LEDs on receiver |
203 | | // Identification Block - send every 10 min |
204 | 118 | navcom_cmd_0x20(session, 0xae, 0x1770); |
205 | 118 | navcom_cmd_0x20(session, 0xb1, 0x4000); // PVT Block |
206 | | // Pseudorange Noise Statistics - send every 20s |
207 | 118 | navcom_cmd_0x20(session, 0xb5, 0x00c8); |
208 | 118 | navcom_cmd_0x20(session, 0xb0, 0x4000); // Raw Meas Data Block |
209 | | // Packed Ephemeris Data - send once |
210 | 118 | navcom_cmd_0x20(session, 0x81, 0x0000); |
211 | 118 | navcom_cmd_0x20(session, 0x81, 0x4000); // Packed Ephemeris Data |
212 | 118 | navcom_cmd_0x20(session, 0x86, 0x4000); // Channel Status |
213 | 118 | navcom_cmd_0x20(session, 0x83, 0x4000); // Ionosphere and UTC Data |
214 | | // Clock Drift - send every 5 min |
215 | 118 | navcom_cmd_0x20(session, 0xef, 0x0bb8); |
216 | 118 | } |
217 | 4.21k | } |
218 | | |
219 | | // Ionosphere and UTC Data |
220 | | static gps_mask_t handle_0x83(struct gps_device_t *session) |
221 | 0 | { |
222 | | /* NOTE - At the present moment this is only being used |
223 | | * for determining the GPS-UTC time difference, |
224 | | * for which the iono data is not needed as far |
225 | | * as we are concerned. However, I am still |
226 | | * reporting it (if debuglevel >= LOG_DATA) as a |
227 | | * matter of interest */ |
228 | | // 2^-30 |
229 | 0 | #define SF_A0 (0.000000000931322574615478515625) |
230 | | // 2^-50 |
231 | 0 | #define SF_A1 (0.000000000000000888178419700125) |
232 | | // 2^12 |
233 | 0 | #define SF_TOT (4096) |
234 | | // 2^-30 |
235 | 0 | #define SF_ALPHA0 (0.000000000931322574615478515625) |
236 | | // 2^-27 |
237 | 0 | #define SF_ALPHA1 (0.000000007450580596923828125) |
238 | | // 2^-24 |
239 | 0 | #define SF_ALPHA2 (0.000000059604644775390625) |
240 | | // 2^-24 |
241 | 0 | #define SF_ALPHA3 (0.000000059604644775390625) |
242 | | // 2^11 |
243 | 0 | #define SF_BETA0 (2048) |
244 | | // 2^14 |
245 | 0 | #define SF_BETA1 (16384) |
246 | | // 2^16 |
247 | 0 | #define SF_BETA2 (65536) |
248 | | // 2^16 |
249 | 0 | #define SF_BETA3 (65536) |
250 | 0 | unsigned char *buf = session->lexer.outbuffer + 3; |
251 | 0 | uint16_t week = getleu16(buf, 3); |
252 | 0 | uint32_t tow = getleu32(buf, 5); |
253 | 0 | int8_t alpha0 = getsb(buf, 9); |
254 | 0 | int8_t alpha1 = getsb(buf, 10); |
255 | 0 | int8_t alpha2 = getsb(buf, 11); |
256 | 0 | int8_t alpha3 = getsb(buf, 12); |
257 | 0 | int8_t beta0 = getsb(buf, 13); |
258 | 0 | int8_t beta1 = getsb(buf, 14); |
259 | 0 | int8_t beta2 = getsb(buf, 15); |
260 | 0 | int8_t beta3 = getsb(buf, 16); |
261 | 0 | int32_t a1 = getles32(buf, 17); |
262 | 0 | int32_t a0 = getles32(buf, 21); |
263 | 0 | uint8_t tot = getub(buf, 25); |
264 | 0 | uint8_t wnt = getub(buf, 26); |
265 | 0 | int8_t dtls = getsb(buf, 27); |
266 | 0 | uint8_t wnlsf = getub(buf, 28); |
267 | 0 | uint8_t dn = getub(buf, 29); |
268 | 0 | int8_t dtlsf = getsb(buf, 30); |
269 | | |
270 | | // Ref.: ICD-GPS-200C 20.3.3.5.2.4 |
271 | 0 | if ((week % 256) * 604800 + tow / 1000.0 < wnlsf * 604800 + dn * 86400) { |
272 | | // Effectivity time is in the future, use dtls |
273 | 0 | session->context->leap_seconds = (int)dtls; |
274 | 0 | } else { |
275 | | // Effectivity time is not in the future, use dtlsf |
276 | 0 | session->context->leap_seconds = (int)dtlsf; |
277 | 0 | } |
278 | |
|
279 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
280 | 0 | "Navcom: received packet type 0x83 (Ionosphere and UTC Data)\n"); |
281 | 0 | GPSD_LOG(LOG_DATA, &session->context->errout, |
282 | 0 | "Navcom: Scaled parameters follow:\n"); |
283 | 0 | GPSD_LOG(LOG_DATA, &session->context->errout, |
284 | 0 | "Navcom: GPS Week: %u, GPS Time of Week: %u (GPS Time: %f)\n", |
285 | 0 | week, tow, week * 604800 + tow / 1000.0); |
286 | 0 | GPSD_LOG(LOG_DATA, &session->context->errout, |
287 | 0 | "Navcom: a0: %12.4E, a1: %12.4E, a2: %12.4E, a3: %12.4E, " |
288 | 0 | "b0: %12.4E, b1: %12.4E, b2: %12.4E, b3: %12.4E\n", |
289 | 0 | (double)alpha0 * SF_ALPHA0, (double)alpha1 * SF_ALPHA1, |
290 | 0 | (double)alpha2 * SF_ALPHA2, (double)alpha3 * SF_ALPHA3, |
291 | 0 | (double)beta0 * SF_BETA0, (double)beta1 * SF_BETA1, |
292 | 0 | (double)beta2 * SF_BETA2, (double)beta3 * SF_BETA3); |
293 | 0 | GPSD_LOG(LOG_DATA, &session->context->errout, |
294 | 0 | "Navcom: A0: %19.12E, A1: %19.12E\n", (double)a0 * SF_A0, |
295 | 0 | (double)a1 * SF_A1); |
296 | 0 | GPSD_LOG(LOG_DATA, &session->context->errout, |
297 | 0 | "Navcom: UTC Ref. Time: %lu, UTC Ref. Week: %u, dTls: %d\n", |
298 | 0 | (unsigned long)tot * SF_TOT, wnt, dtls); |
299 | 0 | GPSD_LOG(LOG_DATA, &session->context->errout, |
300 | 0 | "Navcom: Week of leap seconds: %u, Day number of leap seconds: %u, dTlsf: %d\n", |
301 | 0 | wnlsf, dn, dtlsf); |
302 | |
|
303 | 0 | return 0; // No flag for update of leap seconds (Not part of a fix) |
304 | |
|
305 | 0 | #undef SF_A0 |
306 | 0 | #undef SF_A1 |
307 | 0 | #undef SF_TOT |
308 | 0 | #undef SF_ALPHA0 |
309 | 0 | #undef SF_ALPHA1 |
310 | 0 | #undef SF_ALPHA2 |
311 | 0 | #undef SF_ALPHA3 |
312 | 0 | #undef SF_BETA0 |
313 | 0 | #undef SF_BETA1 |
314 | 0 | #undef SF_BETA2 |
315 | 0 | #undef SF_BETA3 |
316 | 0 | } |
317 | | |
318 | | // Acknowledgement (without error) |
319 | | static gps_mask_t handle_0x06(struct gps_device_t *session) |
320 | 67 | { |
321 | 67 | unsigned char *buf = session->lexer.outbuffer + 3; |
322 | 67 | uint8_t cmd_id = getub(buf, 3); |
323 | 67 | uint8_t port = getub(buf, 4); |
324 | | |
325 | | // This tells us which serial port was used last |
326 | 67 | session->driver.navcom.physical_port = port; |
327 | 67 | GPSD_LOG(LOG_PROG, &session->context->errout, |
328 | 67 | "Navcom: received packet type 0x06 (Acknowledgement (without " |
329 | 67 | "error))\n"); |
330 | 67 | GPSD_LOG(LOG_DATA, &session->context->errout, |
331 | 67 | "Navcom: acknowledged command id 0x%02x on port %c\n", |
332 | 67 | cmd_id, (port == 0 ? 'A' : (port == 1 ? 'B' : '?'))); |
333 | 67 | return 0; // Nothing updated |
334 | 67 | } |
335 | | |
336 | | // Negative Acknowledge*/ |
337 | | static gps_mask_t handle_0x15(struct gps_device_t *session) |
338 | 615 | { |
339 | 615 | unsigned n; |
340 | 615 | unsigned char *buf = session->lexer.outbuffer + 3; |
341 | 615 | unsigned msg_len = getleu16(buf, 1); |
342 | 615 | uint8_t port, cmd_id = getub(buf, 3); |
343 | | |
344 | 615 | GPSD_LOG(LOG_PROG, &session->context->errout, |
345 | 615 | "Navcom: received packet type 0x15 (Negative Acknowledge)\n"); |
346 | | |
347 | 615 | if (18 < msg_len) { |
348 | 150 | GPSD_LOG(LOG_PROG, &session->context->errout, |
349 | 150 | "Navcom: 0x15 too long %u\n", msg_len); |
350 | 150 | return 0; |
351 | 150 | } |
352 | 1.92k | for (n = 4; n < (msg_len - 2); n += 2) { |
353 | 1.46k | uint8_t err_id = getub(buf, n); |
354 | 1.46k | uint8_t err_desc = getub(buf, n + 1); |
355 | 1.46k | GPSD_LOG(LOG_DATA, &session->context->errout, |
356 | 1.46k | "Navcom: error id = 0x%02x, error description = 0x%02x\n", |
357 | 1.46k | err_id, err_desc); |
358 | 1.46k | } |
359 | 465 | port = getub(buf, n); |
360 | 465 | GPSD_LOG(LOG_DATA, &session->context->errout, |
361 | 465 | "Navcom: negative acknowledge was for command id 0x%02x " |
362 | 465 | "on port %c\n", |
363 | 465 | cmd_id, (port == 0 ? 'A' : (port == 1 ? 'B' : '?'))); |
364 | 465 | return 0; // Nothing updated*/ |
365 | 615 | } |
366 | | |
367 | | // PVT Block*/ |
368 | | static gps_mask_t handle_0xb1(struct gps_device_t *session) |
369 | 0 | { |
370 | 0 | gps_mask_t mask = 0; |
371 | 0 | unsigned char *buf = session->lexer.outbuffer + 3; |
372 | 0 | uint16_t week; |
373 | 0 | uint32_t tow; |
374 | 0 | timespec_t ts_tow = {0, 0}; // pacify codacy |
375 | 0 | int32_t lat, lon; |
376 | 0 | char ts_buf[TIMESPEC_LEN]; |
377 | | // Resolution of lat/lon values (2^-11)*/ |
378 | 0 | #define LL_RES (0.00048828125) |
379 | 0 | uint8_t lat_fraction, lon_fraction; |
380 | | // Resolution of lat/lon fractions (2^-15)*/ |
381 | 0 | #define LL_FRAC_RES (0.000030517578125) |
382 | 0 | uint8_t nav_mode; |
383 | 0 | int32_t ellips_height, altitude; |
384 | | // Resolution of height and altitude values (2.0^-10)*/ |
385 | 0 | #define EL_RES (0.0009765625) |
386 | 0 | double vel_north, vel_east, vel_up; |
387 | 0 | uint8_t gdop, pdop, hdop, vdop, tdop; |
388 | | // This value means "undefined"*/ |
389 | 0 | #define DOP_UNDEFINED (255) |
390 | |
|
391 | 0 | int16_t ant_height_adj; |
392 | 0 | int32_t set_delta_up; |
393 | | /* Resolution of delta north, east, and up, |
394 | | * and ant. height adjustment values (1mm) */ |
395 | 0 | #define D_RES (0.001) |
396 | |
|
397 | | #ifdef __UNUSED__ |
398 | | /* Other values provided by the PVT block which we |
399 | | * may want to provide in the future. |
400 | | */ |
401 | | uint8_t dgps_conf; |
402 | | uint16_t max_dgps_age; |
403 | | uint8_t ext_nav_mode; |
404 | | int32_t set_delta_north, set_delta_east; |
405 | | uint8_t nav_failure_code; |
406 | | #endif // __UNUSED__*/ |
407 | | |
408 | | // Timestamp |
409 | 0 | week = (uint16_t) getleu16(buf, 3); |
410 | 0 | tow = (uint32_t) getleu32(buf, 5); // tow in ms |
411 | 0 | MSTOTS(&ts_tow, tow); |
412 | 0 | session->newdata.time = gpsd_gpstime_resolv(session, week, ts_tow); |
413 | | |
414 | | // Get latitude, longitude |
415 | 0 | lat = getles32(buf, 13); |
416 | 0 | lon = getles32(buf, 17); |
417 | 0 | lat_fraction = (uint8_t) (getub(buf, 21) >> 4); |
418 | 0 | lon_fraction = (uint8_t) (getub(buf, 21) & 0x0f); |
419 | |
|
420 | 0 | session->newdata.latitude = |
421 | 0 | (double)(lat * LL_RES + lat_fraction * LL_FRAC_RES) / 3600; |
422 | 0 | session->newdata.longitude = |
423 | 0 | (double)(lon * LL_RES + lon_fraction * LL_FRAC_RES) / 3600; |
424 | | |
425 | | // Nav mode |
426 | 0 | nav_mode = (uint8_t) getub(buf, 22); |
427 | 0 | if (-nav_mode & 0x80) { |
428 | 0 | session->newdata.status = STATUS_UNK; |
429 | 0 | session->newdata.mode = MODE_NO_FIX; |
430 | 0 | } else { |
431 | 0 | session->newdata.mode = ((nav_mode & 0x40)!=0 ? MODE_3D : MODE_2D); |
432 | 0 | session->newdata.status = |
433 | 0 | ((nav_mode & 0x03)!=0 ? STATUS_DGPS : STATUS_GPS); |
434 | 0 | } |
435 | | |
436 | | // altHAE |
437 | 0 | ellips_height = getles32(buf, 23); |
438 | | // altMSL |
439 | 0 | altitude = getles32(buf, 27); |
440 | |
|
441 | 0 | ant_height_adj = getles16(buf, 51); |
442 | 0 | set_delta_up = getles32(buf, 79); |
443 | |
|
444 | 0 | session->newdata.altMSL = (double)(altitude * EL_RES) |
445 | 0 | + (ant_height_adj * D_RES) + (set_delta_up * D_RES); |
446 | 0 | session->newdata.altHAE = (double)(ellips_height) * EL_RES |
447 | 0 | + (ant_height_adj * D_RES) + (set_delta_up * D_RES); |
448 | | // Let gpsd_error_model() deal with geoid_sep |
449 | | |
450 | | // Speed Data |
451 | 0 | vel_north = (double)getles3224(buf, 31); |
452 | 0 | vel_east = (double)getles3224(buf, 34); |
453 | 0 | vel_up = (double)getles3224(buf, 37); |
454 | |
|
455 | 0 | session->newdata.NED.velN = vel_north * 0.1; |
456 | 0 | session->newdata.NED.velE = vel_east * 0.1; |
457 | 0 | session->newdata.NED.velD = -vel_up * 0.1; |
458 | | |
459 | | // Quality indicators |
460 | | // UNUSED fom = getub(buf, 40); * FOM is DRMS |
461 | 0 | gdop = getub(buf, 41); |
462 | 0 | pdop = getub(buf, 42); |
463 | 0 | hdop = getub(buf, 43); |
464 | 0 | vdop = getub(buf, 44); |
465 | 0 | tdop = getub(buf, 45); |
466 | | // UNUSED tfom = getub(buf, 46); * tfom == 10 * TDOP |
467 | | |
468 | | // let gpsd_error_model() do the error estimates |
469 | |
|
470 | 0 | if (gdop != DOP_UNDEFINED) { |
471 | 0 | session->gpsdata.dop.gdop = gdop / 10.0; |
472 | 0 | mask |= DOP_SET; |
473 | 0 | } |
474 | 0 | if (pdop != DOP_UNDEFINED) { |
475 | 0 | session->gpsdata.dop.pdop = pdop / 10.0; |
476 | 0 | mask |= DOP_SET; |
477 | 0 | } |
478 | 0 | if (hdop != DOP_UNDEFINED) { |
479 | 0 | session->gpsdata.dop.hdop = hdop / 10.0; |
480 | 0 | mask |= DOP_SET; |
481 | 0 | } |
482 | 0 | if (vdop != DOP_UNDEFINED) { |
483 | 0 | session->gpsdata.dop.vdop = vdop / 10.0; |
484 | 0 | mask |= DOP_SET; |
485 | 0 | } |
486 | 0 | if (tdop != DOP_UNDEFINED) { |
487 | 0 | session->gpsdata.dop.tdop = tdop / 10.0; |
488 | 0 | mask |= DOP_SET; |
489 | 0 | } |
490 | |
|
491 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
492 | 0 | "Navcom: received packet type 0xb1 (PVT Report)\n"); |
493 | 0 | GPSD_LOG(LOG_DATA, &session->context->errout, |
494 | 0 | "Navcom: navigation mode %s (0x%02x) - %s - %s\n", |
495 | 0 | ((-nav_mode & 0x80)!='\0' ? "invalid" : "valid"), nav_mode, |
496 | 0 | ((nav_mode & 0x40)!='\0' ? "3D" : "2D"), |
497 | 0 | ((nav_mode & 0x03)!='\0' ? "DGPS" : "GPS")); |
498 | 0 | GPSD_LOG(LOG_DATA, &session->context->errout, |
499 | 0 | "Navcom: velocities: north = %f east = %f up = %f\n", |
500 | 0 | session->newdata.NED.velN, |
501 | 0 | session->newdata.NED.velE, |
502 | 0 | -session->newdata.NED.velD); |
503 | 0 | #undef D_RES |
504 | 0 | #undef LL_RES |
505 | 0 | #undef LL_FRAC_RES |
506 | 0 | #undef EL_RES |
507 | 0 | #undef VEL_RES |
508 | 0 | #undef DOP_UNDEFINED |
509 | |
|
510 | 0 | mask |= LATLON_SET | ALTITUDE_SET | STATUS_SET | MODE_SET | USED_IS | |
511 | 0 | HERR_SET | TIMERR_SET | VNED_SET | TIME_SET | NTPTIME_IS; |
512 | 0 | GPSD_LOG(LOG_DATA, &session->context->errout, |
513 | 0 | "PVT 0xb1: time=%s, lat=%.2f lon=%.2f altHAE=%.2f " |
514 | 0 | "altMSL %.2f mode=%d status=%d gdop=%.2f pdop=%.2f hdop=%.2f " |
515 | 0 | "vdop=%.2f tdop=%.2f mask={%s}\n", |
516 | 0 | timespec_str(&session->newdata.time, ts_buf, sizeof(ts_buf)), |
517 | 0 | session->newdata.latitude, |
518 | 0 | session->newdata.longitude, |
519 | 0 | session->newdata.altHAE, |
520 | 0 | session->newdata.altMSL, |
521 | 0 | session->newdata.mode, |
522 | 0 | session->newdata.status, |
523 | 0 | session->gpsdata.dop.gdop, |
524 | 0 | session->gpsdata.dop.pdop, |
525 | 0 | session->gpsdata.dop.hdop, |
526 | 0 | session->gpsdata.dop.vdop, session->gpsdata.dop.tdop, |
527 | 0 | gps_maskdump(mask)); |
528 | 0 | return mask; |
529 | 0 | } |
530 | | |
531 | | // Packed Ephemeris Data |
532 | | static gps_mask_t handle_0x81(struct gps_device_t *session) |
533 | 4 | { |
534 | | // Scale factors for everything |
535 | | // 2^-31 |
536 | 4 | #define SF_TGD (.000000000465661287307739257812) |
537 | | // 2^4 |
538 | 4 | #define SF_TOC (16) |
539 | | // 2^-55 |
540 | 4 | #define SF_AF2 (.000000000000000027755575615628) |
541 | | // 2^-43 |
542 | 4 | #define SF_AF1 (.000000000000113686837721616029) |
543 | | // 2^-31 |
544 | 4 | #define SF_AF0 (.000000000465661287307739257812) |
545 | | // 2^-5 |
546 | 4 | #define SF_CRS (.031250000000000000000000000000) |
547 | | // 2^-43 |
548 | 4 | #define SF_DELTA_N (.000000000000113686837721616029) |
549 | | // 2^-31 |
550 | 4 | #define SF_M0 (.000000000465661287307739257812) |
551 | | // 2^-29 |
552 | 4 | #define SF_CUC (.000000001862645149230957031250) |
553 | | // 2^-33 |
554 | 4 | #define SF_E (.000000000116415321826934814453) |
555 | | // 2^-29 |
556 | 4 | #define SF_CUS (.000000001862645149230957031250) |
557 | | // 2^-19 |
558 | 4 | #define SF_SQRT_A (.000001907348632812500000000000) |
559 | | // 2^4 |
560 | 4 | #define SF_TOE (16) |
561 | | // 2^-29 |
562 | 4 | #define SF_CIC (.000000001862645149230957031250) |
563 | | // 2^-31 |
564 | 4 | #define SF_OMEGA0 (.000000000465661287307739257812) |
565 | | // 2^-29 |
566 | 4 | #define SF_CIS (.000000001862645149230957031250) |
567 | | // 2^-31 |
568 | 4 | #define SF_I0 (.000000000465661287307739257812) |
569 | | // 2^-5 |
570 | 4 | #define SF_CRC (.031250000000000000000000000000) |
571 | | // 2^-31 |
572 | 4 | #define SF_OMEGA (.000000000465661287307739257812) |
573 | | // 2^-43 |
574 | 4 | #define SF_OMEGADOT (.000000000000113686837721616029) |
575 | | // 2^-43 |
576 | 4 | #define SF_IDOT (.000000000000113686837721616029) |
577 | | |
578 | 4 | char ts_buf[TIMESPEC_LEN]; |
579 | 4 | unsigned char *buf = session->lexer.outbuffer + 3; |
580 | 4 | uint8_t prn = getub(buf, 3); |
581 | 4 | uint16_t week = getleu16(buf, 4); |
582 | 4 | uint32_t tow = getleu32(buf, 6); |
583 | 4 | uint16_t iodc = getleu16(buf, 10); |
584 | | /* And now the fun starts... everything that follows is |
585 | | * raw GPS data minus parity */ |
586 | | // Subframe 1, words 3 to 10 minus parity |
587 | 4 | uint16_t wn = (getleu16_be(buf, 12) & 0xffc0) >> 6; |
588 | 4 | uint8_t cl2 = (getub(buf, 13) & 0x30) >> 4; |
589 | 4 | uint8_t ura = getub(buf, 13) & 0x0f; |
590 | 4 | uint8_t svh = (getub(buf, 14) & 0xfc) >> 2; |
591 | | /* We already have IODC from earlier in the message, so |
592 | | * we do not decode again */ |
593 | | // uint16_t iodc = (getub(buf, 14)&0x03)<<8;*/ |
594 | 4 | uint8_t l2pd = (getub(buf, 15) & 0x80) >> 7; |
595 | 4 | int8_t tgd = getsb(buf, 26); |
596 | | // iodc |= getub(buf, 27);*/ |
597 | 4 | uint16_t toc = getleu16_be(buf, 28); |
598 | 4 | int8_t af2 = getsb(buf, 30); |
599 | 4 | int16_t af1 = getles16_be(buf, 31); |
600 | 4 | int32_t af0 = getles3224_be(buf, 33) >> 2; |
601 | | // Subframe 2, words 3 to 10 minus parity |
602 | 4 | uint8_t iode = getub(buf, 36); |
603 | 4 | int16_t crs = getles16_be(buf, 37); |
604 | 4 | int16_t delta_n = getles16_be(buf, 39); |
605 | 4 | int32_t m0 = getles32_be(buf, 41); |
606 | 4 | int16_t cuc = getles16_be(buf, 45); |
607 | 4 | uint32_t e = getleu32_be(buf, 47); |
608 | 4 | int16_t cus = getles16_be(buf, 51); |
609 | 4 | uint32_t sqrt_a = getleu32_be(buf, 53); |
610 | 4 | uint16_t toe = getleu16_be(buf, 57); |
611 | | // NOTE - Fit interval & AODO not collected |
612 | | // Subframe 3, words 3 to 10 minus parity |
613 | 4 | int16_t cic = getles16_be(buf, 60); |
614 | 4 | int32_t Omega0 = getles32_be(buf, 62); |
615 | 4 | int16_t cis = getles16_be(buf, 66); |
616 | 4 | int32_t i0 = getles32_be(buf, 68); |
617 | 4 | int16_t crc = getles16_be(buf, 72); |
618 | 4 | int32_t omega = getles32_be(buf, 74); |
619 | 4 | int32_t Omegadot = getles3224_be(buf, 78); |
620 | | /* Question: What is the proper way of shifting a signed int 2 bits to |
621 | | * the right, preserving sign? Answer: integer division by 4. */ |
622 | 4 | int16_t idot = (int16_t) (((getles16_be(buf, 82) & 0xfffc) / 4) | |
623 | 4 | ((getub(buf, 82) & 80) ? 0xc000 : 0x0000)); |
624 | 4 | session->context->gps_week = (unsigned short)wn; |
625 | 4 | DTOTS(&session->context->gps_tow, (double)(toc * SF_TOC)); |
626 | | // leap second? |
627 | 4 | GPSD_LOG(LOG_PROG, &session->context->errout, |
628 | 4 | "Navcom: received packet type 0x81 (Packed Ephemeris Data)\n"); |
629 | 4 | GPSD_LOG(LOG_DATA, &session->context->errout, |
630 | 4 | "Navcom: PRN: %u, Week: %u, TOW: %s " |
631 | 4 | "SV clock bias/drift/drift rate: %#19.12E/%#19.12E/%#19.12E\n", |
632 | 4 | prn, |
633 | 4 | session->context->gps_week, |
634 | 4 | timespec_str(&session->context->gps_tow, ts_buf, sizeof(ts_buf)), |
635 | 4 | ((double)af0) * SF_AF0, |
636 | 4 | ((double)af1) * SF_AF1, ((double)af2) * SF_AF2); |
637 | 4 | GPSD_LOG(LOG_DATA, &session->context->errout, |
638 | 4 | "Navcom: IODE (!AODE): %u Crs: %19.12e, Delta n: %19.12e, M0: %19.12e\n", |
639 | 4 | iode, (double)crs * SF_CRS, |
640 | 4 | (double)delta_n * SF_DELTA_N * GPS_PI, |
641 | 4 | (double)m0 * SF_M0 * GPS_PI); |
642 | 4 | GPSD_LOG(LOG_DATA, &session->context->errout, |
643 | 4 | "Navcom: Cuc: %19.12e, Eccentricity: %19.12e, Cus: %19.12e, A^1/2: %19.12e\n", |
644 | 4 | (double)cuc * SF_CUC, (double)e * SF_E, (double)cus * SF_CUS, |
645 | 4 | (double)sqrt_a * SF_SQRT_A); |
646 | 4 | GPSD_LOG(LOG_DATA, &session->context->errout, |
647 | 4 | "Navcom: TOE: %u, Cic: %19.12e, Omega %19.12e, Cis: %19.12e\n", |
648 | 4 | toe * SF_TOE, (double)cic * SF_CIC, |
649 | 4 | (double)Omega0 * SF_OMEGA0 * GPS_PI, (double)cis * SF_CIS); |
650 | 4 | GPSD_LOG(LOG_DATA, &session->context->errout, |
651 | 4 | "Navcom: i0: %19.12e, Crc: %19.12e, omega: %19.12e, Omega dot: %19.12e\n", |
652 | 4 | (double)i0 * SF_I0 * GPS_PI, (double)crc * SF_CRC, |
653 | 4 | (double)omega * SF_OMEGA * GPS_PI, |
654 | 4 | (double)Omegadot * SF_OMEGADOT * GPS_PI); |
655 | 4 | GPSD_LOG(LOG_DATA, &session->context->errout, |
656 | 4 | "Navcom: IDOT: %19.12e, Codes on L2: 0x%x, GPS Week: %u, L2 P data flag: %x\n", |
657 | 4 | (double)idot * SF_IDOT * GPS_PI, cl2, |
658 | 4 | week - (week % 1024) + wn, l2pd); |
659 | 4 | GPSD_LOG(LOG_DATA, &session->context->errout, |
660 | 4 | "Navcom: SV accuracy: 0x%x, SV health: 0x%x, TGD: %f, IODC (!AODC): %u\n", |
661 | 4 | ura, svh, (double)tgd * SF_TGD, iodc); |
662 | 4 | GPSD_LOG(LOG_DATA, &session->context->errout, |
663 | 4 | "Navcom: Transmission time: %u\n", tow); |
664 | | |
665 | 4 | #undef SF_TGD |
666 | 4 | #undef SF_TOC |
667 | 4 | #undef SF_AF2 |
668 | 4 | #undef SF_AF1 |
669 | 4 | #undef SF_AF0 |
670 | 4 | #undef SF_CRS |
671 | 4 | #undef SF_DELTA_N |
672 | 4 | #undef SF_M0 |
673 | 4 | #undef SF_CUC |
674 | 4 | #undef SF_E |
675 | 4 | #undef SF_CUS |
676 | 4 | #undef SF_SQRT_A |
677 | 4 | #undef SF_TOE |
678 | 4 | #undef SF_CIC |
679 | 4 | #undef SF_OMEGA0 |
680 | 4 | #undef SF_CIS |
681 | 4 | #undef SF_I0 |
682 | 4 | #undef SF_CRC |
683 | 4 | #undef SF_OMEGA |
684 | 4 | #undef SF_OMEGADOT |
685 | 4 | #undef SF_IDOT |
686 | | |
687 | 4 | return 0; |
688 | 4 | } |
689 | | |
690 | | // Channel Status |
691 | | static gps_mask_t handle_0x86(struct gps_device_t *session) |
692 | 238 | { |
693 | 238 | size_t n, i, nsu; |
694 | 238 | unsigned char *buf = session->lexer.outbuffer + 3; |
695 | 238 | unsigned msg_len = getleu16(buf, 1); |
696 | 238 | unsigned short week = getleu16(buf, 3); |
697 | 238 | uint32_t tow = getleu32(buf, 5); |
698 | 238 | uint8_t eng_status = getub(buf, 9); |
699 | 238 | uint16_t sol_status = getleu16(buf, 10); |
700 | 238 | uint8_t sats_visible = getub(buf, 12); |
701 | | //uint8_t sats_tracked = getub(buf, 13); |
702 | | //uint8_t used_sats = getub(buf, 14); |
703 | | //uint8_t pdop = getub(buf, 15); |
704 | 238 | timespec_t ts_tow; |
705 | | |
706 | 238 | MSTOTS(&ts_tow, tow); |
707 | | |
708 | | // Timestamp |
709 | 238 | session->gpsdata.skyview_time = gpsd_gpstime_resolv(session, week, ts_tow); |
710 | | |
711 | | // Give this driver a single point of truth about DOPs |
712 | | //session->gpsdata.dop.pdop = (int)pdop / 10.0; |
713 | | |
714 | | // Satellite count |
715 | 238 | session->gpsdata.satellites_visible = (int)sats_visible; |
716 | 238 | if (MAXCHANNELS < session->gpsdata.satellites_visible) { |
717 | 135 | GPSD_LOG(LOG_WARN, &session->context->errout, |
718 | 135 | "NAVCOM: too many satellites %d\n", |
719 | 135 | session->gpsdata.satellites_visible); |
720 | 135 | session->gpsdata.satellites_visible = MAXCHANNELS; |
721 | 135 | } |
722 | | |
723 | | // Fix mode |
724 | 238 | switch (sol_status & 0x05) { |
725 | 5 | case 0x05: |
726 | 5 | session->newdata.status = STATUS_DGPS; |
727 | 5 | break; |
728 | 5 | case 0x01: |
729 | 5 | session->newdata.status = STATUS_GPS; |
730 | 5 | break; |
731 | 228 | default: |
732 | 228 | session->newdata.status = STATUS_UNK; |
733 | 238 | } |
734 | | |
735 | 238 | GPSD_LOG(LOG_DATA, &session->context->errout, |
736 | 238 | "Navcom: engine status 0x%x almanac %s time 0x%x pos 0x%x\n", |
737 | 238 | eng_status & 0x07, ((eng_status & 0x08) ? "valid" : "invalid"), |
738 | 238 | eng_status & 0x30 >> 4, eng_status & 0xc0 >> 6); |
739 | | |
740 | | // Satellite details |
741 | 238 | i = nsu = 0; |
742 | 238 | if (298 < msg_len) { |
743 | | /* pasify coverity |
744 | | * msg_len = 18 + (14 * nsat) |
745 | | * assume 20 sats max */ |
746 | 0 | msg_len = 298; |
747 | 0 | } |
748 | 361 | for (n = 17; n < msg_len; n += 14) { |
749 | 123 | uint8_t prn, ele, ca_snr, p2_snr, log_channel, hw_channel, s, stat; |
750 | 123 | uint16_t azm, dgps_age; |
751 | 123 | if (i >= MAXCHANNELS) { |
752 | 0 | GPSD_LOG(LOG_ERROR, &session->context->errout, |
753 | 0 | "Navcom: packet type 0x86: too many satellites!\n"); |
754 | 0 | gpsd_zero_satellites(&session->gpsdata); |
755 | 0 | return 0; |
756 | 0 | } |
757 | 123 | prn = getub(buf, n); |
758 | | /* |
759 | | * This field is described in the Technical Reference as follows: |
760 | | * |
761 | | * Channel Tracking Status: |
762 | | * B0-B1: C/A tracking status |
763 | | * B2-B3: P1 tracking status |
764 | | * B4-B5: P2 tracking status |
765 | | * 00 Acquisition or reacquisition |
766 | | * 01 Code loop locked |
767 | | * 02 Costas loop locked |
768 | | * 11 Full tracking with aiding and active |
769 | | * multipath reduction - all data is valid |
770 | | * B6=1: C/A Bit sync |
771 | | * B7=1: C/A Frame sync |
772 | | * |
773 | | * By observation, the satellite is in use if this status is 0xff. |
774 | | * But errors here are not very serious, all they can affect is |
775 | | * the coverance-matrix calculation for error modeling. |
776 | | */ |
777 | 123 | stat = getub(buf, n + 1); |
778 | 123 | log_channel = getub(buf, n + 2); |
779 | 123 | ele = getub(buf, n + 5); |
780 | 123 | azm = getleu16(buf, n + 6); |
781 | 123 | ca_snr = getub(buf, n + 8); |
782 | 123 | p2_snr = getub(buf, n + 10); |
783 | 123 | dgps_age = getleu16(buf, n + 11); |
784 | 123 | hw_channel = getub(buf, n + 13); |
785 | 123 | s = (unsigned char)0; |
786 | | /* NOTE - In theory, I think one would check for hw channel number to |
787 | | * see if one is dealing with a GPS or other satellite, but the |
788 | | * channel numbers reported bear no resemblance to what the spec |
789 | | * says should be. So I check for the fact that if all three |
790 | | * values below are zero, one is not interested in this satellite */ |
791 | 123 | if (!(ele == 0 && azm == 0 && dgps_age == 0)) { |
792 | 46 | session->gpsdata.skyview[i].PRN = (short)prn; |
793 | 46 | session->gpsdata.skyview[i].elevation = (double)ele; |
794 | 46 | session->gpsdata.skyview[i].azimuth = (double)azm; |
795 | 46 | s = session->gpsdata.skyview[i].ss = |
796 | 46 | (p2_snr ? p2_snr : ca_snr) / 4.0; |
797 | 46 | session->gpsdata.skyview[i++].used = (stat == 0xff); |
798 | 46 | if (stat == 0xff) |
799 | 0 | nsu++; |
800 | 46 | } |
801 | 123 | session->gpsdata.satellites_used = (int)nsu; |
802 | 123 | GPSD_LOG(LOG_DATA, &session->context->errout, |
803 | 123 | "Navcom: prn = %3u, ele = %02u, azm = %03u, snr = %d (%s), " |
804 | 123 | "dgps age = %.1fs, log ch = %d, hw ch = 0x%02x\n", |
805 | 123 | prn, ele, azm, s, (p2_snr ? "P2" : "C/A"), |
806 | 123 | (double)dgps_age * 0.1, log_channel & 0x3f, hw_channel); |
807 | 123 | GPSD_LOG(LOG_DATA, &session->context->errout, |
808 | 123 | "Navcom: sol. valid = %c, clock = %s, pos. = %s, " |
809 | 123 | "height = %s, err. code = 0x%x\n", |
810 | 123 | ((sol_status & 0x01) ? 'Y' : 'N'), |
811 | 123 | ((sol_status & 0x02) ? "stable" : "unstable"), |
812 | 123 | ((sol_status & 0x04) ? "dgps" : "unaided"), |
813 | 123 | ((sol_status & 0x08) ? "solved" : "constrained"), |
814 | 123 | ((sol_status & 0x01) ? 0x00 : sol_status & 0x0f00 >> 8)); |
815 | 123 | } |
816 | | |
817 | 238 | GPSD_LOG(LOG_DATA, &session->context->errout, |
818 | 238 | "CS 0x86: visible=%d, used=%d, mask={SATELLITE|STATUS}\n", |
819 | 238 | session->gpsdata.satellites_visible, |
820 | 238 | session->gpsdata.satellites_used); |
821 | 238 | return SATELLITE_SET | STATUS_SET; |
822 | 238 | } |
823 | | |
824 | | /* Raw Meas. Data Block |
825 | | * Size 4 + 8 + (16 * numSat) = 524 |
826 | | */ |
827 | | static gps_mask_t handle_0xb0(struct gps_device_t *session) |
828 | 1 | { |
829 | 1 | char ts_buf[TIMESPEC_LEN]; |
830 | | /* L1 wavelength (299792458m/s / 1575420000Hz) |
831 | | * from their Technical reference Manual */ |
832 | 3 | #define LAMBDA_L1 (299792458.0 / 1575420000.0) |
833 | 1 | unsigned n; |
834 | 1 | unsigned char *buf = session->lexer.outbuffer + 3; |
835 | 1 | unsigned msg_len = getleu16(buf, 1); |
836 | 1 | uint16_t week = getleu16(buf, 3); |
837 | 1 | uint32_t tow = getleu32(buf, 5); |
838 | 1 | uint8_t tm_slew_acc = getub(buf, 9); |
839 | 1 | uint8_t status = getub(buf, 10); |
840 | | |
841 | 1 | session->context->gps_week = (unsigned short)week; |
842 | 1 | MSTOTS(&session->context->gps_tow, tow); |
843 | | |
844 | 1 | GPSD_LOG(LOG_PROG, &session->context->errout, |
845 | 1 | "Navcom: received packet type 0xb0 (Raw Meas. Data Block)\n"); |
846 | 1 | GPSD_LOG(LOG_DATA, &session->context->errout, |
847 | 1 | "Navcom: week = %u, tow = %s " |
848 | 1 | "time slew accumulator = %u (1/1023mS), status = 0x%02x " |
849 | 1 | "(%sclock %s - %u blocks follow) msg_len %u\n", |
850 | 1 | session->context->gps_week, |
851 | 1 | timespec_str(&session->context->gps_tow, ts_buf, sizeof(ts_buf)), |
852 | 1 | tm_slew_acc, status, |
853 | 1 | ((status & 0x80) ? "channel time set - " : ""), |
854 | 1 | ((status & 0x40) ? "stable" : "not stable"), status & 0x0f, |
855 | 1 | msg_len); |
856 | | |
857 | 1 | if (530 < msg_len) { |
858 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
859 | 0 | "Navcom: received packet type 0xb0, length %u too long\n", |
860 | 0 | msg_len); |
861 | 0 | return 0; |
862 | 0 | } |
863 | | |
864 | 3 | for (n = 11; n < (msg_len - 1); n += 16) { |
865 | 2 | uint8_t sv_status = getub(buf, n); |
866 | 2 | uint8_t ch_status = getub(buf, n + 1); |
867 | 2 | uint32_t ca_pseudorange = getleu32(buf, n + 2); |
868 | | // integer division by 16 is a sign-preserving right shift of 4 bits |
869 | 2 | int32_t l1_phase = getles3224(buf, n + 6) / 16; |
870 | 2 | uint8_t l1_slips = (uint8_t) (getles3224(buf, n + 6) & 0x0f); |
871 | 2 | int16_t p1_ca_pseudorange = getles16(buf, n + 9); |
872 | 2 | int16_t p2_ca_pseudorange = getles16(buf, n + 11); |
873 | 2 | int32_t l2_phase = getles3224(buf, n + 13) / 16; |
874 | 2 | uint8_t l2_slips = (uint8_t) (getles3224(buf, n + 13) & 0x0f); |
875 | 2 | double c1 = |
876 | 2 | ((sv_status & 0x80) ? (double)ca_pseudorange / 16.0 * |
877 | 2 | LAMBDA_L1 : NAN); |
878 | 2 | double l1 = |
879 | 2 | ((sv_status & 0x80) ? (double)ca_pseudorange / 16.0 + |
880 | 2 | (double)l1_phase / 256.0 : NAN); |
881 | 2 | double l2 = |
882 | 2 | ((sv_status & 0x20) |
883 | 2 | ? ((double)ca_pseudorange / 16.0 + |
884 | 1 | (double)p2_ca_pseudorange / 16.0) * (120.0 / 154.0) |
885 | 2 | + (double)l2_phase / 256.0 : NAN); |
886 | 2 | double p1 = |
887 | 2 | ((sv_status & 0x40) ? c1 + |
888 | 2 | (double)p1_ca_pseudorange / 16.0 * LAMBDA_L1 : NAN); |
889 | 2 | double p2 = |
890 | 2 | ((sv_status & 0x20) ? c1 + |
891 | 2 | (double)p2_ca_pseudorange / 16.0 * LAMBDA_L1 : NAN); |
892 | 2 | GPSD_LOG(LOG_SPIN, &session->context->errout, |
893 | 2 | "Navcom: >> sv status = 0x%02x (PRN %u - C/A & L1 %s " |
894 | 2 | "- P1 %s - P2 & L2 %s)\n", |
895 | 2 | sv_status, (sv_status & 0x1f), |
896 | 2 | ((sv_status & 0x80) ? "valid" : "invalid"), |
897 | 2 | ((sv_status & 0x40) ? "valid" : "invalid"), |
898 | 2 | ((sv_status & 0x20) ? "valid" : "invalid")); |
899 | 2 | GPSD_LOG(LOG_SPIN, &session->context->errout, |
900 | 2 | "Navcom: >>> ch status = 0x%02x " |
901 | 2 | "(Logical channel: %u - CA C/No: %u dBHz) " |
902 | 2 | "sL1: %u, sL2: %u\n", ch_status, ch_status & 0x0f, |
903 | 2 | ((ch_status & 0xf0) >> 4) + 35, l1_slips, l2_slips); |
904 | 2 | GPSD_LOG(LOG_SPIN, &session->context->errout, |
905 | 2 | "Navcom: >>> C1: %14.3f, L1: %14.3f, L2: %14.3f, P1: %14.3f, P2: %14.3f\n", |
906 | 2 | c1, l1, l2, p1, p2); |
907 | 2 | } |
908 | 1 | #undef LAMBDA_L1 |
909 | 1 | return 0; // Raw measurements not yet implemented in gpsd |
910 | 1 | } |
911 | | |
912 | | // Pseudorange Noise Statistics |
913 | | static gps_mask_t handle_0xb5(struct gps_device_t *session) |
914 | 0 | { |
915 | 0 | if (sizeof(double) == 8) { |
916 | 0 | gps_mask_t mask = TIME_SET; |
917 | 0 | char *buf = (char *)session->lexer.outbuffer + 3; |
918 | 0 | uint16_t week = getleu16(buf, 3); |
919 | 0 | uint32_t tow = getleu32(buf, 5); |
920 | 0 | timespec_t ts_tow; |
921 | | #ifdef __UNUSED__ |
922 | | double rms = getled64(buf, 9); |
923 | | /* Reason why it's unused is these figures do not agree |
924 | | * with those obtained from the PVT report (handle_0xb1). |
925 | | * The figures from 0xb1 do agree with the values reported |
926 | | * by Navcom's PC utility */ |
927 | | // let gpsd_error_model() handle this |
928 | | //double ellips_maj = getled64(buf, 17); |
929 | | //double ellips_min = getled64(buf, 25); |
930 | | //double ellips_azm = getled64(buf, 33); |
931 | | double lat_sd = getled64(buf, 41); |
932 | | double lon_sd = getled64(buf, 49); |
933 | | double alt_sd = getled64(buf, 57); |
934 | | double hrms = sqrt(pow(lat_sd, 2) + pow(lon_sd, 2)); |
935 | | // Navcom doc unclear, this is likely sep? |
936 | | session->newdata.sep = rms * 1.96; |
937 | | session->newdata.eph = hrms * 1.96; |
938 | | session->newdata.epv = alt_sd * 1.96; |
939 | | mask |= HERR_SET; |
940 | | #endif // __UNUSED__ |
941 | 0 | MSTOTS(&ts_tow, tow); |
942 | 0 | session->newdata.time = gpsd_gpstime_resolv(session, |
943 | 0 | (unsigned short)week, |
944 | 0 | ts_tow); |
945 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
946 | 0 | "Navcom: received packet type 0xb5 (Pseudorange Noise Statistics)\n"); |
947 | 0 | GPSD_LOG(LOG_DATA, &session->context->errout, |
948 | 0 | "Navcom: sep = %f\n", session->newdata.sep); |
949 | 0 | return mask; |
950 | 0 | } else { |
951 | | // Ignore this message block |
952 | 0 | if (!session->driver.navcom.warned) { |
953 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
954 | 0 | "Navcom: received packet type 0xb5 (Pseudorange " |
955 | 0 | "Noise Statistics) ignored " |
956 | 0 | " - sizeof(double) == 64 bits required\n"); |
957 | 0 | session->driver.navcom.warned = true; |
958 | 0 | } |
959 | 0 | return 0; // Block ignored - wrong sizeof(double) |
960 | 0 | } |
961 | 0 | } |
962 | | |
963 | | // LBM DSP Status Block |
964 | | static gps_mask_t handle_0xd3(struct gps_device_t *session UNUSED) |
965 | 0 | { |
966 | | /* This block contains status information about the |
967 | | * unit's L-band (Inmarsat) module. There is nothing |
968 | | * interesting in it for our purposes so we do not deal |
969 | | * with it. This callback is purely to a) stop |
970 | | * "unrecognised packet" messages appearing in the log |
971 | | * and b) explain what it is for the curious */ |
972 | 0 | return 0; // Nothing updated |
973 | 0 | } |
974 | | |
975 | | // Identification Block |
976 | | static gps_mask_t handle_0xae(struct gps_device_t *session) |
977 | 122 | { |
978 | 122 | char *engconfstr, *asicstr; |
979 | 122 | unsigned char *buf = session->lexer.outbuffer + 3; |
980 | 122 | size_t msg_len = (size_t) getleu16(buf, 1); |
981 | 122 | uint8_t engconf = getub(buf, 3); |
982 | 122 | uint8_t asic = getub(buf, 4); |
983 | 122 | uint8_t swvermaj = getub(buf, 5); |
984 | 122 | uint8_t swvermin = getub(buf, 6); |
985 | 122 | uint16_t dcser = getleu16(buf, 7); |
986 | 122 | uint8_t dcclass = getub(buf, 9); |
987 | 122 | uint16_t rfcser = getleu16(buf, 10); |
988 | 122 | uint8_t rfcclass = getub(buf, 12); |
989 | 122 | uint8_t softtm[17] = {0}; |
990 | 122 | uint8_t bootstr[17] = {0}; |
991 | 122 | uint8_t ioptm[17] = {0}; |
992 | 122 | uint8_t iopvermaj = (uint8_t) 0x00; |
993 | 122 | uint8_t iopvermin = (uint8_t) 0x00; |
994 | 122 | uint8_t picver = (uint8_t) 0x00; |
995 | 122 | uint8_t slsbn = (uint8_t) 0x00; |
996 | 122 | uint8_t iopsbn = (uint8_t) 0x00; |
997 | | |
998 | 122 | memcpy(softtm, &buf[13], 16); |
999 | 122 | memcpy(bootstr, &buf[29], 16); |
1000 | 122 | if (0x0037 == msg_len) { // No IOP |
1001 | 0 | slsbn = getub(buf, 53); |
1002 | 122 | } else { // IOP Present |
1003 | 122 | iopvermaj = getub(buf, 53); |
1004 | 122 | iopvermin = getub(buf, 54); |
1005 | 122 | memcpy(ioptm, &buf[55], 16); |
1006 | 122 | picver = getub(buf, 71); |
1007 | 122 | slsbn = getub(buf, 72); |
1008 | 122 | iopsbn = getub(buf, 73); |
1009 | 122 | } |
1010 | | |
1011 | 122 | switch (engconf) { |
1012 | 0 | case 0x00: |
1013 | 0 | engconfstr = "Unknown/Undefined"; |
1014 | 0 | break; |
1015 | 0 | case 0x01: |
1016 | 0 | engconfstr = "NCT 2000 S"; |
1017 | 0 | break; |
1018 | 0 | case 0x02: |
1019 | 0 | engconfstr = "NCT 2000 D"; |
1020 | 0 | break; |
1021 | 0 | case 0x03: |
1022 | 0 | engconfstr = "Startfire Single"; |
1023 | 0 | break; |
1024 | 0 | case 0x04: |
1025 | 0 | engconfstr = "Starfire Dual"; |
1026 | 0 | break; |
1027 | 0 | case 0x05: |
1028 | 0 | engconfstr = "Pole Mount RTK (Internal Radio)"; |
1029 | 0 | break; |
1030 | 0 | case 0x06: |
1031 | 0 | engconfstr = "Pole Mount GIS (LBM)"; |
1032 | 0 | break; |
1033 | 0 | case 0x07: |
1034 | 0 | engconfstr = "Black Box RTK (Internal Radio)"; |
1035 | 0 | break; |
1036 | 0 | case 0x08: |
1037 | 0 | engconfstr = "Black Box GIS (LBM)"; |
1038 | 0 | break; |
1039 | 36 | case 0x80: |
1040 | 36 | engconfstr = "R100"; |
1041 | 36 | break; |
1042 | 34 | case 0x81: |
1043 | 34 | engconfstr = "R200"; |
1044 | 34 | break; |
1045 | 18 | case 0x82: |
1046 | 18 | engconfstr = "R210"; |
1047 | 18 | break; |
1048 | 34 | case 0x83: |
1049 | 34 | engconfstr = "R300"; |
1050 | 34 | break; |
1051 | 0 | case 0x84: |
1052 | 0 | engconfstr = "R310"; |
1053 | 0 | break; |
1054 | 0 | default: |
1055 | 0 | engconfstr = "?"; |
1056 | 122 | } |
1057 | | |
1058 | 122 | switch (asic) { |
1059 | 0 | case 0x01: |
1060 | 0 | asicstr = "A-ASIC"; |
1061 | 0 | break; |
1062 | 0 | case 0x02: |
1063 | 0 | asicstr = "B-ASIC"; |
1064 | 0 | break; |
1065 | 0 | case 0x03: |
1066 | 0 | asicstr = "C-ASIC"; |
1067 | 0 | break; |
1068 | 0 | case 0x04: |
1069 | 0 | asicstr = "M-ASIC"; |
1070 | 0 | break; |
1071 | 122 | default: |
1072 | 122 | asicstr = "?"; |
1073 | 122 | } |
1074 | | |
1075 | 122 | GPSD_LOG(LOG_PROG, &session->context->errout, |
1076 | 122 | "Navcom: received packet type 0xae (Identification Block)\n"); |
1077 | 122 | if (0x0037 == msg_len) { |
1078 | 0 | GPSD_LOG(LOG_INF, &session->context->errout, "Navcom: ID Data: " |
1079 | 0 | "%s %s Ver. %u.%u.%u, DC S/N: %u.%u, RF S/N: %u.%u, " |
1080 | 0 | "Build ID: %s, Boot software: %s\n", |
1081 | 0 | engconfstr, asicstr, swvermaj, swvermin, slsbn, dcser, |
1082 | 0 | dcclass, rfcser, rfcclass, softtm, bootstr); |
1083 | 122 | } else { |
1084 | 122 | GPSD_LOG(LOG_INF, &session->context->errout, "Navcom: ID Data: " |
1085 | 122 | "%s %s Ver. %u.%u.%u, DC S/N: %u.%u, RF S/N: %u.%u, " |
1086 | 122 | "Build ID: %s, Boot software: %s, " |
1087 | 122 | "IOP Ver.: %u.%u.%u, PIC: %u, IOP Build ID: %s\n", |
1088 | 122 | engconfstr, asicstr, swvermaj, swvermin, slsbn, dcser, |
1089 | 122 | dcclass, rfcser, rfcclass, softtm, bootstr, iopvermaj, |
1090 | 122 | iopvermin, iopsbn, picver, ioptm); |
1091 | 122 | } |
1092 | | |
1093 | 122 | (void)snprintf(session->subtype, sizeof(session->subtype), |
1094 | 122 | "%s %s Ver. %u.%u.%u S/N %u.%u %u.%u", |
1095 | 122 | engconfstr, asicstr, swvermaj, swvermin, slsbn, dcser, |
1096 | 122 | dcclass, rfcser, rfcclass); |
1097 | 122 | return DEVICEID_SET; |
1098 | 122 | } |
1099 | | |
1100 | | // Clock Drift and Offset |
1101 | | static gps_mask_t handle_0xef(struct gps_device_t *session) |
1102 | 81 | { |
1103 | 81 | unsigned char *buf = session->lexer.outbuffer + 3; |
1104 | | //uint16_t week = getleu16(buf, 3); |
1105 | | //uint32_t tow = getleu32(buf, 5); |
1106 | 81 | int8_t osc_temp = getsb(buf, 9); |
1107 | 81 | uint8_t nav_status = getub(buf, 10); |
1108 | 81 | double nav_clock_offset; |
1109 | 81 | float nav_clock_drift; |
1110 | 81 | float osc_filter_drift_est; |
1111 | 81 | char ts_buf[TIMESPEC_LEN]; |
1112 | 81 | int32_t time_slew = (int32_t) getles32(buf, 27); |
1113 | 81 | if (sizeof(double) == 8) { |
1114 | 81 | nav_clock_offset = getled64((char *)buf, 11); |
1115 | 81 | } else { |
1116 | 0 | nav_clock_offset = NAN; |
1117 | 0 | } |
1118 | 81 | if (sizeof(float) == 4) { |
1119 | 81 | nav_clock_drift = getlef32((char *)buf, 19); |
1120 | 81 | osc_filter_drift_est = getlef32((char *)buf, 23); |
1121 | 81 | } else { |
1122 | 0 | nav_clock_drift = NAN; |
1123 | 0 | osc_filter_drift_est = NAN; |
1124 | 0 | } |
1125 | | |
1126 | 81 | GPSD_LOG(LOG_DATA, &session->context->errout, |
1127 | 81 | "Navcom: oscillator temp. = %d, nav. status = 0x%02x, " |
1128 | 81 | "nav. clock offset = %f, nav. clock drift = %f, " |
1129 | 81 | "osc. filter drift est. = %f, acc.time slew value = %d\n", |
1130 | 81 | osc_temp, nav_status, nav_clock_offset, nav_clock_drift, |
1131 | 81 | osc_filter_drift_est, time_slew); |
1132 | 81 | GPSD_LOG(LOG_DATA, &session->context->errout, |
1133 | 81 | "CDO 0xef: time=%s mask={TIME}\n", |
1134 | 81 | timespec_str(&session->newdata.time, ts_buf, sizeof(ts_buf))); |
1135 | 81 | return 0; |
1136 | 81 | } |
1137 | | |
1138 | | |
1139 | | gps_mask_t navcom_parse(struct gps_device_t * session, unsigned char *buf, |
1140 | | size_t len) |
1141 | 5.22k | { |
1142 | 5.22k | unsigned char cmd_id; |
1143 | 5.22k | unsigned int msg_len; |
1144 | | |
1145 | 5.22k | if (7 > len) { |
1146 | | // min msg: STX, 0x99, 0x66, cmd_id, msg_len(2), ..., checksum |
1147 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
1148 | 0 | "Navcom: too short, len %zu\n", len); |
1149 | 0 | return 0; |
1150 | 0 | } |
1151 | | // Navcom checksum already checked in gpsd/parser.c |
1152 | | |
1153 | 5.22k | cmd_id = (unsigned char)getub(buf, 3); |
1154 | | //payload = &buf[6]; |
1155 | 5.22k | msg_len = (unsigned)getleu16(buf, 4); |
1156 | | |
1157 | 5.22k | if (4 > msg_len || |
1158 | 5.09k | (msg_len + 4) > len) { |
1159 | | /* min payload: cmd_id, msg_len(2), cksum |
1160 | | * Fuzzers love to do this. */ |
1161 | 3.95k | GPSD_LOG(LOG_WARN, &session->context->errout, |
1162 | 3.95k | "Navcom: bad lenghts, msg_len %u len %zu\n", msg_len, len); |
1163 | 3.95k | return 0; |
1164 | 3.95k | } |
1165 | | |
1166 | 1.26k | GPSD_LOG(LOG_RAW, &session->context->errout, |
1167 | 1.26k | "Navcom: packet type x%02x msg_len %u\n", cmd_id, msg_len); |
1168 | | |
1169 | 1.26k | session->cycle_end_reliable = true; |
1170 | | |
1171 | 1.26k | switch (cmd_id) { |
1172 | 67 | case 0x06: |
1173 | 67 | return handle_0x06(session); |
1174 | 615 | case 0x15: |
1175 | 615 | return handle_0x15(session); |
1176 | 4 | case 0x81: |
1177 | 4 | return handle_0x81(session); |
1178 | 0 | case 0x83: |
1179 | 0 | return handle_0x83(session); |
1180 | 238 | case 0x86: |
1181 | 238 | return handle_0x86(session); |
1182 | 122 | case 0xae: |
1183 | 122 | return handle_0xae(session); |
1184 | 1 | case 0xb0: |
1185 | 1 | return handle_0xb0(session); |
1186 | 0 | case 0xb1: |
1187 | 0 | return handle_0xb1(session) | (CLEAR_IS | REPORT_IS); |
1188 | 0 | case 0xb5: |
1189 | 0 | return handle_0xb5(session); |
1190 | 0 | case 0xd3: |
1191 | 0 | return handle_0xd3(session); |
1192 | 81 | case 0xef: |
1193 | 81 | return handle_0xef(session); |
1194 | 139 | default: |
1195 | 139 | GPSD_LOG(LOG_PROG, &session->context->errout, |
1196 | 139 | "Navcom: received packet type 0x%02x, msg_len %d - " |
1197 | 139 | "unknown or unimplemented\n", |
1198 | 139 | cmd_id, msg_len); |
1199 | 139 | return 0; |
1200 | 1.26k | } |
1201 | 1.26k | } |
1202 | | |
1203 | | |
1204 | | static gps_mask_t navcom_parse_input(struct gps_device_t *session) |
1205 | 5.22k | { |
1206 | 5.22k | if (NAVCOM_PACKET == session->lexer.type) { |
1207 | 5.22k | return navcom_parse(session, session->lexer.outbuffer, |
1208 | 5.22k | session->lexer.outbuflen); |
1209 | 5.22k | } |
1210 | 0 | if (NMEA_PACKET == session->lexer.type) { |
1211 | 0 | return nmea_parse((char *)session->lexer.outbuffer, session); |
1212 | 0 | } |
1213 | 0 | return 0; |
1214 | 0 | } |
1215 | | |
1216 | | static ssize_t navcom_control_send(struct gps_device_t *session, |
1217 | | char *buf, size_t len) |
1218 | 0 | { |
1219 | 0 | putbyte(session->msgbuf, 0, 0x02); |
1220 | 0 | putbyte(session->msgbuf, 1, 0x99); |
1221 | 0 | putbyte(session->msgbuf, 2, 0x66); |
1222 | 0 | putbyte(session->msgbuf, 3, buf[0]); // Cmd ID |
1223 | 0 | putle16(session->msgbuf, 4, len + 4); // Length |
1224 | 0 | memcpy(session->msgbuf, buf + 6, len - 1); |
1225 | 0 | putbyte(session->msgbuf, 6 + len, |
1226 | 0 | checksum((unsigned char *)session->msgbuf + 3, len + 5)); |
1227 | 0 | putbyte(session->msgbuf, 7 + len, 0x03); |
1228 | 0 | session->msgbuflen = len + 9; |
1229 | 0 | return gpsd_write(session, session->msgbuf, session->msgbuflen); |
1230 | 0 | } |
1231 | | |
1232 | | static bool navcom_speed(struct gps_device_t *session, |
1233 | | speed_t speed, char parity, int stopbits) |
1234 | 0 | { |
1235 | | // parity and stopbit switching aren't implemented |
1236 | 0 | if (parity != session->gpsdata.dev.parity |
1237 | 0 | || stopbits != (int)session->gpsdata.dev.parity) { |
1238 | 0 | return false; |
1239 | 0 | } else { |
1240 | 0 | uint8_t port, port_selection; |
1241 | 0 | uint8_t baud; |
1242 | 0 | if (session->driver.navcom.physical_port == (uint8_t) 0xFF) { |
1243 | | // We still don't know which port we're connected to |
1244 | 0 | return false; |
1245 | 0 | } |
1246 | 0 | switch (speed) { |
1247 | | /* NOTE - The spec says that certain baud combinations |
1248 | | * on ports A and B are not allowed, those are |
1249 | | * 1200/115200, 2400/57600, and 2400/115200. |
1250 | | * To try and minimise the possibility of those |
1251 | | * occurring, we do not allow baud rates below |
1252 | | * 4800. We could also disallow 57600 and 115200 |
1253 | | * to totally prevent this, but I do not consider |
1254 | | * that reasonable. Finding which baud speed the |
1255 | | * other port is set at would also be too much |
1256 | | * trouble, so we do not do it. */ |
1257 | 0 | case 4800: |
1258 | 0 | baud = 0x04; |
1259 | 0 | break; |
1260 | 0 | case 9600: |
1261 | 0 | baud = 0x06; |
1262 | 0 | break; |
1263 | 0 | case 19200: |
1264 | 0 | baud = 0x08; |
1265 | 0 | break; |
1266 | 0 | case 38400: |
1267 | 0 | baud = 0x0a; |
1268 | 0 | break; |
1269 | 0 | case 57600: |
1270 | 0 | baud = 0x0c; |
1271 | 0 | break; |
1272 | 0 | case 115200: |
1273 | 0 | baud = 0x0e; |
1274 | 0 | break; |
1275 | 0 | default: |
1276 | | // Unsupported speed |
1277 | 0 | return false; |
1278 | 0 | } |
1279 | | |
1280 | | // Proceed to construct our message |
1281 | 0 | port = session->driver.navcom.physical_port; |
1282 | 0 | port_selection = (port ? port : (uint8_t) 0xff) | baud; |
1283 | | |
1284 | | // Send it off |
1285 | 0 | navcom_cmd_0x11(session, port_selection); |
1286 | | |
1287 | | /* And cheekily return true, even though we have |
1288 | | * no way to know if the speed change succeeded |
1289 | | * until and if we receive an ACK (message 0x06), |
1290 | | * which will be at the new baud speed if the |
1291 | | * command was successful. Bottom line, the client |
1292 | | * should requery gpsd to see if the new speed is |
1293 | | * different than the old one */ |
1294 | | return true; |
1295 | 0 | } |
1296 | 0 | } |
1297 | | |
1298 | | // this is everything we export |
1299 | | // *INDENT-OFF* |
1300 | | const struct gps_type_t driver_navcom = |
1301 | | { |
1302 | | .type_name = "Navcom NCT", // full name of type |
1303 | | .packet_type = NAVCOM_PACKET, // lexer packet type |
1304 | | .flags = DRIVER_STICKY, // remember this |
1305 | | .trigger = NULL, // none |
1306 | | .channels = NAVCOM_CHANNELS, // 12 L1 + 12 L2 + 2 Inmarsat L-Band |
1307 | | .probe_detect = NULL, // no probe |
1308 | | .get_packet = packet_get1, // use generic one |
1309 | | .parse_packet = navcom_parse_input, // parse message packets |
1310 | | .rtcm_writer = gpsd_write, // send RTCM data straight |
1311 | | .init_query = NULL, // non-perturbing query |
1312 | | .event_hook = navcom_event_hook, // lifetime event handler |
1313 | | .speed_switcher = navcom_speed, // we do change baud rates |
1314 | | .mode_switcher = NULL, // there is not a mode switcher |
1315 | | .rate_switcher = NULL, // no sample-rate switcher |
1316 | | .min_cycle.tv_sec = 1, // not relevant, no rate switch |
1317 | | .min_cycle.tv_nsec = 0, // not relevant, no rate switch |
1318 | | .control_send = navcom_control_send, // how to send a control string |
1319 | | .time_offset = NULL, // no method for NTP fudge factor |
1320 | | }; |
1321 | | // *INDENT-ON* |
1322 | | |
1323 | | #endif // defined(NAVCOM_ENABLE) |
1324 | | // vim: set expandtab shiftwidth=4 |