Coverage Report

Created: 2026-01-17 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gpsd/gpsd-3.27.6~dev/drivers/driver_nmea2000.c
Line
Count
Source
1
/*
2
 * NMEA2000 over CAN.
3
 *
4
 * NMEA2000 is proprietary and the doc is not public.
5
 * Much of this code is reverse engineered or built from
6
 * the sparse doc some vendors provide on their interpretation
7
 * of the specification.
8
 *
9
 * Here is one good source of reverse engineered info:
10
 *     https://github.com/canboat/canboat
11
 *
12
 * Message contents can be had from canboat/analyzer:
13
 *     analyzer -explain
14
 *
15
 * This file is Copyright by the GPSD project
16
 * SPDX-License-Identifier: BSD-2-clause
17
 */
18
19
#include "../include/gpsd_config.h"  // must be before all includes
20
21
#if defined(NMEA2000_ENABLE)
22
23
#include <ctype.h>
24
#include <fcntl.h>
25
#include <linux/can.h>
26
#include <linux/can/raw.h>
27
#include <math.h>
28
#include <net/if.h>
29
#include <stdarg.h>
30
#include <stdbool.h>
31
#include <stdio.h>
32
#include <stdlib.h>
33
#include <string.h>
34
#include <sys/ioctl.h>
35
#include <sys/socket.h>
36
#include <time.h>
37
#include <unistd.h>
38
39
#include "../include/gpsd.h"
40
#include "../include/libgps.h"
41
#include "../include/driver_nmea2000.h"
42
#include "../include/bits.h"
43
#include "../include/timespec.h"
44
45
46
#define LOG_FILE 1
47
0
#define NMEA2000_NETS 4
48
0
#define NMEA2000_UNITS 256
49
#define CAN_NAMELEN 32
50
0
#define MIN(a,b) ((a < b) ? a : b)
51
52
#define NMEA2000_DEBUG_AIS 0
53
#define NMEA2000_FAST_DEBUG 0
54
55
static struct gps_device_t *nmea2000_units[NMEA2000_NETS][NMEA2000_UNITS];
56
static char can_interface_name[NMEA2000_NETS][CAN_NAMELEN+1];
57
58
typedef struct PGN
59
    {
60
    unsigned int  pgn;
61
    unsigned int  fast;
62
    unsigned int  type;
63
    gps_mask_t    (* func)(unsigned char *bu, int len, struct PGN *pgn,
64
                           struct gps_device_t *session);
65
    const char    *name;
66
    } PGN;
67
68
69
#if LOG_FILE
70
FILE *logFile = NULL;
71
#endif  // of if LOG_FILE
72
73
extern bool __attribute__ ((weak)) gpsd_add_device(const char *device_name,
74
                                                   bool flag_nowait);
75
76
0
#define SHIFT32 0x100000000l
77
78
static int scale_int(int32_t var, const int64_t factor)
79
0
{
80
0
        int64_t ret;
81
82
0
        ret   = var;
83
0
        ret  *= factor;
84
0
        ret >>= 32;
85
86
0
        return((int)ret);
87
0
}
88
89
static void print_data(struct gps_context_t *context,
90
                       unsigned char *buffer, int len, PGN *pgn)
91
0
{
92
0
    if (LOG_IO <= libgps_debuglevel) {
93
0
        int   l1;
94
0
        char  bu[128];
95
96
0
        int ptr = 0;
97
0
        int l2 = sprintf(&bu[ptr], "got data:%6u:%3d: ", pgn->pgn, len);
98
0
        ptr += l2;
99
0
        for (l1 = 0; l1 < len; l1++) {
100
0
            if (((l1 % 20) == 0) && (l1 != 0)) {
101
0
                GPSD_LOG(LOG_IO, &context->errout, "%s\n", bu);
102
0
                ptr = 0;
103
0
                l2 = sprintf(&bu[ptr], "                   : ");
104
0
                ptr += l2;
105
0
            }
106
0
            l2 = sprintf(&bu[ptr], "%02ux ", (unsigned int)buffer[l1]);
107
0
            ptr += l2;
108
0
        }
109
0
        GPSD_LOG(LOG_IO, &context->errout, "%s\n", bu);
110
0
    }
111
0
}
112
113
static gps_mask_t get_mode(struct gps_device_t *session)
114
0
{
115
0
    if (session->driver.nmea2000.mode_valid & 1) {
116
0
        session->newdata.mode = session->driver.nmea2000.mode;
117
0
    } else {
118
0
        session->newdata.mode = MODE_NOT_SEEN;
119
0
    }
120
121
0
    if (session->driver.nmea2000.mode_valid & 2) {
122
0
        return MODE_SET | USED_IS;
123
0
    } else {
124
0
        return MODE_SET;
125
0
    }
126
0
}
127
128
129
static int decode_ais_header(struct gps_context_t *context,
130
    unsigned char *bu, int len, struct ais_t *ais, unsigned int mask)
131
0
{
132
0
    if (len > 4) {
133
0
        ais->type   = (unsigned int) ( bu[0]       & 0x3f);
134
0
        ais->repeat = (unsigned int) ((bu[0] >> 6) & 0x03);
135
0
        ais->mmsi   = (unsigned int)  getleu32(bu, 1);
136
0
        ais->mmsi  &= mask;
137
0
        GPSD_LOG(LOG_INF, &context->errout,
138
0
                 "NMEA2000 AIS  message type %u, MMSI %09d:\n",
139
0
                 ais->type, ais->mmsi);
140
0
        return(1);
141
0
    } else {
142
0
        ais->type   =  0;
143
0
        ais->repeat =  0;
144
0
        ais->mmsi   =  0;
145
0
        GPSD_LOG(LOG_ERROR, &context->errout,
146
0
                 "NMEA2000 AIS  message type %u, too short message.\n",
147
0
                 ais->type);
148
0
    }
149
0
    return(0);
150
0
}
151
152
153
static void decode_ais_channel_info(unsigned char *bu,
154
                                    int len,
155
                                    unsigned int offset,
156
                                    struct gps_device_t *session)
157
0
{
158
0
    unsigned int pos, bpos;
159
0
    uint16_t x;
160
161
0
    pos = offset / 8;
162
0
    bpos = offset % 8;
163
0
    if (pos >= (unsigned int)len) {
164
0
        session->driver.aivdm.ais_channel = 'A';
165
0
        return;
166
0
    }
167
0
    x = getleu16(bu, pos);
168
0
    x = (uint16_t)((x >> bpos) & 0x1f);
169
0
    switch (x) {
170
0
    case 1:
171
0
    case 3:
172
0
        session->driver.aivdm.ais_channel = 'B';
173
0
        break;
174
0
    default:
175
0
        session->driver.aivdm.ais_channel = 'A';
176
0
        break;
177
0
    }
178
0
    return;
179
0
}
180
181
182
static int ais_turn_rate(int rate)
183
0
{
184
0
    if (rate < 0) {
185
0
        return(-ais_turn_rate(-rate));
186
0
    }
187
0
    return((int)(4.733 * sqrt(rate * RAD_2_DEG * .0001 * 60.0)));
188
0
}
189
190
191
static double ais_direction(unsigned int val, double scale)
192
0
{
193
0
    if ((val == 0xffff) && (scale == 1.0)) {
194
0
        return(511.0);
195
0
    }
196
0
    return(val * RAD_2_DEG * 0.0001 * scale);
197
0
}
198
199
200
/*
201
 *   PGN 59392: ISO  Acknowledgment
202
 */
203
static gps_mask_t hnd_059392(unsigned char *bu, int len, PGN *pgn,
204
                             struct gps_device_t *session)
205
0
{
206
0
    print_data(session->context, bu, len, pgn);
207
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
208
0
             "pgn %6d(%3d):\n", pgn->pgn, session->driver.nmea2000.unit);
209
0
    return(0);
210
0
}
211
212
213
/*
214
 *   PGN 60928: ISO  Address Claim
215
 */
216
static gps_mask_t hnd_060928(unsigned char *bu, int len, PGN *pgn,
217
                             struct gps_device_t *session)
218
0
{
219
0
    print_data(session->context, bu, len, pgn);
220
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
221
0
             "pgn %6d(%3d):\n", pgn->pgn, session->driver.nmea2000.unit);
222
0
    return(0);
223
0
}
224
225
226
/*
227
 *   PGN 126208: NMEA Command/Request/Acknowledge
228
 */
229
static gps_mask_t hnd_126208(unsigned char *bu, int len, PGN *pgn,
230
                             struct gps_device_t *session)
231
0
{
232
0
    print_data(session->context, bu, len, pgn);
233
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
234
0
             "pgn %6d(%3d):\n", pgn->pgn, session->driver.nmea2000.unit);
235
0
    return(0);
236
0
}
237
238
239
/*
240
 *   PGN 126464: ISO Transmit/Receive PGN List
241
 */
242
static gps_mask_t hnd_126464(unsigned char *bu, int len, PGN *pgn,
243
                             struct gps_device_t *session)
244
0
{
245
0
    print_data(session->context, bu, len, pgn);
246
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
247
0
             "pgn %6d(%3d):\n", pgn->pgn, session->driver.nmea2000.unit);
248
0
    return(0);
249
0
}
250
251
252
/*
253
 *   PGN 126996: ISO  Product Information
254
 */
255
static gps_mask_t hnd_126996(unsigned char *bu, int len, PGN *pgn,
256
                             struct gps_device_t *session)
257
0
{
258
0
    print_data(session->context, bu, len, pgn);
259
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
260
0
             "pgn %6d(%3d):\n", pgn->pgn, session->driver.nmea2000.unit);
261
0
    return(0);
262
0
}
263
264
265
/*
266
 *   PGN 127258: GNSS Magnetic Variation
267
 *
268
 *   1 Sequence ID
269
 *   2 Variation Source
270
 *   3 Reserved Bits
271
 *   4 Age of Service (Date)
272
 *   5 Variation
273
 *   6 Reserved B
274
 */
275
static gps_mask_t hnd_127258(unsigned char *bu, int len, PGN *pgn,
276
                             struct gps_device_t *session)
277
0
{
278
0
    print_data(session->context, bu, len, pgn);
279
    // FIXME?  Get magnetic variation
280
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
281
0
             "pgn %6d(%3d):\n", pgn->pgn, session->driver.nmea2000.unit);
282
0
    return(0);
283
0
}
284
285
286
/*
287
 *   PGN 129025: GNSS Position Rapid Update
288
 */
289
static gps_mask_t hnd_129025(unsigned char *bu, int len, PGN *pgn,
290
                             struct gps_device_t *session)
291
0
{
292
0
    print_data(session->context, bu, len, pgn);
293
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
294
0
             "pgn %6d(%3d):\n", pgn->pgn, session->driver.nmea2000.unit);
295
296
0
    session->newdata.latitude = getles32(bu, 0) * 1e-7;
297
0
    session->newdata.longitude = getles32(bu, 4) * 1e-7;
298
299
0
    return LATLON_SET | get_mode(session);
300
0
}
301
302
303
/*
304
 *   PGN 129026: GNSS COG and SOG Rapid Update
305
 */
306
static gps_mask_t hnd_129026(unsigned char *bu, int len, PGN *pgn,
307
                             struct gps_device_t *session)
308
0
{
309
0
    print_data(session->context, bu, len, pgn);
310
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
311
0
             "pgn %6d(%3d):\n", pgn->pgn, session->driver.nmea2000.unit);
312
313
0
    session->driver.nmea2000.sid[0]  =  bu[0];
314
315
0
    session->newdata.track           =  getleu16(bu, 2) * 1e-4 * RAD_2_DEG;
