Coverage Report

Created: 2025-11-24 06:31

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