Coverage Report

Created: 2024-02-25 06:36

/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