316
0
    session->newdata.speed           =  getleu16(bu, 4) * 1e-2;
317
318
0
    return SPEED_SET | TRACK_SET | get_mode(session);
319
0
}
320
321
322
/*
323
 * PGN: 126992 / 00370020 / 1F010 - 8 - System Time
324
 *
325
 *  Field #1: SID
326
 *                  Bits: 8
327
 *                  Signed: false
328
 *  Field #2: Source
329
 *                  Bits: 4
330
 *                  Type: Lookup table
331
 *                  Signed: false
332
 *                  Lookup: 0=GPS
333
 *                  Lookup: 1=GLONASS
334
 *                  Lookup: 2=Radio Station
335
 *                  Lookup: 3=Local Cesium clock
336
 *                  Lookup: 4=Local Rubidium clock
337
 *                  Lookup: 5=Local Crystal clock
338
 *  Field #3: Reserved - Reserved
339
 *                  Bits: 4
340
 *                  Type: Binary data
341
 *                  Signed: false
342
 *  Field #4: Date - Days since January 1, 1970
343
 *                  Bits: 16
344
 *                  Units: days
345
 *                  Type: Date
346
 *                  Resolution: 1
347
 *                  Signed: false
348
 *  Field #5: Time - Seconds since midnight
349
 *                  Bits: 32
350
 *                  Units: s
351
 *                  Type: Time
352
 *                  Resolution: 0.0001
353
 *                  Signed: false
354
 *
355
 */
356
static gps_mask_t hnd_126992(unsigned char *bu, int len, PGN *pgn,
357
                             struct gps_device_t *session)
358
0
{
359
    // uint8_t        sid;
360
    // uint8_t        source;
361
0
    uint64_t usecs;       // time in us
362
363
0
    print_data(session->context, bu, len, pgn);
364
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
365
0
             "pgn %6d(%3d):\n", pgn->pgn, session->driver.nmea2000.unit);
366
367
    // sid        = bu[0];
368
    // source     = bu[1] & 0x0f;
369
370
0
    usecs = getleu32(bu, 4) * (uint64_t)100;
371
0
    USTOTS(&session->newdata.time, usecs);
372
0
    session->newdata.time.tv_sec += (time_t)(getleu16(bu, 2) * 24 * 60 * 60);
373
374
0
    return TIME_SET | get_mode(session);
375
0
}
376
377
378
static const int mode_tab[] = {MODE_NO_FIX, MODE_2D,  MODE_3D, MODE_NO_FIX,
379
                               MODE_NO_FIX, MODE_NO_FIX, MODE_NO_FIX,
380
                               MODE_NO_FIX};
381
382
/*
383
 *   PGN 129539: GNSS DOPs
384
 */
385
static gps_mask_t hnd_129539(unsigned char *bu, int len, PGN *pgn,
386
                             struct gps_device_t *session)
387
0
{
388
0
    gps_mask_t mask;
389
0
    unsigned int req_mode;
390
0
    unsigned int act_mode;
391
392
0
    print_data(session->context, bu, len, pgn);
393
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
394
0
             "pgn %6d(%3d):\n", pgn->pgn, session->driver.nmea2000.unit);
395
396
0
    mask                             = 0;
397
0
    session->driver.nmea2000.sid[1]  = bu[0];
398
399
0
    session->driver.nmea2000.mode_valid |= 1;
400
401
0
    req_mode = (unsigned int)((bu[1] >> 0) & 0x07);
402
0
    act_mode = (unsigned int)((bu[1] >> 3) & 0x07);
403
404
    /* This is a workaround for some GARMIN plotter,
405
     * actual mode auto makes no sense for me! */
406
0
    if ((act_mode == 3) && (req_mode != 3)) {
407
0
        act_mode = req_mode;
408
0
    }
409
410
0
    session->driver.nmea2000.mode    = mode_tab[act_mode];
411
412
0
    session->gpsdata.dop.hdop        = getleu16(bu, 2) * 1e-2;
413
0
    session->gpsdata.dop.vdop        = getleu16(bu, 4) * 1e-2;
414
0
    session->gpsdata.dop.tdop        = getleu16(bu, 6) * 1e-2;
415
0
    mask                            |= DOP_SET;
416
417
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
418
0
             "pgn %6d(%3d): sid:%02x hdop:%5.2f vdop:%5.2f tdop:%5.2f\n",
419
0
             pgn->pgn,
420
0
             session->driver.nmea2000.unit,
421
0
             session->driver.nmea2000.sid[1],
422
0
             session->gpsdata.dop.hdop,
423
0
             session->gpsdata.dop.vdop,
424
0
             session->gpsdata.dop.tdop);
425
426
0
    return mask | get_mode(session);
427
0
}
428
429
430
/*
431
 *   PGN 129540: GNSS Satellites in View
432
 */
433
static gps_mask_t hnd_129540(unsigned char *bu, int len, PGN *pgn,
434
                             struct gps_device_t *session)
435
0
{
436
0
    int         l1;
437
0
    int         expected_len;
438
439
0
    print_data(session->context, bu, len, pgn);
440
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
441
0
             "pgn %6d(%3d):\n", pgn->pgn, session->driver.nmea2000.unit);
442
443
0
    session->driver.nmea2000.sid[2]           = bu[0];
444
0
    session->gpsdata.satellites_visible       = (int)bu[2];
445
0
    if (MAXCHANNELS <= session->gpsdata.satellites_visible) {
446
        // Handle a CVE for overrunning skyview[]
447
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
448
0
                 "pgn %6d(%3d): Too many sats %d\n",
449
0
                 pgn->pgn, session->driver.nmea2000.unit,
450
0
                 session->gpsdata.satellites_visible);
451
0
        session->gpsdata.satellites_visible = MAXCHANNELS;
452
0
    }
453
0
    expected_len = 3 + (12 * session->gpsdata.satellites_visible);
454
0
    if (len != expected_len) {
455
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
456
0
                 "pgn %6d(%3d): wrong  length %d s/b %d\n",
457
0
                 pgn->pgn, session->driver.nmea2000.unit,
458
0
                 len, expected_len);
459
0
        return 0;
460
0
    }
461
462
0
    memset(session->gpsdata.skyview, '\0', sizeof(session->gpsdata.skyview));
463
0
    for (l1 = 0; l1 < session->gpsdata.satellites_visible; l1++) {
464
0
        int offset = 3 + (12 * l1);
465
0
        double elev  = getles16(bu, offset + 1) * 1e-4 * RAD_2_DEG;
466
0
        double azi   = getleu16(bu, offset + 3) * 1e-4 * RAD_2_DEG;
467
0
        double snr   = getles16(bu, offset + 5) * 1e-2;
468
469
0
        int svt   = (int)(bu[offset + 11] & 0x0f);
470
471
0
        session->gpsdata.skyview[l1].elevation  = elev;
472
0
        session->gpsdata.skyview[l1].azimuth    = azi;
473
0
        session->gpsdata.skyview[l1].ss         = snr;
474
0
        session->gpsdata.skyview[l1].PRN        = (int16_t)bu[offset];
475
0
        session->gpsdata.skyview[l1].used = false;
476
0
        if ((2 == svt) ||
477
0
            (5 == svt)) {
478
0
            session->gpsdata.skyview[l1].used = true;
479
0
        }
480
0
    }
481
0
    session->driver.nmea2000.mode_valid |= 2;
482
0
    return  SATELLITE_SET | USED_IS;
483
0
}
484
485
486
/*
487
 * PGN: 129029 / 00374005 / 1F805 - 51 - GNSS Position Data
488
 *
489
 *      The last 3 fields repeat until the data is exhausted.
490
 *
491
 *   Field #1: SID
492
 *                   Bits: 8
493
 *                   Signed: false
494
 *   Field #2: Date - Days since January 1, 1970
495
 *                   Bits: 16
496
 *                   Units: days
497
 *                   Type: Date
498
 *                   Resolution: 1
499
 *                   Signed: false
500
 *   Field #3: Time - Seconds since midnight
501
 *                   Bits: 32
502
 *                   Units: s
503
 *                   Type: Time
504
 *                   Resolution: 0.0001
505
 *                   Signed: false
506
 *   Field #4: Latitude
507
 *                   Bits: 64
508
 *                   Units: deg
509
 *                   Type: Latitude
510
 *                   Resolution: 0.0000000000000001
511
 *                   Signed: true
512
 *   Field #5: Longitude
513
 *                   Bits: 64
514
 *                   Units: deg
515
 *                   Type: Longitude
516
 *                   Resolution: 0.0000000000000001
517
 *                   Signed: true
518
 *   Field #6: Altitude - Altitude referenced to WGS-84
519
 *                   Bits: 64
520
 *                   Units: m
521
 *                   Resolution: 1e-06
522
 *                   Signed: true
523
 *   Field #7: GNSS type
524
 *                   Bits: 4
525
 *                   Type: Lookup table
526
 *                   Signed: false
527
 *                   Lookup: 0=GPS
528
 *                   Lookup: 1=GLONASS
529
 *                   Lookup: 2=GPS+GLONASS
530
 *                   Lookup: 3=GPS+SBAS/WAAS
531
 *                   Lookup: 4=GPS+SBAS/WAAS+GLONASS
532
 *                   Lookup: 5=Chayka
533
 *                   Lookup: 6=integrated
534
 *                   Lookup: 7=surveyed
535
 *                   Lookup: 8=Galileo
536
 *   Field #8: Method
537
 *                   Bits: 4
538
 *                   Type: Lookup table
539
 *                   Signed: false
540
 *                   Lookup: 0=no GNSS
541
 *                   Lookup: 1=GNSS fix
542
 *                   Lookup: 2=DGNSS fix
543
 *                   Lookup: 3=Precise GNSS
544
 *                   Lookup: 4=RTK Fixed Integer
545
 *                   Lookup: 5=RTK float
546
 *                   Lookup: 6=Estimated (DR) mode
547
 *                   Lookup: 7=Manual Input
548
 *                   Lookup: 8=Simulate mode
549
 *   Field #9: Integrity
550
 *                   Bits: 2
551
 *                   Type: Lookup table
552
 *                   Signed: false
553
 *                   Lookup: 0=No integrity checking
554
 *                   Lookup: 1=Safe
555
 *                   Lookup: 2=Caution
556
 *   Field #10: Reserved - Reserved
557
 *                   Bits: 6
558
 *                   Type: Binary data
559
 *                   Signed: false
560
 *   Field #11: Number of SVs - Number of satellites used in solution
561
 *                   Bits: 8
562
 *                   Signed: false
563
 *   Field #12: HDOP - Horizontal dilution of precision
564
 *                   Bits: 16
565
 *                   Resolution: 0.01
566
 *                   Signed: true
567
 *   Field #13: PDOP - Probable dilution of precision
568
 *                   Bits: 16
569
 *                   Resolution: 0.01
570
 *                   Signed: true
571
 *   Field #14: Geoidal Separation - Geoidal Separation
572
 *                   Bits: 32
573
 *                   Units: m
574
 *                   Resolution: 0.01
575
 *                   Signed: true
576
 *   Field #15: Reference Stations - Number of reference stations
577
 *                   Bits: 8
578
 *                   Signed: false
579
 *   Field #16: Reference Station Type
580
 *                   Bits: 4
581
 *                   Type: Lookup table
582
 *                   Signed: false
583
 *                   Lookup: 0=GPS
584
 *                   Lookup: 1=GLONASS
585
 *                   Lookup: 2=GPS+GLONASS
586
 *                   Lookup: 3=GPS+SBAS/WAAS
587
 *                   Lookup: 4=GPS+SBAS/WAAS+GLONASS
588
 *                   Lookup: 5=Chayka
589
 *                   Lookup: 6=integrated
590
 *                   Lookup: 7=surveyed
591
 *                   Lookup: 8=Galileo
592
 *   Field #17: Reference Station ID
593
 *                   Bits: 12
594
 *                   Units:
595
 *                   Signed: false
596
 *   Field #18: Age of DGNSS Corrections
597
 *                   Bits: 16
598
 *                   Units: s
599
 *                   Resolution: 0.01
600
 *                   Signed: false
601
 *
602
 */
