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