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