603
static gps_mask_t hnd_129029(unsigned char *bu, int len, PGN *pgn,
604
                             struct gps_device_t *session)
605
0
{
606
0
    gps_mask_t mask;
607
0
    uint64_t usecs;    // time in us
608
609
0
    print_data(session->context, bu, len, pgn);
610
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
611
0
             "pgn %6d(%3d):\n", pgn->pgn, session->driver.nmea2000.unit);
612
613
0
    mask = 0;
614
0
    session->driver.nmea2000.sid[3]  = bu[0];
615
616
    // field 3 is time in 0.1 ms
617
0
    usecs = getleu32(bu, 3) * (uint64_t)100;
618
0
    USTOTS(&session->newdata.time, usecs);
619
    // add in the date from field 2
620
0
    session->newdata.time.tv_sec += (time_t)(getleu16(bu,1) * 24 * 60 * 60);
621
0
    mask |= TIME_SET;
622
623
0
    session->newdata.latitude = getles64(bu, 7) * 1e-16;
624
0
    session->newdata.longitude = getles64(bu, 15) * 1e-16;
625
0
    mask |= LATLON_SET;
626
627
0
    session->newdata.altHAE = getles64(bu, 23) * 1e-6;
628
0
    mask |= ALTITUDE_SET;
629
630
//  printf("mode %x %x\n", (bu[31] >> 4) & 0x0f, bu[31]);
631
0
    switch ((bu[31] >> 4) & 0x0f) {
632
0
    case 0:
633
0
        session->newdata.status = STATUS_UNK;
634
0
        break;
635
0
    case 1:
636
0
        session->newdata.status = STATUS_GPS;
637
0
        break;
638
0
    case 2:
639
0
        session->newdata.status = STATUS_DGPS;
640
0
        break;
641
0
    case 3:
642
0
    case 4:
643
0
    case 5:
644
0
        session->newdata.status = STATUS_GPS;   // Is this correct ?
645
0
        break;
646
0
    default:
647
0
        session->newdata.status = STATUS_UNK;
648
0
        break;
649
0
    }
650
0
    mask |= STATUS_SET;
651
652
0
    session->newdata.geoid_sep = getles32(bu, 38) / 100.0;
653
654
0
    session->gpsdata.satellites_used = (int)bu[33];
655
656
0
    session->gpsdata.dop.hdop = getleu16(bu, 34) * 0.01;
657
0
    session->gpsdata.dop.pdop = getleu16(bu, 36) * 0.01;
658
0
    mask |= DOP_SET;
659
660
0
    return mask | get_mode(session);
661
0
}
662
663
664
/*
665
 *   PGN 129038: AIS  Class A Position Report
666
 */
667
static gps_mask_t hnd_129038(unsigned char *bu, int len, PGN *pgn,
668
                             struct gps_device_t *session)
669
0
{
670
0
    struct ais_t *ais;
671
672
0
    ais =  &session->gpsdata.ais;
673
0
    print_data(session->context, bu, len, pgn);
674
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
675
0
             "pgn %6d(%3d):\n", pgn->pgn, session->driver.nmea2000.unit);
676
677
0
    if (decode_ais_header(session->context, bu, len, ais, 0xffffffffU) != 0) {
678
0
        ais->type1.lon = (int)scale_int(getles32(bu, 5),
679
0
                                        (int64_t)(SHIFT32 *.06L));
680
0
        ais->type1.lat = (int)scale_int(getles32(bu, 9),
681
0
                                        (int64_t)(SHIFT32 *.06L));
682
0
        ais->type1.accuracy  = (bool)         ((bu[13] >> 0) & 0x01);
683
0
        ais->type1.raim      = (bool)         ((bu[13] >> 1) & 0x01);
684
0
        ais->type1.second    = (unsigned int) ((bu[13] >> 2) & 0x3f);
685
0
        ais->type1.course = (unsigned int)ais_direction(
686
0
                                       (unsigned int)getleu16(bu, 14), 10.0);
687
0
        ais->type1.speed = (unsigned int)(getleu16(bu, 16) *
688
0
                                          MPS_TO_KNOTS * 0.01 / 0.1);
689
0
        ais->type1.radio     = (unsigned int) (getleu32(bu, 18) & 0x7ffff);
690
0
        ais->type1.heading =
691
0
            (unsigned int)ais_direction((unsigned int)getleu16(bu, 21), 1.0);
692
0
        ais->type1.turn = ais_turn_rate((int)getles16(bu, 23));
693
0
        ais->type1.status    = (unsigned int) ((bu[25] >> 0) & 0x0f);
694
0
        ais->type1.maneuver  = 0;  // Not transmitted ????
695
0
        decode_ais_channel_info(bu, len, 163, session);
696
697
0
        return(ONLINE_SET | AIS_SET);
698
0
    }
699
0
    return(0);
700
0
}
701
702
703
/*
704
 *   PGN 129039: AIS  Class B Position Report
705
 */
706
static gps_mask_t hnd_129039(unsigned char *bu, int len, PGN *pgn,
707
                             struct gps_device_t *session)
708
0
{
709
0
    struct ais_t *ais;
710
711
0
    ais =  &session->gpsdata.ais;
712
0
    print_data(session->context, bu, len, pgn);
713
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
714
0
             "pgn %6d(%3d):\n", pgn->pgn, session->driver.nmea2000.unit);
715
716
0
    if (decode_ais_header(session->context, bu, len, ais, 0xffffffffU) != 0) {
717
0
        ais->type18.lon = (int)scale_int(getles32(bu, 5),
718
0
                                         (int64_t)(SHIFT32 *.06L));
719
0
        ais->type18.lat = (int)scale_int(getles32(bu, 9),
720
0
                                         (int64_t)(SHIFT32 *.06L));
721
0
        ais->type18.accuracy = (bool)         ((bu[13] >> 0) & 0x01);
722
0
        ais->type18.raim     = (bool)         ((bu[13] >> 1) & 0x01);
723
0
        ais->type18.second   = (unsigned int) ((bu[13] >> 2) & 0x3f);
724
0
        ais->type18.course =
725
0
            (unsigned int)ais_direction((unsigned int) getleu16(bu, 14), 10.0);
726
0
        ais->type18.speed = (unsigned int)(getleu16(bu, 16) *
727
0
                                           MPS_TO_KNOTS * 0.01 / 0.1);
728
0
        ais->type18.radio    = (unsigned int) (getleu32(bu, 18) & 0x7ffff);
729
0
        ais->type18.heading =
730
0
            (unsigned int)ais_direction((unsigned int) getleu16(bu, 21), 1.0);
731
0
        ais->type18.reserved = 0;
732
0
        ais->type18.regional = (unsigned int) ((bu[24] >> 0) & 0x03);
733
0
        ais->type18.cs       = (bool)         ((bu[24] >> 2) & 0x01);
734
0
        ais->type18.display  = (bool)         ((bu[24] >> 3) & 0x01);
735
0
        ais->type18.dsc      = (bool)         ((bu[24] >> 4) & 0x01);
736
0
        ais->type18.band     = (bool)         ((bu[24] >> 5) & 0x01);
737
0
        ais->type18.msg22    = (bool)         ((bu[24] >> 6) & 0x01);
738
0
        ais->type18.assigned = (bool)         ((bu[24] >> 7) & 0x01);
739
0
        decode_ais_channel_info(bu, len, 163, session);
740
741
0
        return(ONLINE_SET | AIS_SET);
742
0
    }
743
0
    return(0);
744
0
}
745
746
747
/*
748
 *   PGN 129040: AIS Class B Extended Position Report
749
 *
750
 *  No test case for this message at the moment
751
 */
752
static gps_mask_t hnd_129040(unsigned char *bu, int len, PGN *pgn,
753
                             struct gps_device_t *session)
754
0
{
755
0
    struct ais_t *ais;
756
757
0
    ais =  &session->gpsdata.ais;
758
0
    print_data(session->context, bu, len, pgn);
759
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
760
0
             "pgn %6d(%3d):\n", pgn->pgn, session->driver.nmea2000.unit);
761
762
0
    if (decode_ais_header(session->context, bu, len, ais, 0xffffffffU) != 0) {
763
0
        uint16_t length, beam, to_bow, to_starboard;
764
0
        int l;
765
766
0
        ais->type19.lon = (int)scale_int(getles32(bu, 5),
767
0
                                         (int64_t)(SHIFT32 *.06L));
768
0
        ais->type19.lat = (int)scale_int(getles32(bu, 9),
769
0
                                         (int64_t)(SHIFT32 *.06L));
770
0
        ais->type19.accuracy     = (bool)         ((bu[13] >> 0) & 0x01);
771
0
        ais->type19.raim         = (bool)         ((bu[13] >> 1) & 0x01);
772
0
        ais->type19.second       = (unsigned int) ((bu[13] >> 2) & 0x3f);
773
0
        ais->type19.course =
774
0
            (unsigned int)ais_direction((unsigned int)getleu16(bu, 14), 10.0);
775
0
        ais->type19.speed =
776
0
            (unsigned int)(getleu16(bu, 16) * MPS_TO_KNOTS * 0.01 / 0.1);
777
0
        ais->type19.reserved     = (unsigned int) ((bu[18] >> 0) & 0xff);
778
0
        ais->type19.regional     = (unsigned int) ((bu[19] >> 0) & 0x0f);
779
0
        ais->type19.shiptype     = (unsigned int) ((bu[20] >> 0) & 0xff);
780
0
        ais->type19.heading =
781
0
           (unsigned int)  ais_direction((unsigned int) getleu16(bu, 21), 1.0);
782
0
        length                   =                 getleu16(bu, 24);
783
0
        beam                     =                 getleu16(bu, 26);
784
0
        to_starboard             =                 getleu16(bu, 28);
785
0
        to_bow                   =                 getleu16(bu, 30);
786
0
        if ((length == 0xffff) || (to_bow       == 0xffff)) {
787
0
            length       = 0;
788
0
            to_bow       = 0;
789
0
        }
790
0
        if ((beam   == 0xffff) || (to_starboard == 0xffff)) {
791
0
            beam         = 0;
792
0
            to_starboard = 0;
793
0
        }
794
0
        ais->type19.to_bow       = (unsigned int) (to_bow/10);
795
0
        ais->type19.to_stern     = (unsigned int) ((length-to_bow)/10);
796
0
        ais->type19.to_port      = (unsigned int) ((beam-to_starboard)/10);
797
0
        ais->type19.to_starboard = (unsigned int) (to_starboard/10);
798
0
        ais->type19.epfd         = (unsigned int) ((bu[23] >> 4) & 0x0f);
799
0
        ais->type19.dte          = (unsigned int) ((bu[52] >> 0) & 0x01);
800
0
        ais->type19.assigned     = (bool)         ((bu[52] >> 1) & 0x01);
801
0
        for (l = 0; l < AIS_SHIPNAME_MAXLEN; l++) {
802
0
            ais->type19.shipname[l] = (char)bu[32+l];
803
0
        }
804
0
        ais->type19.shipname[AIS_SHIPNAME_MAXLEN] = (char) 0;
805
0
        decode_ais_channel_info(bu, len, 422, session);
806
807
0
        return(ONLINE_SET | AIS_SET);
808
0
    }
809
0
    return(0);
810
0
}
811
812
813
/*
814
 *   PGN 129793: AIS UTC and Date Report
815
 */
816
static gps_mask_t hnd_129793(unsigned char *bu, int len, PGN *pgn,
817
                             struct gps_device_t *session)
818
0
{
819
0
    struct ais_t *ais;
820
821
0
    ais =  &session->gpsdata.ais;
822
0
    print_data(session->context, bu, len, pgn);
823
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
824
0
             "pgn %6d(%3d):\n", pgn->pgn, session->driver.nmea2000.unit);
825
826
0
    if (decode_ais_header(session->context, bu, len, ais, 0xffffffffU) != 0) {
827
0
        uint32_t  time;
828
0
        uint32_t  date;
829
0
        time_t    date1;
830
0
        struct tm date2;
831
832
0
        ais->type4.lon = (int)scale_int(getles32(bu, 5),
833
0
                                        (int64_t)(SHIFT32 *.06L));
834
0
        ais->type4.lat = (int)scale_int(getles32(bu, 9),
835
0
                                        (int64_t)(SHIFT32 *.06L));
836
0
        ais->type4.accuracy     = (bool)         ((bu[13] >> 0) & 0x01);
837
0
        ais->type4.raim         = (bool)         ((bu[13] >> 1) & 0x01);
838
839
0
        time = getleu32(bu, 14);
840
0
        if (time != 0xffffffff) {
841
0
            time                = time / 10000;
842
0
            ais->type4.second   = time % 60; time = time / 60;
843
0
            ais->type4.minute   = time % 60; time = time / 60;
844
0
            ais->type4.hour     = time % 24;
845
0
        } else {
846
0
            ais->type4.second   = AIS_SECOND_NOT_AVAILABLE;
847
0
            ais->type4.minute   = AIS_MINUTE_NOT_AVAILABLE;
848
0
            ais->type4.hour     = AIS_HOUR_NOT_AVAILABLE;
849
0
        }
850
851
0
        ais->type4.radio        = (unsigned int) (getleu32(bu, 18) & 0x7ffff);
852
853
0
        date = getleu16(bu, 21);
854
0
        if (date != 0xffff) {
855
0
            date1 = (time_t)date * (24L *60L *60L);
856
0
            (void) gmtime_r(&date1, &date2);
857
0
            ais->type4.year     = (unsigned int) (date2.tm_year+1900);
858
0
            ais->type4.month    = (unsigned int) (date2.tm_mon+1);
859
0
            ais->type4.day      = (unsigned int) (date2.tm_mday);
860
0
        } else {
861
0
            ais->type4.day      = AIS_DAY_NOT_AVAILABLE;
862
0
            ais->type4.month    = AIS_MONTH_NOT_AVAILABLE;
863
0
            ais->type4.year     = AIS_YEAR_NOT_AVAILABLE;
864
0
        }
865
866
0
        ais->type4.epfd         = (unsigned int) ((bu[23] >> 4) & 0x0f);
867
868
0
        decode_ais_channel_info(bu, len, 163, session);
869
870
0
        return(ONLINE_SET | AIS_SET);
871
0
    }
872
0
    return(0);
873
0
}
874
875
876
/*
877
 *   PGN 129794: AIS Class A Static and Voyage Related Data
878
 */
879
static gps_mask_t hnd_129794(unsigned char *bu, int len, PGN *pgn,
880
                             struct gps_device_t *session)
881
0
{
882
0
    struct ais_t *ais;
883
884
0
    ais =  &session->gpsdata.ais;
885
0
    print_data(session->context, bu, len, pgn);
886
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
887
0
             "pgn %6d(%3d):\n", pgn->pgn, session->driver.nmea2000.unit);
888
889
0
    if (decode_ais_header(session->context, bu, len, ais, 0xffffffffU) != 0) {
890
0
        uint16_t  length, beam, to_bow, to_starboard, date;
891
0
        int       l;
892
0
        uint32_t  time;
893
0
        time_t    date1;
894
0
        struct tm date2;
895
0
        int       cpy_stop;
896
897
0
        ais->type5.ais_version   = (unsigned int) ((bu[73] >> 0) & 0x03);
898
0
        ais->type5.imo           = (unsigned int)  getleu32(bu,  5);
899
0
        if (ais->type5.imo == 0xffffffffU) {
900
0
            ais->type5.imo       = 0;
901
0
        }
902
0
        ais->type5.shiptype      = (unsigned int) ((bu[36] >> 0) & 0xff);
903
0
        length                   =                 getleu16(bu, 37);
904
0
        beam                     =                 getleu16(bu, 39);
905
0
        to_starboard             =                 getleu16(bu, 41);
906
0
        to_bow                   =                 getleu16(bu, 43);
907
0
        if ((length == 0xffff) || (to_bow       == 0xffff)) {
908
0
            length       = 0;
909
0
            to_bow       = 0;
910
0
        }
911
0
        if ((beam   == 0xffff) || (to_starboard == 0xffff)) {
912
0
            beam         = 0;
913
0
            to_starboard = 0;
914
0
        }
915
0
        ais->type5.to_bow        = (unsigned int) (to_bow/10);
916
0
        ais->type5.to_stern      = (unsigned int) ((length-to_bow)/10);
917
0
        ais->type5.to_port       = (unsigned int) ((beam-to_starboard)/10);
918
0
        ais->type5.to_starboard  = (unsigned int) (to_starboard/10);
919
0
        ais->type5.epfd          = (unsigned int) ((bu[73] >> 2) & 0x0f);
920
0
        date                     =                 getleu16(bu, 45);
921
0
        time                     =                 getleu32(bu, 47);
922
0
        date1                    = (time_t)       (date*24*60*60);
923
0
        (void) gmtime_r(&date1, &date2);
924
0
        ais->type5.month         = (unsigned int) (date2.tm_mon+1);
925
0
        ais->type5.day           = (unsigned int) (date2.tm_mday);
926
0
        ais->type5.minute        = (unsigned int) (time/(10000*60));
927
0
        ais->type5.hour          = (unsigned int) (ais->type5.minute/60);
928
0
        ais->type5.minute =
929
0
            (unsigned int)(ais->type5.minute-(ais->type5.hour * 60));
930
931
0
        ais->type5.draught       = (unsigned int) (getleu16(bu, 51)/10);
932
0
        ais->type5.dte           = (unsigned int) ((bu[73] >> 6) & 0x01);
933
934
0
        for (l = 0, cpy_stop = 0; l < 7; l++) {
935
0
            char next;
936
937
0
            next = (char) bu[9+l];
938
0
            if ((next < ' ') || (next > 0x7e)) {
939
0
                cpy_stop = 1;
940
0
            }
941
0
            if (cpy_stop == 0) {
942
0
                ais->type5.callsign[l] = next;
943
0
            } else {
944
0
                ais->type5.callsign[l] = 0;
945
0
            }
946
0
        }
947
0
        ais->type5.callsign[7]   = (char) 0;
948
949
0
        for (l = 0, cpy_stop = 0; l < AIS_SHIPNAME_MAXLEN; l++) {
950
0
            char next;
951
952
0
            next = (char) bu[16+l];
953
0
            if ((next < ' ') || (next > 0x7e)) {
954
0
                cpy_stop = 1;
955
0
            }
956
0
            if (cpy_stop == 0) {
957
0
                ais->type5.shipname[l] = next;
958
0
            } else {
959
0
                ais->type5.shipname[l] = 0;
960
0
            }
961
0
        }
962
0
        ais->type5.shipname[AIS_SHIPNAME_MAXLEN] = (char) 0;
963
964
0
        for (l = 0, cpy_stop = 0; l < 20; l++) {
965
0
            char next;
966
967
0
            next = (char) bu[53+l];
968
0
            if ((next < ' ') || (next > 0x7e)) {
969
0
                cpy_stop = 1;
970
0
            }
971
0
            if (cpy_stop == 0) {
972
0
                ais->type5.destination[l] = next;
973
0
            } else {
974
0
                ais->type5.destination[l] = 0;
975
0
            }
976
0
        }
977
0
        ais->type5.destination[20] = (char) 0;
978
#if NMEA2000_DEBUG_AIS
979
        printf("AIS: MMSI:  %09u\n",
980
               ais->mmsi);
981
        printf("AIS: name:  %-20.20s i:%8u c:%-8.8s b:%6u s:%6u p:%6u"
982
               "s:%6u dr:%4.1f\n",
983
               ais->type5.shipname,
984
               ais->type5.imo,
985
               ais->type5.callsign,
986
               ais->type5.to_bow,
987
               ais->type5.to_stern,
988
               ais->type5.to_port,
989
               ais->type5.to_starboard,
990
               ais->type5.draught/10.0);
991
        printf("AIS: arrival:%-20.20s at %02u-%02u-%04d %02u:%0u\n",
992
               ais->type5.destination,
993
               ais->type5.day,
994
               ais->type5.month,
995
               date2.tm_year+1900,
996
               ais->type5.hour,
997
               ais->type5.minute);
998
#endif  // end of #if NMEA2000_DEBUG_AIS
999
0
        decode_ais_channel_info(bu, len, 592, session);
1000
0
        return(ONLINE_SET | AIS_SET);
1001
0
    }
1002
0
    return(0);
1003
0
}
1004
1005
1006
/*
1007
 *   PGN 129798: AIS SAR Aircraft Position Report
1008
 *
1009
 * No test case for this message at the moment
1010
 */
1011
static gps_mask_t hnd_129798(unsigned char *bu, int len, PGN *pgn,
1012
                             struct gps_device_t *session)
1013
0
{
1014
0
    struct ais_t *ais;
1015
1016
0
    ais =  &session->gpsdata.ais;
1017
0
    print_data(session->context, bu, len, pgn);
1018
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
1019
0
             "pgn %6d(%3d):\n", pgn->pgn, session->driver.nmea2000.unit);
1020
1021
0
    if (decode_ais_header(session->context, bu, len, ais, 0xffffffffU) != 0) {
1022
0
        ais->type9.lon = (int)scale_int(getles32(bu, 5),
1023
0
                                        (int64_t)(SHIFT32 *.06L));
1024
0
        ais->type9.lat = (int)scale_int(getles32(bu, 9),
1025
0
                                        (int64_t)(SHIFT32 *.06L));
1026
0
        ais->type9.accuracy  = (bool)         ((bu[13] >> 0) & 0x01);
1027
0
        ais->type9.raim      = (bool)         ((bu[13] >> 1) & 0x01);
1028
0
        ais->type9.second    = (unsigned int) ((bu[13] >> 2) & 0x3f);
1029
0
        ais->type9.course =
1030
0
            (unsigned int)ais_direction((unsigned int)getleu16(bu, 14), 10.0);
1031
0
        ais->type9.speed =
1032
0
            (unsigned int)(getleu16(bu, 16) * MPS_TO_KNOTS * 0.01 / 0.1);
1033
0
        ais->type9.radio     = (unsigned int) (getleu32(bu, 18) & 0x7ffff);
1034
0
        ais->type9.alt       = (unsigned int) (getleu64(bu, 21)/1000000);
1035
0
        ais->type9.regional  = (unsigned int) ((bu[29] >> 0) & 0xff);
1036
0
        ais->type9.dte       = (unsigned int) ((bu[30] >> 0) & 0x01);
1037
//      ais->type9.spare     = (bu[30] >> 1) & 0x7f;
1038
0
        ais->type9.assigned  = 0;  // Not transmitted ????
1039
0
        decode_ais_channel_info(bu, len, 163, session);
1040
1041
0
        return(ONLINE_SET | AIS_SET);
1042
0
    }
1043
0
    return(0);
1044
0
}
1045
1046
1047
/*
1048
 *   PGN 129802: AIS Safety Related Broadcast Message
1049
 *
1050
 * No test case for this message at the moment
1051
 */
1052
static gps_mask_t hnd_129802(unsigned char *bu, int len, PGN *pgn,
1053
                             struct gps_device_t *session)
1054
0
{
1055
0
    struct ais_t *ais;
1056
1057
0
    ais =  &session->gpsdata.ais;
1058
0
    print_data(session->context, bu, len, pgn);
1059
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
1060
0
             "pgn %6d(%3d):\n", pgn->pgn, session->driver.nmea2000.unit);
1061
1062
0
    if (decode_ais_header(session->context, bu, len, ais, 0x3fffffff) != 0) {
1063
0
        int                   l;
1064
1065
//      ais->type14.channel = (bu[ 5] >> 0) & 0x1f;
1066
0
        for (l = 0; l < 36; l++) {
1067
0
            ais->type14.text[l] = (char) bu[6+l];
1068
0
        }
1069
0
        ais->type14.text[36] = (char) 0;
1070
0
        decode_ais_channel_info(bu, len, 40, session);
1071
1072
0
        return(ONLINE_SET | AIS_SET);
1073
0
    }
1074
0
    return(0);
1075
0
}
1076
1077
1078
/*
1079
 *   PGN 129809: AIS Class B CS Static Data Report, Part A
1080
 */
1081
static gps_mask_t hnd_129809(unsigned char *bu, int len, PGN *pgn,
1082
                             struct gps_device_t *session)
1083
0
{
1084
0
    struct ais_t *ais;
1085
1086
0
    ais =  &session->gpsdata.ais;
1087
0
    print_data(session->context, bu, len, pgn);
1088
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
1089
0
             "pgn %6d(%3d):\n", pgn->pgn, session->driver.nmea2000.unit);
1090
1091
0
    if (decode_ais_header(session->context, bu, len, ais, 0xffffffffU) != 0) {
1092
0
        int l;
1093
0
        int index   = session->driver.aivdm.context[0].type24_queue.index;
1094
0
        struct ais_type24a_t *saveptr =
1095
0
            &session->driver.aivdm.context[0].type24_queue.ships[index];
1096
1097
0
        GPSD_LOG(LOG_PROG, &session->context->errout,
1098
0
                 "NMEA2000: AIS message 24A from %09u stashed.\n",
1099
0
                 ais->mmsi);
1100
1101
0
        for (l = 0; l < AIS_SHIPNAME_MAXLEN; l++) {
1102
0
            ais->type24.shipname[l] = (char) bu[ 5+l];
1103
0
            saveptr->shipname[l] = (char) bu[ 5+l];
1104
0
        }
1105
0
        ais->type24.shipname[AIS_SHIPNAME_MAXLEN] = (char) 0;
1106
0
        saveptr->shipname[AIS_SHIPNAME_MAXLEN] = (char) 0;
1107
1108
0
        saveptr->mmsi = ais->mmsi;
1109
1110
0
        index += 1;
1111
0
        index %= MAX_TYPE24_INTERLEAVE;
1112
0
        session->driver.aivdm.context[0].type24_queue.index = index;
1113
1114
0
        decode_ais_channel_info(bu, len, 200, session);
1115
1116
0
        ais->type24.part = part_a;
1117
0
        return(ONLINE_SET | AIS_SET);
1118
0
    }
1119
0
    return(0);
1120
0
}
1121
1122
1123
/*
1124
 *   PGN 129810: AIS Class B CS Static Data Report, Part B
1125
 */
1126
static gps_mask_t hnd_129810(unsigned char *bu, int len, PGN *pgn,
1127
                             struct gps_device_t *session)
1128
0
{
1129
0
    struct ais_t *ais;
1130
1131
0
    ais =  &session->gpsdata.ais;
1132
0
    print_data(session->context, bu, len, pgn);
1133
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
1134
0
             "pgn %6d(%3d):\n", pgn->pgn, session->driver.nmea2000.unit);
1135
1136
0
    if (decode_ais_header(session->context, bu, len, ais, 0xffffffffU) != 0) {
1137
0
        int l, i;
1138
1139
0
        ais->type24.shiptype = (unsigned int) ((bu[ 5] >> 0) & 0xff);
1140
1141
0
        for (l = 0; l < 7; l++) {
1142
0
            ais->type24.vendorid[l] = (char) bu[ 6+l];
1143
0
        }
1144
0
        ais->type24.vendorid[7] = (char) 0;
1145
1146
0
        for (l = 0; l < 7; l++) {
1147
0
            ais->type24.callsign[l] = (char) bu[13+l];
1148
0
        }
1149
0
        ais->type24.callsign[7] = (char )0;
1150
1151
0
        ais->type24.model = 0;
1152
0
        ais->type24.serial = 0;
1153
1154
0
        if (AIS_AUXILIARY_MMSI(ais->mmsi)) {
1155
0
            ais->type24.mothership_mmsi   = (unsigned int) (getleu32(bu, 28));
1156
0
        } else {
1157
0
            uint16_t length, beam, to_bow, to_starboard;
1158
1159
0
            length                        =                 getleu16(bu, 20);
1160
0
            beam                          =                 getleu16(bu, 22);
1161
0
            to_starboard                  =                 getleu16(bu, 24);
1162
0
            to_bow                        =                 getleu16(bu, 26);
1163
0
            if ((length == 0xffff) || (to_bow       == 0xffff)) {
1164
0
                length       = 0;
1165
0
                to_bow       = 0;
1166
0
            }
1167
0
            if ((beam   == 0xffff) || (to_starboard == 0xffff)) {
1168
0
                beam         = 0;
1169
0
                to_starboard = 0;
1170
0
            }
1171
0
            ais->type24.dim.to_bow   = (unsigned int) (to_bow/10);
1172
0
            ais->type24.dim.to_stern = (unsigned int) ((length-to_bow)/10);
1173
0
            ais->type24.dim.to_port  = (unsigned int) ((beam-to_starboard)/10);
1174
0
            ais->type24.dim.to_starboard  = (unsigned int) (to_starboard/10);
1175
0
        }
1176
1177
0
        for (i = 0; i < MAX_TYPE24_INTERLEAVE; i++) {
1178
0
            if (session->driver.aivdm.context[0].type24_queue.ships[i].mmsi ==
1179
0
                ais->mmsi) {
1180
0
                for (l = 0; l < AIS_SHIPNAME_MAXLEN; l++) {
1181
0
                    ais->type24.shipname[l] =
1182
0
  (char)(session->driver.aivdm.context[0].type24_queue.ships[i].shipname[l]);
1183
0
                }
1184
0
                ais->type24.shipname[AIS_SHIPNAME_MAXLEN] = (char) 0;
1185
1186
0
                GPSD_LOG(LOG_PROG, &session->context->errout,
1187
0
                         "NMEA2000: AIS 24B from %09u matches a 24A.\n",
1188
0
                            ais->mmsi);
1189
                /* prevent false match if a 24B is repeated */
1190
0
                session->driver.aivdm.context[0].type24_queue.ships[i].mmsi = 0;
1191
#if NMEA2000_DEBUG_AIS
1192
                printf("AIS: MMSI:  %09u\n", ais->mmsi);
1193
                printf("AIS: name:  %-20.20s v:%-8.8s c:%-8.8s b:%6u "
1194
                       "s:%6u p:%6u s:%6u\n",
1195
                       ais->type24.shipname,
1196
                       ais->type24.vendorid,
1197
                       ais->type24.callsign,
1198
                       ais->type24.dim.to_bow,
1199
                       ais->type24.dim.to_stern,
1200
                       ais->type24.dim.to_port,
1201
                       ais->type24.dim.to_starboard);
1202
#endif /* of #if NMEA2000_DEBUG_AIS */
1203
1204
0
                decode_ais_channel_info(bu, len, 264, session);
1205
0
                ais->type24.part = both;
1206
0
                return(ONLINE_SET | AIS_SET);
1207
0
            }
1208
0
        }
1209
#if NMEA2000_DEBUG_AIS
1210
        printf("AIS: MMSI  :  %09u\n", ais->mmsi);
1211
        printf("AIS: vendor:  %-8.8s c:%-8.8s b:%6u s:%6u p:%6u s:%6u\n",
1212
               ais->type24.vendorid,
1213
               ais->type24.callsign,
1214
               ais->type24.dim.to_bow,
1215
               ais->type24.dim.to_stern,
1216
               ais->type24.dim.to_port,
1217
               ais->type24.dim.to_starboard);
1218
#endif /* of #if NMEA2000_DEBUG_AIS */
1219
0
        decode_ais_channel_info(bu, len, 264, session);
1220
0
        ais->type24.part = part_b;
1221
0
        return(ONLINE_SET | AIS_SET);
1222
0
    }
1223
0
    return(0);
1224
0
}
1225
1226
1227
/*
1228
 *   PGN 127506: PWR DC Detailed Status
1229
 */
1230
static gps_mask_t hnd_127506(unsigned char *bu, int len, PGN *pgn,
1231
                             struct gps_device_t *session)
1232
0
{
1233
0
    print_data(session->context, bu, len, pgn);
1234
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
1235
0
             "pgn %6d(%3d):\n", pgn->pgn, session->driver.nmea2000.unit);
1236
0
    return(0);
1237
0
}
1238
1239
1240
/*
1241
 *   PGN 127508: PWR Battery Status
1242
 */
1243
static gps_mask_t hnd_127508(unsigned char *bu, int len, PGN *pgn,
1244
                             struct gps_device_t *session)
1245
0
{
1246
0
    print_data(session->context, bu, len, pgn);
1247
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
1248
0
             "pgn %6d(%3d):\n", pgn->pgn, session->driver.nmea2000.unit);
1249
0
    return(0);
1250
0
}
1251
1252
1253
/*
1254
 *   PGN 127513: PWR Battery Configuration Status
1255
 */
1256
static gps_mask_t hnd_127513(unsigned char *bu, int len, PGN *pgn,
1257
                             struct gps_device_t *session)
1258
0
{
1259
0
    print_data(session->context, bu, len, pgn);
1260
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
1261
0
             "pgn %6d(%3d):\n", pgn->pgn, session->driver.nmea2000.unit);
1262
0
    return(0);
1263
0
}
1264
1265
1266
/*
1267
 *   PGN 127245: NAV Rudder
1268
 */
1269
static gps_mask_t hnd_127245(unsigned char *bu, int len, PGN *pgn,
1270
                             struct gps_device_t *session)
1271
0
{
1272
0
    print_data(session->context, bu, len, pgn);
1273
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
1274
0
             "pgn %6d(%3d):\n", pgn->pgn, session->driver.nmea2000.unit);
1275
0
    return(0);
1276
0
}
1277
1278
1279
/*
1280
 *   PGN 127250: NAV Vessel Heading
1281
 */
1282
static gps_mask_t hnd_127250(unsigned char *bu, int len,
1283
                             PGN *pgn, struct gps_device_t *session)
1284
0
{
1285
0
    int aux;
1286
1287
0
    print_data(session->context, bu, len, pgn);
1288
1289
0
    session->gpsdata.attitude.heading = getleu16(bu, 1) * RAD_2_DEG * 0.0001;
1290
//  printf("ATT 0:%8.3f\n",session->gpsdata.attitude.heading);
1291
0
    aux = getles16(bu, 3);
1292
0
    if (aux != 0x07fff) {
1293
0
        session->gpsdata.attitude.heading += aux * RAD_2_DEG * 0.0001;
1294
0
    }
1295
//  printf("ATT 1:%8.3f %6x\n",session->gpsdata.attitude.heading, aux);
1296
0
    aux = getles16(bu, 5);
1297
0
    if (aux != 0x07fff) {
1298
0
        session->gpsdata.attitude.heading += aux * RAD_2_DEG * 0.0001;
1299
0
    }
1300
//  printf("ATT 2:%8.3f %6x\n",session->gpsdata.attitude.heading, aux);
1301
1302
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
1303
0
             "pgn %6d(%3d):\n", pgn->pgn, session->driver.nmea2000.unit);
1304
0
    return(ONLINE_SET | ATTITUDE_SET);
1305
0
}
1306
1307
1308
/*
1309
 *   PGN 128259: NAV Speed
1310
 */
1311
static gps_mask_t hnd_128259(unsigned char *bu, int len, PGN *pgn,
1312
                             struct gps_device_t *session)
1313
0
{
1314
0
    print_data(session->context, bu, len, pgn);
1315
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
1316
0
             "pgn %6d(%3d):\n", pgn->pgn, session->driver.nmea2000.unit);
1317
0
    return(0);
1318
0
}
1319
1320
1321
/*
1322
 *   PGN 128267: NAV Water Depth
1323
 */
1324
static gps_mask_t hnd_128267(unsigned char *bu, int len, PGN *pgn,
1325
                             struct gps_device_t *session)
1326
0
{
1327
0
    print_data(session->context, bu, len, pgn);
1328
1329
0
    session->gpsdata.attitude.depth = getleu32(bu, 1) *.01;
1330
1331
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
1332
0
             "pgn %6d(%3d):\n", pgn->pgn, session->driver.nmea2000.unit);
1333
0
    return(ONLINE_SET | ATTITUDE_SET);
1334
0
}
1335
1336
1337
/*
1338
 *   PGN 128275: NAV Distance Log
1339
 */
1340
static gps_mask_t hnd_128275(unsigned char *bu, int len, PGN *pgn,
1341
                             struct gps_device_t *session)
1342
0
{
1343
0
    print_data(session->context, bu, len, pgn);
1344
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
1345
0
             "pgn %6d(%3d):\n", pgn->pgn, session->driver.nmea2000.unit);
1346
0
    return(0);
1347
0
}
1348
1349
1350
/*
1351
 *   PGN 129283: NAV Cross Track Error
1352
 */
1353
static gps_mask_t hnd_129283(unsigned char *bu, int len, PGN *pgn,
1354
                             struct gps_device_t *session)
1355
0
{
1356
0
    print_data(session->context, bu, len, pgn);
1357
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
1358
0
             "pgn %6d(%3d):\n", pgn->pgn, session->driver.nmea2000.unit);
1359
0
    return(0);
1360
0
}
1361
1362
1363
/*
1364
 *   PGN 129284: NAV Navigation Data
1365
 */
1366
static gps_mask_t hnd_129284(unsigned char *bu, int len, PGN *pgn,
1367
                             struct gps_device_t *session)
1368
0
{
1369
0
    print_data(session->context, bu, len, pgn);
1370
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
1371
0
             "pgn %6d(%3d):\n", pgn->pgn, session->driver.nmea2000.unit);
1372
0
    return(0);
1373
0
}
1374
1375
1376
/*
1377
 *   PGN 129285: NAV Navigation - Route/WP Information
1378
 */
1379
static gps_mask_t hnd_129285(unsigned char *bu, int len, PGN *pgn,
1380
                             struct gps_device_t *session)
1381
0
{
1382
0
    print_data(session->context, bu, len, pgn);
1383
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
1384
0
             "pgn %6d(%3d):\n", pgn->pgn, session->driver.nmea2000.unit);
1385
0
    return(0);
1386
0
}
1387
1388
1389
/*
1390
 *   PGN 130306: NAV Wind Data
1391
 */
1392
static gps_mask_t hnd_130306(unsigned char *bu, int len, PGN *pgn,
1393
                             struct gps_device_t *session)
1394
0
{
1395
0
    print_data(session->context, bu, len, pgn);
1396
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
1397
0
             "pgn %6d(%3d):\n", pgn->pgn, session->driver.nmea2000.unit);
1398
0
    return(0);
1399
0
}
1400
1401
1402
/*
1403
 *   PGN 130310: NAV Water Temp., Outside Air Temp., Atmospheric Pressure
1404
 */
1405
static gps_mask_t hnd_130310(unsigned char *bu, int len, PGN *pgn,
1406
                             struct gps_device_t *session)
1407
0
{
1408
0
    print_data(session->context, bu, len, pgn);
1409
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
1410
0
             "pgn %6d(%3d):\n", pgn->pgn, session->driver.nmea2000.unit);
1411
0
    return(0);
1412
0
}
1413
1414
1415
/*
1416
 *   PGN 130311: NAV Environmental Parameters
1417
 */
1418
static gps_mask_t hnd_130311(unsigned char *bu, int len, PGN *pgn,
1419
                             struct gps_device_t *session)
1420
0
{
1421
0
    print_data(session->context, bu, len, pgn);
1422
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
1423
0
             "pgn %6d(%3d):\n", pgn->pgn, session->driver.nmea2000.unit);
1424
0
    return(0);
1425
0
}
1426
1427
1428
static const char msg_059392[] = {"ISO  Acknowledgment"};
1429
static const char msg_060928[] = {"ISO  Address Claim"};
1430
static const char msg_126208[] = {"NMEA Command/Request/Acknowledge"};
1431
static const char msg_126464[] = {"ISO  Transmit/Receive PGN List"};
1432
static const char msg_126992[] = {"GNSS System Time"};
1433
static const char msg_126996[] = {"ISO  Product Information"};
1434
1435
static const char msg_127506[] = {"PWR DC Detailed Status"};
1436
static const char msg_127508[] = {"PWR Battery Status"};
1437
static const char msg_127513[] = {"PWR Battery Configuration Status"};
1438
1439
static const char msg_127258[] = {"GNSS Magnetic Variation"};
1440
static const char msg_129025[] = {"GNSS Position Rapid Update"};
1441
static const char msg_129026[] = {"GNSS COG and SOG Rapid Update"};
1442
static const char msg_129029[] = {"GNSS Positition Data"};
1443
static const char msg_129539[] = {"GNSS DOPs"};
1444
static const char msg_129540[] = {"GNSS Satellites in View"};
1445
1446
static const char msg_129038[] = {"AIS  Class A Position Report"};
1447
static const char msg_129039[] = {"AIS  Class B Position Report"};
1448
static const char msg_129040[] = {"AIS  Class B Extended Position Report"};
1449
static const char msg_129793[] = {"AIS  UTC and Date report"};
1450
static const char msg_129794[] = {"AIS  Class A Static and Voyage Related Data"};
1451
static const char msg_129798[] = {"AIS  SAR Aircraft Position Report"};
1452
static const char msg_129802[] = {"AIS  Safety Related Broadcast Message"};
1453
static const char msg_129809[] = {"AIS  Class B CS Static Data Report, Part A"};
1454
static const char msg_129810[] = {"AIS  Class B CS Static Data Report, Part B"};
1455
1456
static const char msg_127245[] = {"NAV Rudder"};
1457
static const char msg_127250[] = {"NAV Vessel Heading"};
1458
static const char msg_128259[] = {"NAV Speed"};
1459
static const char msg_128267[] = {"NAV Water Depth"};
1460
static const char msg_128275[] = {"NAV Distance Log"};
1461
1462
static const char msg_129283[] = {"NAV Cross Track Error"};
1463
static const char msg_129284[] = {"NAV Navigation Data"};
1464
static const char msg_129285[] = {"NAV Navigation - Route/WP Information"};
1465
1466
static const char msg_130306[] = {"NAV Wind Data"};
1467
static const char msg_130310[] = {"NAV Water Temp., Outside Air Temp.,"
1468
                                  "Atmospheric Pressure"};
1469
static const char msg_130311[] = {"NAV Environmental Parameters"};
1470
1471
static const char msg_error [] = {"**error**"};
1472
1473
static PGN gpspgn[] = {{ 59392, 0, 0, hnd_059392, &msg_059392[0]},
1474
                       { 60928, 0, 0, hnd_060928, &msg_060928[0]},
1475
                       {126208, 0, 0, hnd_126208, &msg_126208[0]},
1476
                       {126464, 1, 0, hnd_126464, &msg_126464[0]},
1477
                       {126992, 0, 0, hnd_126992, &msg_126992[0]},
1478
                       {126996, 1, 0, hnd_126996, &msg_126996[0]},
1479
                       {127258, 0, 0, hnd_127258, &msg_127258[0]},
1480
                       {129025, 0, 1, hnd_129025, &msg_129025[0]},
1481
                       {129026, 0, 1, hnd_129026, &msg_129026[0]},
1482
                       {129029, 1, 1, hnd_129029, &msg_129029[0]},
1483
                       {129283, 0, 0, hnd_129283, &msg_129283[0]},
1484
                       {129284, 1, 0, hnd_129284, &msg_129284[0]},
1485
                       {129285, 1, 0, hnd_129285, &msg_129285[0]},
1486
                       {129539, 0, 1, hnd_129539, &msg_129539[0]},
1487
                       {129540, 1, 1, hnd_129540, &msg_129540[0]},
1488
                       {0     , 0, 0, NULL,       &msg_error [0]}};
1489
1490
static PGN aispgn[] = {{ 59392, 0, 0, hnd_059392, &msg_059392[0]},
1491
                       { 60928, 0, 0, hnd_060928, &msg_060928[0]},
1492
                       {126208, 0, 0, hnd_126208, &msg_126208[0]},
1493
                       {126464, 1, 0, hnd_126464, &msg_126464[0]},
1494
                       {126992, 0, 0, hnd_126992, &msg_126992[0]},
1495
                       {126996, 1, 0, hnd_126996, &msg_126996[0]},
1496
                       {129038, 1, 2, hnd_129038, &msg_129038[0]},
1497
                       {129039, 1, 2, hnd_129039, &msg_129039[0]},
1498
                       {129040, 1, 2, hnd_129040, &msg_129040[0]},
1499
                       {129793, 1, 2, hnd_129793, &msg_129793[0]},
1500
                       {129794, 1, 2, hnd_129794, &msg_129794[0]},
1501
                       {129798, 1, 2, hnd_129798, &msg_129798[0]},
1502
                       {129802, 1, 2, hnd_129802, &msg_129802[0]},
1503
                       {129809, 1, 2, hnd_129809, &msg_129809[0]},
1504
                       {129810, 1, 2, hnd_129810, &msg_129810[0]},
1505
                       {0     , 0, 0, NULL,       &msg_error [0]}};
1506
1507
static PGN pwrpgn[] = {{ 59392, 0, 0, hnd_059392, &msg_059392[0]},
1508
                       { 60928, 0, 0, hnd_060928, &msg_060928[0]},
1509
                       {126208, 0, 0, hnd_126208, &msg_126208[0]},
1510
                       {126464, 1, 0, hnd_126464, &msg_126464[0]},
1511
                       {126992, 0, 0, hnd_126992, &msg_126992[0]},
1512
                       {126996, 1, 0, hnd_126996, &msg_126996[0]},
1513
                       {127506, 1, 3, hnd_127506, &msg_127506[0]},
1514
                       {127508, 1, 3, hnd_127508, &msg_127508[0]},
1515
                       {127513, 1, 3, hnd_127513, &msg_127513[0]},
1516
                       {0     , 0, 0, NULL,       &msg_error [0]}};
1517
1518
static PGN navpgn[] = {{ 59392, 0, 0, hnd_059392, &msg_059392[0]},
1519
                       { 60928, 0, 0, hnd_060928, &msg_060928[0]},
1520
                       {126208, 0, 0, hnd_126208, &msg_126208[0]},
1521
                       {126464, 1, 0, hnd_126464, &msg_126464[0]},
1522
                       {126992, 0, 0, hnd_126992, &msg_126992[0]},
1523
                       {126996, 1, 0, hnd_126996, &msg_126996[0]},
1524
                       {127245, 0, 4, hnd_127245, &msg_127245[0]},
1525
                       {127250, 0, 4, hnd_127250, &msg_127250[0]},
1526
                       {127258, 0, 0, hnd_127258, &msg_127258[0]},
1527
                       {128259, 0, 4, hnd_128259, &msg_128259[0]},
1528
                       {128267, 0, 4, hnd_128267, &msg_128267[0]},
1529
                       {128275, 1, 4, hnd_128275, &msg_128275[0]},
1530
                       {129283, 0, 0, hnd_129283, &msg_129283[0]},
1531
                       {129284, 1, 0, hnd_129284, &msg_129284[0]},
1532
                       {129285, 1, 0, hnd_129285, &msg_129285[0]},
1533
                       {130306, 0, 4, hnd_130306, &msg_130306[0]},
1534
                       {130310, 0, 4, hnd_130310, &msg_130310[0]},
1535
                       {130311, 0, 4, hnd_130311, &msg_130311[0]},
1536
                       {0     , 0, 0, NULL,       &msg_error [0]}};
1537
1538
1539
1540
static PGN *search_pgnlist(unsigned int pgn, PGN *pgnlist)
1541
0
{
1542
0
    int l1;
1543
0
    PGN *work;
1544
1545
0
    l1 = 0;
1546
0
    work = NULL;
1547
0
    while (pgnlist[l1].pgn != 0) {
1548
0
        if (pgnlist[l1].pgn == pgn) {
1549
0
            work = &pgnlist[l1];
1550
0
            break;
1551
0
        } else {
1552
0
            l1 = l1 + 1;
1553
0
            }
1554
0
        }
1555
0
    return work;
1556
0
}
1557
1558
static void find_pgn(struct can_frame *frame, struct gps_device_t *session)
1559
0
{
1560
0
    unsigned int can_net;
1561
1562
0
    session->driver.nmea2000.workpgn = NULL;
1563
0
    can_net = session->driver.nmea2000.can_net;
1564
0
    if (can_net > (NMEA2000_NETS-1)) {
1565
0
        GPSD_LOG(LOG_ERROR, &session->context->errout,
1566
0
                 "NMEA2000 find_pgn: Invalid can network %d.\n", can_net);
1567
0
        return;
1568
0
    }
1569
1570
0
    if (frame->can_id & 0x80000000) {
1571
0
        unsigned int source_prio;
1572
0
        unsigned int daddr;
1573
0
        unsigned int source_pgn;
1574
0
        unsigned int source_unit;
1575
1576
0
#if LOG_FILE
1577
0
        if (logFile != NULL) {
1578
0
            struct timespec  msgTime;
1579
1580
0
            clock_gettime(CLOCK_REALTIME, &msgTime);
1581
0
            (void)fprintf(logFile,
1582
0
                          "(%010lld.%06ld) can0 %08x#",
1583
0
                          (long long)msgTime.tv_sec,
1584
0
                          msgTime.tv_nsec / 1000,
1585
0
                          frame->can_id & 0x1ffffff);
1586
0
            if ((frame->can_dlc & 0x0f) > 0) {
1587
0
                int l1;
1588
0
                for(l1 = 0; l1 < (frame->can_dlc & 0x0f); l1++) {
1589
0
                    (void)fprintf(logFile, "%02x", frame->data[l1]);
1590
0
                }
1591
0
            }
1592
0
            (void)fprintf(logFile, "\n");
1593
0
        }
1594
0
#endif /* of if LOG_FILE */
1595
0
        session->driver.nmea2000.can_msgcnt += 1;
1596
0
        source_pgn = (frame->can_id >> 8) & 0x1ffff;
1597
0
        source_prio = (frame->can_id >> 26) & 0x7;
1598
0
        source_unit = frame->can_id & 0x0ff;
1599
1600
0
        if (((source_pgn & 0x0ff00) >> 8) < 240) {
1601
0
            daddr  = source_pgn & 0x000ff;
1602
0
            source_pgn  = source_pgn & 0x1ff00;
1603
0
        } else {
1604
0
            daddr = 0xff;
1605
0
        }
1606
0
        GPSD_LOG(LOG_DATA, &session->context->errout,
1607
0
                 "nmea2000: source_prio %u daddr %u",
1608
0
                 source_prio, daddr);
1609
1610
0
        if (!session->driver.nmea2000.unit_valid) {
1611
0
            unsigned int l1, l2;
1612
1613
0
            for (l1 = 0; l1 < NMEA2000_NETS; l1++) {
1614
0
                for (l2 = 0; l2 < NMEA2000_UNITS; l2++) {
1615
0
                    if (session == nmea2000_units[l1][l2]) {
1616
0
                        session->driver.nmea2000.unit = l2;
1617
0
                        session->driver.nmea2000.unit_valid = true;
1618
0
                        session->driver.nmea2000.can_net = l1;
1619
0
                        can_net = l1;
1620
0
                    }
1621
0
                }
1622
0
            }
1623
1624
0
            session->driver.nmea2000.unit = source_unit;
1625
0
            session->driver.nmea2000.unit_valid = true;
1626
0
            nmea2000_units[can_net][source_unit] = session;
1627
0
        }
1628
1629
0
        if (source_unit == session->driver.nmea2000.unit) {
1630
0
            PGN *work;
1631
0
            if (session->driver.nmea2000.pgnlist != NULL) {
1632
0
                work = search_pgnlist(source_pgn,
1633
0
                                      session->driver.nmea2000.pgnlist);
1634
0
            } else {
1635
0
                PGN *pgnlist;
1636
1637
0
                pgnlist = &gpspgn[0];
1638
0
                work = search_pgnlist(source_pgn, pgnlist);
1639
0
                if (work == NULL) {
1640
0
                    pgnlist = &aispgn[0];
1641
0
                    work = search_pgnlist(source_pgn, pgnlist);
1642
0
                }
1643
0
                if (work == NULL) {
1644
0
                    pgnlist = &pwrpgn[0];
1645
0
                    work = search_pgnlist(source_pgn, pgnlist);
1646
0
                }
1647
0
                if (work == NULL) {
1648
0
                    pgnlist = &navpgn[0];
1649
0
                    work = search_pgnlist(source_pgn, pgnlist);
1650
0
                }
1651
0
                if ((work != NULL) && (work->type > 0)) {
1652
0
                    session->driver.nmea2000.pgnlist = pgnlist;
1653
0
                }
1654
0
            }
1655
0
            if (work != NULL) {
1656
0
                if (work->fast == 0) {
1657
0
                    size_t l2;
1658
1659
0
                    GPSD_LOG(LOG_DATA, &session->context->errout,
1660
0
                             "pgn %6d:%s \n", work->pgn, work->name);
1661
0
                    session->driver.nmea2000.workpgn = (void *) work;
1662
0
                    session->lexer.outbuflen =  frame->can_dlc & 0x0f;
1663
0
                    for (l2 = 0; l2 < session->lexer.outbuflen; l2++) {
1664
0
                        session->lexer.outbuffer[l2]= frame->data[l2];
1665
0
                    }
1666
0
                } else if ((frame->data[0] & 0x1f) == 0) {
1667
0
                    unsigned int l2;
1668
1669
0
                    session->driver.nmea2000.fast_packet_len = frame->data[1];
1670
0
                    session->driver.nmea2000.idx = frame->data[0];
1671
#if NMEA2000_FAST_DEBUG
1672
                    GPSD_LOG(LOG_ERROR, &session->context->errout,
1673
                             "Set idx    %2x    %2x %2x %6d\n",
1674
                             frame->data[0],
1675
                             session->driver.nmea2000.unit,
1676
                             frame->data[1],
1677
                             source_pgn);
1678
#endif /* of #if NMEA2000_FAST_DEBUG */
1679
0
                    session->lexer.inbuflen = 0;
1680
0
                    session->driver.nmea2000.idx += 1;
1681
0
                    for (l2 = 2; l2 < 8; l2++) {
1682
0
                        session->lexer.inbuffer[session->lexer.inbuflen++] =
1683
0
                            frame->data[l2];
1684
0
                    }
1685
0
                    GPSD_LOG(LOG_DATA, &session->context->errout,
1686
0
                             "pgn %6d:%s \n", work->pgn, work->name);
1687
0
                } else if (frame->data[0] == session->driver.nmea2000.idx) {
1688
0
                    unsigned int l2;
1689
1690
0
                    for (l2 = 1; l2 < 8; l2++) {
1691
0
                        if (session->driver.nmea2000.fast_packet_len >
1692
0
                            session->lexer.inbuflen) {
1693
0
                            session->lexer.inbuffer[session->lexer.inbuflen++] =
1694
0
                                frame->data[l2];
1695
0
                        }
1696
0
                    }
1697
0
                    if (session->lexer.inbuflen ==
1698
0
                        session->driver.nmea2000.fast_packet_len) {
1699
#if NMEA2000_FAST_DEBUG
1700
                        GPSD_LOG(LOG_ERROR, &session->context->errout,
1701
                                 "Fast done  %2x %2x %2x %2x %6d\n",
1702
                                 session->driver.nmea2000.idx,
1703
                                 frame->data[0],
1704
                                 session->driver.nmea2000.unit,
1705
                                 (unsigned int)session->driver.nmea2000.fast_packet_len,
1706
                                 source_pgn);
1707
#endif /* of #if  NMEA2000_FAST_DEBUG */
1708
0
                        session->driver.nmea2000.workpgn = (void *) work;
1709
0
                        session->lexer.outbuflen =
1710
0
                            session->driver.nmea2000.fast_packet_len;
1711
0
                        for(l2 = 0; l2 < (unsigned int)session->lexer.outbuflen;
1712
0
                            l2++) {
1713
0
                            session->lexer.outbuffer[l2] =
1714
0
                                session->lexer.inbuffer[l2];
1715
0
                        }
1716
0
                        session->driver.nmea2000.fast_packet_len = 0;
1717
0
                    } else {
1718
0
                        session->driver.nmea2000.idx += 1;
1719
0
                    }
1720
0
                } else {
1721
0
                    GPSD_LOG(LOG_ERROR, &session->context->errout,
1722
0
                         "Fast error %2x %2x %2x %2x %6d\n",
1723
0
                         session->driver.nmea2000.idx,
1724
0
                         frame->data[0],
1725
0
                         session->driver.nmea2000.unit,
1726
0
                         (unsigned int)session->driver.nmea2000.fast_packet_len,
1727
0
                         source_pgn);
1728
0
                }
1729
0
            } else {
1730
0
                GPSD_LOG(LOG_WARN, &session->context->errout,
1731
0
                         "PGN not found %08d %08x \n",
1732
0
                         source_pgn, source_pgn);
1733
0
            }
1734
0
        } else {
1735
            // we got a unknown unit number
1736
0
            if (nmea2000_units[can_net][source_unit] == NULL) {
1737
0
                char buffer[55];
1738
1739
0
                (void) snprintf(buffer,
1740
0
                                sizeof(buffer),
1741
0
                                "nmea2000://%s:%u",
1742
0
                                can_interface_name[can_net],
1743
0
                                source_unit);
1744
0
                if (gpsd_add_device != NULL) {
1745
0
                    (void) gpsd_add_device(buffer, true);
1746
0
                }
1747
0
            }
1748
0
        }
1749
0
    } else {
1750
        // we got RTR or 2.0A CAN frame, not used
1751
0
    }
1752
0
}
1753
1754
1755
static ssize_t nmea2000_get(struct gps_device_t *session)
1756
0
{
1757
0
    struct can_frame frame;
1758
0
    ssize_t          status;
1759
1760
0
    session->lexer.outbuflen = 0;
1761
    // FIXME: read() into a struct is not guaranteed in C
1762
0
    status = read(session->gpsdata.gps_fd, &frame, sizeof(frame));
1763
0
    if (status == (ssize_t)sizeof(frame)) {
1764
0
        session->lexer.type = NMEA2000_PACKET;
1765
0
        find_pgn(&frame, session);
1766
1767
0
        return frame.can_dlc & 0x0f;
1768
0
    }
1769
0
    return 0;
1770
0
}
1771
1772
static gps_mask_t nmea2000_parse_input(struct gps_device_t *session)
1773
0
{
1774
0
    gps_mask_t mask;
1775
0
    PGN *work;
1776
1777
//  printf("NMEA2000 parse_input called\n");
1778
0
    mask = 0;
1779
0
    work = (PGN *) session->driver.nmea2000.workpgn;
1780
1781
0
    if (work != NULL) {
1782
0
        mask = (work->func)(&session->lexer.outbuffer[0],
1783
0
                            (int)session->lexer.outbuflen, work, session);
1784
0
        session->driver.nmea2000.workpgn = NULL;
1785
0
    }
1786
0
    session->lexer.outbuflen = 0;
1787
1788
0
    return mask;
1789
0
}
1790
1791
1792
int nmea2000_open(struct gps_device_t *session)
1793
0
{
1794
0
    char interface_name[GPS_PATH_MAX];
1795
0
    socket_t sock;
1796
0
    int status;
1797
0
    int unit_number;
1798
0
    int can_net;
1799
0
    unsigned int l;
1800
0
    struct ifreq ifr;
1801
0
    struct sockaddr_can addr;
1802
0
    char *unit_ptr;
1803
1804
0
    INVALIDATE_SOCKET(session->gpsdata.gps_fd);
1805
1806
0
    session->driver.nmea2000.can_net = 0;
1807
0
    can_net = -1;
1808
1809
0
    unit_number = -1;
1810
1811
0
    (void)strlcpy(interface_name, session->gpsdata.dev.path + 11,
1812
0
                  sizeof(interface_name));
1813
0
    unit_ptr = NULL;
1814
0
    for (l = 0; l < strnlen(interface_name, sizeof(interface_name)); l++) {
1815
0
        if (interface_name[l] == ':') {
1816
0
            unit_ptr = &interface_name[l+1];
1817
0
            interface_name[l] = 0;
1818
0
            continue;
1819
0
        }
1820
0
        if (unit_ptr != NULL) {
1821
0
            if (isdigit(interface_name[l]) == 0) {
1822
0
                GPSD_LOG(LOG_ERROR, &session->context->errout,
1823
0
                         "NMEA2000 open: Invalid character in unit number.\n");
1824
0
                return -1;
1825
0
            }
1826
0
        }
1827
0
    }
1828
1829
0
    if (unit_ptr != NULL) {
1830
0
        unit_number = atoi(unit_ptr);
1831
0
        if ((unit_number < 0) || (unit_number > (NMEA2000_UNITS-1))) {
1832
0
            GPSD_LOG(LOG_ERROR, &session->context->errout,
1833
0
                     "NMEA2000 open: Unit number out of range.\n");
1834
0
            return -1;
1835
0
        }
1836
0
        for (l = 0; l < NMEA2000_NETS; l++) {
1837
0
            if (strncmp(can_interface_name[l],
1838
0
                        interface_name,
1839
0
                        MIN(sizeof(interface_name),
1840
0
                            sizeof(can_interface_name[l]))) == 0) {
1841
0
                can_net = l;
1842
0
                break;
1843
0
            }
1844
0
        }
1845
0
        if (can_net < 0) {
1846
0
            GPSD_LOG(LOG_ERROR, &session->context->errout,
1847
0
                     "NMEA2000 open: CAN device not open: %s .\n",
1848
0
                     interface_name);
1849
0
            return -1;
1850
0
        }
1851
0
    } else {
1852
0
        for (l = 0; l < NMEA2000_NETS; l++) {
1853
0
            if (strncmp(can_interface_name[l],
1854
0
                        interface_name,
1855
0
                        MIN(sizeof(interface_name),
1856
0
                            sizeof(can_interface_name[l]))) == 0) {
1857
0
                GPSD_LOG(LOG_ERROR, &session->context->errout,
1858
0
                         "NMEA2000 open: CAN device duplicate open: %s .\n",
1859
0
                         interface_name);
1860
0
                return -1;
1861
0
            }
1862
0
        }
1863
0
        for (l = 0; l < NMEA2000_NETS; l++) {
1864
0
            if (can_interface_name[l][0] == 0) {
1865
0
                can_net = l;
1866
0
                break;
1867
0
            }
1868
0
        }
1869
0
        if (can_net < 0) {
1870
0
            GPSD_LOG(LOG_ERROR, &session->context->errout,
1871
0
                     "NMEA2000 open: Too many CAN networks open.\n");
1872
0
            return -1;
1873
0
        }
1874
0
    }
1875
1876
    /* Create the socket */
1877
0
    sock = socket(PF_CAN, SOCK_RAW, CAN_RAW);
1878
1879
0
    if (BAD_SOCKET(sock)) {
1880
0
        GPSD_LOG(LOG_ERROR, &session->context->errout,
1881
0
                 "NMEA2000 open: can not get socket.\n");
1882
0
        return -1;
1883
0
    }
1884
1885
0
    status = fcntl(sock, F_SETFL, O_NONBLOCK);
1886
0
    if (status != 0) {
1887
0
        GPSD_LOG(LOG_ERROR, &session->context->errout,
1888
0
                 "NMEA2000 open: can not set socket to O_NONBLOCK.\n");
1889
0
        close(sock);
1890
0
        return -1;
1891
0
    }
1892
1893
    /* Locate the interface you wish to use */
1894
0
    strlcpy(ifr.ifr_name, interface_name, sizeof(ifr.ifr_name));
1895
0
    status = ioctl(sock, SIOCGIFINDEX, &ifr); /* ifr.ifr_ifindex gets filled
1896
                                               * with that device's index */
1897
1898
0
    if (status != 0) {
1899
0
        GPSD_LOG(LOG_ERROR, &session->context->errout,
1900
0
                 "NMEA2000 open: can not find CAN device.\n");
1901
0
        close(sock);
1902
0
        return -1;
1903
0
    }
1904
1905
    /* Select that CAN interface, and bind the socket to it. */
1906
0
    addr.can_family = AF_CAN;
1907
0
    addr.can_ifindex = ifr.ifr_ifindex;
1908
0
    status = bind(sock, (struct sockaddr*)&addr, sizeof(addr) );
1909
0
    if (status != 0) {
1910
0
        GPSD_LOG(LOG_ERROR, &session->context->errout,
1911
0
                 "NMEA2000 open: bind failed.\n");
1912
0
        close(sock);
1913
0
        return -1;
1914
0
    }
1915
1916
0
    gpsd_switch_driver(session, "NMEA2000");
1917
0
    session->gpsdata.gps_fd = sock;
1918
0
    session->sourcetype = SOURCE_CAN;
1919
0
    session->servicetype = SERVICE_SENSOR;
1920
0
    session->driver.nmea2000.can_net = can_net;
1921
1922
0
    if (unit_ptr != NULL) {
1923
0
        nmea2000_units[can_net][unit_number] = session;
1924
0
        session->driver.nmea2000.unit = unit_number;
1925
0
        session->driver.nmea2000.unit_valid = true;
1926
0
    } else {
1927
0
        strlcpy(can_interface_name[can_net],
1928
0
                interface_name,
1929
0
                MIN(sizeof(can_interface_name[0]), sizeof(interface_name)));
1930
0
        session->driver.nmea2000.unit_valid = false;
1931
0
        for (l = 0; l < NMEA2000_UNITS; l++) {
1932
0
            nmea2000_units[can_net][l] = NULL;
1933
0
        }
1934
0
    }
1935
1936
0
    session->gpsdata.dev.parity = 'N';
1937
0
    session->gpsdata.dev.baudrate = 250000;
1938
0
    session->gpsdata.dev.stopbits = 0;
1939
0
    return session->gpsdata.gps_fd;
1940
0
}
1941
1942
void nmea2000_close(struct gps_device_t *session)
1943
0
{
1944
0
    if (!BAD_SOCKET(session->gpsdata.gps_fd)) {
1945
        // cast for 32-bit ints.
1946
0
        GPSD_LOG(LOG_SPIN, &session->context->errout,
1947
0
                 "close(%ld) in nmea2000_close(%s)\n",
1948
0
                 (long)session->gpsdata.gps_fd, session->gpsdata.dev.path);
1949
0
        (void)close(session->gpsdata.gps_fd);
1950
0
        INVALIDATE_SOCKET(session->gpsdata.gps_fd);
1951
1952
0
        if (session->driver.nmea2000.unit_valid) {
1953
0
            unsigned int l1, l2;
1954
1955
0
            for (l1 = 0; l1 < NMEA2000_NETS; l1++) {
1956
0
                for (l2 = 0; l2 < NMEA2000_UNITS; l2++) {
1957
0
                    if (session == nmea2000_units[l1][l2]) {
1958
0
                        session->driver.nmea2000.unit_valid = false;
1959
0
                        session->driver.nmea2000.unit = 0;
1960
0
                        session->driver.nmea2000.can_net = 0;
1961
                        nmea2000_units[l1][l2] = NULL;
1962
0
                    }
1963
0
                }
1964
0
            }
1965
0
        }
1966
0
    }
1967
0
}
1968
1969
/* *INDENT-OFF* */
1970
const struct gps_type_t driver_nmea2000 = {
1971
    .type_name      = "NMEA2000",       /* full name of type */
1972
    .packet_type    = NMEA2000_PACKET,  /* associated lexer packet type */
1973
    .flags          = DRIVER_STICKY,    /* remember this */
1974
    .trigger        = NULL,             /* detect their main sentence */
1975
    .channels       = 12,               /* not an actual GPS at all */
1976
    .probe_detect   = NULL,
1977
    .get_packet     = nmea2000_get,     /* how to get a packet */
1978
    .parse_packet   = nmea2000_parse_input,     /* how to interpret a packet */
1979
    .rtcm_writer    = NULL,             /* Don't send RTCM to this */
1980
    .init_query     = NULL,             /* non-perturbing query */
1981
    .event_hook     = NULL,
1982
    .speed_switcher = NULL,             /* no speed switcher */
1983
    .mode_switcher  = NULL,             /* no mode switcher */
1984
    .rate_switcher  = NULL,             /* no rate switcher */
1985
    .min_cycle.tv_sec  = 1,             /* not relevant, no rate switch */
1986
    .min_cycle.tv_nsec = 0,             /* not relevant, no rate switch */
1987
    .control_send   = NULL,             /* how to send control strings */
1988
    .time_offset     = NULL,
1989
};
1990
/* *INDENT-ON* */
1991
1992
/* end */
1993
1994
#else   /* of  defined(NMEA2000_ENABLE) */
1995
/* dummy variable to some old linkers do not complain about empty
1996
 * object file */
1997
int nmea2000_dummy = 1;
1998
#endif /* of  defined(NMEA2000_ENABLE) */
1999
2000
// vim: set expandtab shiftwidth=4