Coverage Report

Created: 2026-04-12 06:12

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
 *     https://canboat.github.io/canboat/canboat.html
12
 *
13
 * Message contents can be had from canboat/analyzer:
14
 *     analyzer -explain
15
 *
16
 * This file is Copyright by the GPSD project
17
 * SPDX-License-Identifier: BSD-2-clause
18
 */
19
20
#include "../include/gpsd_config.h"  // must be before all includes
21
22
#if defined(NMEA2000_ENABLE)
23
24
#include <ctype.h>
25
#include <errno.h>                  // for strerror(errno), errno)
26
#include <fcntl.h>
27
#include <linux/can.h>              // for  struct can_frame
28
#include <linux/can/error.h>        // for CAN_ERR_BUSOFF, etc.
29
#include <linux/can/raw.h>
30
#include <math.h>
31
#include <net/if.h>
32
#include <stdarg.h>
33
#include <stdbool.h>
34
#include <stdio.h>
35
#include <stdlib.h>
36
#include <string.h>
37
#include <sys/ioctl.h>
38
#include <sys/socket.h>
39
#include <time.h>
40
#include <unistd.h>
41
42
#include "../include/gpsd.h"
43
#include "../include/libgps.h"
44
#include "../include/driver_nmea2000.h"
45
#include "../include/bits.h"
46
#include "../include/timespec.h"
47
48
49
#define LOG_FILE 1
50
0
#define NMEA2000_NETS 4
51
/* NMEA 2000 source addr (SA) is a byte,
52
 * but 254 is "request for address claim"
53
 * and 255 is broadcast address.  So 254 is the number of addresses possible,
54
 * and 253 the highest addresses number. */
55
0
#define NMEA2000_ADDRS 254
56
#define CAN_NAMELEN 32
57
0
#define MIN(a,b) ((a < b) ? a : b)
58
59
#define NMEA2000_DEBUG_AIS 0
60
61
static struct gps_device_t *nmea2000_units[NMEA2000_NETS][NMEA2000_ADDRS];
62
static char can_interface_name[NMEA2000_NETS][CAN_NAMELEN + 1];
63
64
typedef struct PGN {
65
    unsigned int pgn;
66
    char  fast;
67
    char  type;
68
    gps_mask_t (* func)(struct gps_device_t *session);
69
    const char *name;
70
} PGN;
71
72
#if LOG_FILE
73
FILE *logFile = NULL;
74
#endif  // of if LOG_FILE
75
76
// WTF???
77
extern bool __attribute__ ((weak)) gpsd_add_device(const char *device_name,
78
                                                   bool flag_nowait);
79
80
/* Industry ids
81
 * https://canboat.github.io/canboat/canboat.html#lookup-INDUSTRY_CODE
82
 */
83
static const struct vlist_t indus_ids[] = {
84
    {0, "Global"},
85
    {1, "Highway"},
86
    {2, "Agriculture"},
87
    {3, "Construction"},
88
    {4, "Marine Industry"},
89
    {5, "Industrial"},
90
    {0, NULL}
91
};
92
93
/* Manfacturer ids
94
 * https://canboat.github.io/canboat/canboat.html#lookup-MANUFACTURER_CODE
95
 */
96
static const struct vlist_t mfg_ids[] = {
97
    {69, "ARKS Enterprises, Inc."},
98
    {78, "FW Murphy/Enovation Controls"},
99
    {80, "Twin Disc"},
100
    {85, "Kohler Power Systems"},
101
    {88, "Hemisphere GPS Inc"},
102
    {116, "BEP Marine"},
103
    {135, "Airmar"},
104
    {137, "Maretron"},
105
    {140, "Lowrance"},
106
    {144, "Mercury Marine"},
107
    {147, "Nautibus Electronic GmbH"},
108
    {148, "Blue Water Data"},
109
    {154, "Westerbeke"},
110
    {157, "ISSPRO Inc"},
111
    {161, "Offshore Systems (UK) Ltd."},
112
    {163, "Evinrude/BRP"},
113
    {165, "CPAC Systems AB"},
114
    {168, "Xantrex Technology Inc."},
115
    {169, "Marlin Technologies, Inc."},
116
    {172, "Yanmar Marine"},
117
    {174, "Volvo Penta"},
118
    {175, "Honda Marine"},
119
    {176, "Carling Technologies Inc. (Moritz Aerospace)"},
120
    {185, "Beede Instruments"},
121
    {192, "Floscan Instrument Co. Inc."},
122
    {193, "Nobletec"},
123
    {198, "Mystic Valley Communications"},
124
    {199, "Actia"},
125
    {200, "Honda Marine"},
126
    {201, "Disenos Y Technologia"},
127
    {211, "Digital Switching Systems"},
128
    {215, "Xintex/Atena"},
129
    {224, "EMMI NETWORK S.L."},
130
    {225, "Honda Marine"},
131
    {228, "ZF"},
132
    {229, "Garmin"},
133
    {233, "Yacht Monitoring Solutions"},
134
    {235, "Sailormade Marine Telemetry/Tetra Technology LTD"},
135
    {243, "Eride"},
136
    {250, "Honda Marine"},
137
    {257, "Honda Motor Company LTD"},
138
    {272, "Groco"},
139
    {273, "Actisense"},
140
    {274, "Amphenol LTW Technology"},
141
    {275, "Navico"},
142
    {283, "Hamilton Jet"},
143
    {285, "Sea Recovery"},
144
    {286, "Coelmo SRL Italy"},
145
    {295, "BEP Marine"},
146
    {304, "Empir Bus"},
147
    {305, "NovAtel"},
148
    {306, "Sleipner Motor AS"},
149
    {307, "MBW Technologies"},
150
    {311, "Fischer Panda"},
151
    {315, "ICOM"},
152
    {328, "Qwerty"},
153
    {329, "Dief"},
154
    {341, "Boening Automationstechnologie GmbH & Co. KG"},
155
    {345, "Korean Maritime University"},
156
    {351, "Thrane and Thrane"},
157
    {355, "Mastervolt"},
158
    {356, "Fischer Panda Generators"},
159
    {358, "Victron Energy"},
160
    {370, "Rolls Royce Marine"},
161
    {373, "Electronic Design"},
162
    {374, "Northern Lights"},
163
    {378, "Glendinning"},
164
    {381, "B & G"},
165
    {384, "Rose Point Navigation Systems"},
166
    {385, "Johnson Outdoors Marine Electronics Inc Geonav"},
167
    {394, "Capi 2"},
168
    {396, "Beyond Measure"},
169
    {400, "Livorsi Marine"},
170
    {404, "ComNav"},
171
    {409, "Chetco"},
172
    {419, "Fusion Electronics"},
173
    {421, "Standard Horizon"},
174
    {422, "True Heading AB"},
175
    {426, "Egersund Marine Electronics AS"},
176
    {427, "em-trak Marine Electronics"},
177
    {431, "Tohatsu Co, JP"},
178
    {437, "Digital Yacht"},
179
    {438, "Comar Systems Limited"},
180
    {440, "Cummins"},
181
    {443, "VDO (aka Continental-Corporation)"},
182
    {451, "Parker Hannifin aka Village Marine Tech"},
183
    {459, "Alltek Marine Electronics Corp"},
184
    {460, "SAN GIORGIO S.E.I.N"},
185
    {466, "Veethree Electronics & Marine"},
186
    {467, "Humminbird Marine Electronics"},
187
    {470, "SI-TEX Marine Electronics"},
188
    {471, "Sea Cross Marine AB"},
189
    {475, "GME aka Standard Communications Pty LTD"},
190
    {476, "Humminbird Marine Electronics"},
191
    {478, "Ocean Sat BV"},
192
    {481, "Chetco Digitial Instruments"},
193
    {493, "Watcheye"},
194
    {499, "Lcj Capteurs"},
195
    {502, "Attwood Marine"},
196
    {503, "Naviop S.R.L."},
197
    {504, "Vesper Marine Ltd"},
198
    {510, "Marinesoft Co. LTD"},
199
    {513, "Simarine"},
200
    {517, "NoLand Engineering"},
201
    {518, "Transas USA"},
202
    {529, "National Instruments Korea"},
203
    {530, "National Marine Electronics Association"},
204
    {532, "Onwa Marine"},
205
    {540, "Webasto"},
206
    {571, "Marinecraft (South Korea)"},
207
    {573, "McMurdo Group aka Orolia LTD"},
208
    {578, "Advansea"},
209
    {579, "KVH"},
210
    {580, "San Jose Technology"},
211
    {583, "Yacht Control"},
212
    {586, "Suzuki Motor Corporation"},
213
    {591, "US Coast Guard"},
214
    {595, "Ship Module aka Customware"},
215
    {600, "Aquatic AV"},
216
    {605, "Aventics GmbH"},
217
    {606, "Intellian"},
218
    {612, "SamwonIT"},
219
    {614, "Arlt Tecnologies"},
220
    {637, "Bavaria Yacts"},
221
    {641, "Diverse Yacht Services"},
222
    {644, "Wema U.S.A dba KUS"},
223
    {645, "Garmin"},
224
    {658, "Shenzhen Jiuzhou Himunication"},
225
    {688, "Rockford Corp"},
226
    {699, "Harman International"},
227
    {704, "JL Audio"},
228
    {708, "Lars Thrane"},
229
    {715, "Autonnic"},
230
    {717, "Yacht Devices"},
231
    {734, "REAP Systems"},
232
    {735, "Au Electronics Group"},
233
    {739, "LxNav"},
234
    {741, "Littelfuse, Inc (formerly Carling Technologies)"},
235
    {743, "DaeMyung"},
236
    {744, "Woosung"},
237
    {748, "ISOTTA IFRA srl"},
238
    {773, "Clarion US"},
239
    {776, "HMI Systems"},
240
    {777, "Ocean Signal"},
241
    {778, "Seekeeper"},
242
    {781, "Poly Planar"},
243
    {785, "Fischer Panda DE"},
244
    {795, "Broyda Industries"},
245
    {796, "Canadian Automotive"},
246
    {797, "Tides Marine"},
247
    {798, "Lumishore"},
248
    {799, "Still Water Designs and Audio"},
249
    {802, "BJ Technologies (Beneteau)"},
250
    {803, "Gill Sensors"},
251
    {811, "Blue Water Desalination"},
252
    {815, "FLIR"},
253
    {824, "Undheim Systems"},
254
    {826, "Lewmar Inc"},
255
    {838, "TeamSurv"},
256
    {844, "Fell Marine"},
257
    {847, "Oceanvolt"},
258
    {862, "Prospec"},
259
    {868, "Data Panel Corp"},
260
    {890, "L3 Technologies"},
261
    {894, "Rhodan Marine Systems"},
262
    {896, "Nexfour Solutions"},
263
    {905, "ASA Electronics"},
264
    {909, "Marines Co (South Korea)"},
265
    {911, "Nautic-on"},
266
    {917, "Sentinel"},
267
    {929, "JL Marine ystems"},
268
    {930, "Ecotronix"},
269
    {944, "Zontisa Marine"},
270
    {951, "EXOR International"},
271
    {962, "Timbolier Industries"},
272
    {963, "TJC Micro"},
273
    {968, "Cox Powertrain"},
274
    {969, "Blue Seas"},
275
    {981, "Kobelt Manufacturing Co. Ltd"},
276
    {992, "Blue Ocean IOT"},
277
    {997, "Xenta Systems"},
278
    {1004, "Ultraflex SpA"},
279
    {1008, "Lintest SmartBoat"},
280
    {1011, "Soundmax"},
281
    {1020, "Team Italia Marine (Onyx Marine Automation s.r.l)"},
282
    {1021, "Entratech"},
283
    {1022, "ITC Inc."},
284
    {1029, "The Marine Guardian LLC"},
285
    {1047, "Sonic Corporation"},
286
    {1051, "ProNav"},
287
    {1053, "Vetus Maxwell INC."},
288
    {1056, "Lithium Pros"},
289
    {1059, "Boatrax"},
290
    {1062, "Marol Co ltd"},
291
    {1065, "CALYPSO Instruments"},
292
    {1066, "Spot Zero Water"},
293
    {1069, "Lithionics Battery LLC"},
294
    {1070, "Quick-teck Electronics Ltd"},
295
    {1075, "Uniden America"},
296
    {1083, "Nauticoncept"},
297
    {1084, "Shadow-Caster LED lighting LLC"},
298
    {1085, "Wet Sounds, LLC"},
299
    {1088, "E-T-A Circuit Breakers"},
300
    {1092, "Scheiber"},
301
    {1100, "Smart Yachts International Limited"},
302
    {1109, "Dockmate"},
303
    {1114, "Bobs Machine"},
304
    {1118, "L3Harris ASV"},
305
    {1119, "Balmar LLC"},
306
    {1120, "Elettromedia spa"},
307
    {1127, "Electromaax"},
308
    {1140, "Across Oceans Systems Ltd."},
309
    {1145, "Kiwi Yachting"},
310
    {1150, "BSB Artificial Intelligence GmbH"},
311
    {1151, "Orca Technologoes AS"},
312
    {1154, "TBS Electronics BV"},
313
    {1158, "Technoton Electroics"},
314
    {1160, "MG Energy Systems B.V."},
315
    {1169, "Sea Macine Robotics Inc."},
316
    {1171, "Vista Manufacturing"},
317
    {1183, "Zipwake"},
318
    {1186, "Sailmon BV"},
319
    {1192, "Airmoniq Pro Kft"},
320
    {1194, "Sierra Marine"},
321
    {1200, "Xinuo Information Technology (Xiamen)"},
322
    {1218, "Septentrio"},
323
    {1233, "NKE Marine Elecronics"},
324
    {1238, "SuperTrack Aps"},
325
    {1239, "Honda Electronics Co., LTD"},
326
    {1245, "Raritan Engineering Company, Inc"},
327
    {1249, "Integrated Power Solutions AG"},
328
    {1260, "Interactive Technologies, Inc."},
329
    {1283, "LTG-Tech"},
330
    {1299, "Energy Solutions (UK) LTD."},
331
    {1300, "WATT Fuel Cell Corp"},
332
    {1302, "Pro Mainer"},
333
    {1305, "Dragonfly Energy"},
334
    {1306, "Koden Electronics Co., Ltd"},
335
    {1311, "Humphree AB"},
336
    {1316, "Hinkley Yachts"},
337
    {1317, "Global Marine Management GmbH (GMM)"},
338
    {1320, "Triskel Marine Ltd"},
339
    {1330, "Warwick Control Technologies"},
340
    {1331, "Dolphin Charger"},
341
    {1337, "Barnacle Systems Inc"},
342
    {1348, "Radian IoT, Inc."},
343
    {1353, "Ocean LED Marine Ltd"},
344
    {1359, "BluNav"},
345
    {1361, "OVA (Nantong Saiyang Electronics Co., Ltd)"},
346
    {1368, "RAD Propulsion"},
347
    {1369, "Electric Yacht"},
348
    {1372, "Elco Motor Yachts"},
349
    {1384, "Tecnoseal Foundry S.r.l"},
350
    {1385, "Pro Charging Systems, LLC"},
351
    {1389, "EVEX Co., LTD"},
352
    {1398, "Gobius Sensor Technology AB"},
353
    {1403, "Arco Marine"},
354
    {1408, "Lenco Marine Inc."},
355
    {1413, "Naocontrol S.L."},
356
    {1417, "Revatek"},
357
    {1438, "Aeolionics"},
358
    {1439, "PredictWind Ltd"},
359
    {1440, "Egis Mobile Electric"},
360
    {1445, "Starboard Yacht Group"},
361
    {1446, "Roswell Marine"},
362
    {1451, "ePropulsion (Guangdong ePropulsion Technology Ltd.)"},
363
    {1452, "Micro-Air LLC"},
364
    {1453, "Vital Battery"},
365
    {1458, "Ride Controller LLC"},
366
    {1460, "Tocaro Blue"},
367
    {1461, "Vanquish Yachts"},
368
    {1471, "FT Technologies"},
369
    {1478, "Alps Alpine Co., Ltd."},
370
    {1481, "E-Force Marine"},
371
    {1482, "CMC Marine"},
372
    {1483, "Nanjing Sandemarine Information Technology Co., Ltd."},
373
    {1850, "Teleflex Marine (SeaStar Solutions)"},
374
    {1851, "Raymarine"},
375
    {1852, "Navionics"},
376
    {1853, "Japan Radio Co"},
377
    {1854, "Northstar Technologies"},
378
    {1855, "Furuno"},
379
    {1856, "Trimble"},
380
    {1857, "Simrad"},
381
    {1858, "Litton"},
382
    {1859, "Kvasar AB"},
383
    {1860, "MMP"},
384
    {1861, "Vector Cantech"},
385
    {1862, "Yamaha Marine"},
386
    {1863, "Faria Instrument"},
387
    {0, NULL}
388
};
389
390
0
#define SHIFT32 0x100000000l
391
392
static int scale_int(int32_t var, const int64_t factor)
393
0
{
394
0
    int64_t ret = (var * factor) >> 32;
395
396
0
    return (int)ret;
397
0
}
398
399
static void print_data(struct gps_device_t *session)
400
0
{
401
0
    unsigned char *buffer = session->lexer.outbuffer;
402
0
    size_t len = session->lexer.outbuflen;
403
0
    const PGN *pgn = (const PGN *)session->driver.nmea2000.workpgn;
404
0
    size_t l1;
405
0
    int l2 = 0;
406
0
    int idx = 0;
407
0
    char bu[128];
408
409
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
410
0
             "NMEA2000: pgn %6d SA %u len %zu %s\n",
411
0
             pgn->pgn, session->driver.nmea2000.source_addr, len, pgn->name);
412
413
0
    if (LOG_IO > libgps_debuglevel) {
414
0
        return;
415
0
    }
416
417
0
    for (l1 = 0; l1 < len; l1++) {
418
0
        if (0 == (l1 % 20) &&
419
0
            0 != l1) {
420
0
            GPSD_LOG(LOG_IO, &session->context->errout,
421
0
                     "NMEA2000: got data: %s\n", bu);
422
0
            idx = 0;
423
0
            bu[0] = '\0';
424
0
        }
425
        // FIXME: check buffer overrun
426
0
        l2 = sprintf(&bu[idx], "x%02x ", (unsigned)buffer[l1]);
427
0
        idx += l2;
428
0
    }
429
0
    GPSD_LOG(LOG_IO, &session->context->errout,
430
0
             "NMEA2000: got data: %s\n", bu);
431
0
}
432
433
static gps_mask_t get_mode(struct gps_device_t *session)
434
0
{
435
0
    if (1 & session->driver.nmea2000.mode_valid) {
436
0
        session->newdata.mode = session->driver.nmea2000.mode;
437
0
    } else {
438
0
        session->newdata.mode = MODE_NOT_SEEN;
439
0
    }
440
441
0
    if (2 & session->driver.nmea2000.mode_valid) {
442
0
        return MODE_SET | USED_IS;
443
0
    } else {
444
0
        return MODE_SET;
445
0
    }
446
0
}
447
448
449
static int decode_ais_header(struct gps_context_t *context,
450
                             unsigned char *bu,
451
                             size_t len,
452
                             struct ais_t *ais,
453
                             unsigned int mask)
454
0
{
455
0
    if (4 < len) {
456
0
        ais->type   = (unsigned)( bu[0]       & 0x3f);
457
0
        ais->repeat = (unsigned)((bu[0] >> 6) & 0x03);
458
0
        ais->mmsi   = (unsigned) getleu32(bu, 1);
459
0
        ais->mmsi  &= mask;
460
0
        GPSD_LOG(LOG_INF, &context->errout,
461
0
                 "NMEA2000 AIS  message type %u, MMSI %09u:\n",
462
0
                 ais->type, ais->mmsi);
463
0
        return 1;
464
0
    }
465
    //  else
466
0
    ais->type   =  0;
467
0
    ais->repeat =  0;
468
0
    ais->mmsi   =  0;
469
0
    GPSD_LOG(LOG_ERROR, &context->errout,
470
0
             "NMEA2000 AIS  message type %u, too short message.\n",
471
0
             ais->type);
472
0
    return 0;
473
0
}
474
475
476
static void decode_ais_channel_info(unsigned char *bu,
477
                                    size_t len,
478
                                    unsigned int offset,
479
                                    struct gps_device_t *session)
480
0
{
481
0
    unsigned int pos = offset / 8;
482
0
    unsigned int bpos = offset % 8;
483
0
    uint16_t x;
484
485
0
    if (pos >= (unsigned int)len) {
486
0
        session->driver.aivdm.ais_channel = 'A';
487
0
        return;
488
0
    }
489
0
    x = getleu16(bu, pos);
490
0
    x = (uint16_t)((x >> bpos) & 0x1f);
491
0
    switch (x) {
492
0
    case 1:
493
0
        FALLTHROUGH
494
0
    case 3:
495
0
        session->driver.aivdm.ais_channel = 'B';
496
0
        break;
497
0
    default:
498
0
        session->driver.aivdm.ais_channel = 'A';
499
0
        break;
500
0
    }
501
0
    return;
502
0
}
503
504
505
static int ais_turn_rate(int rate)
506
0
{
507
0
    if (0 > rate) {
508
0
        return -ais_turn_rate(-rate);
509
0
    }
510
0
    return (int)(4.733 * sqrt(rate * RAD_2_DEG * .0001 * 60.0));
511
0
}
512
513
514
static double ais_direction(unsigned int val, double scale)
515
0
{
516
0
    if ((0xffff == val) &&
517
0
        (1.0 == scale)) {
518
0
        return 511.0;
519
0
    }
520
0
    return val * RAD_2_DEG * 0.0001 * scale;
521
0
}
522
523
524
/*
525
 *   PGN 59392: ISO Acknowledgment
526
 */
527
static gps_mask_t hnd_059392(struct gps_device_t *session UNUSED)
528
0
{
529
0
    return 0;
530
0
}
531
532
533
/*
534
 *   PGN 60928: ISO Address Claim
535
 */
536
static gps_mask_t hnd_060928(struct gps_device_t *session UNUSED)
537
0
{
538
0
    return 0;
539
0
}
540
541
542
/*
543
 *   PGN 126208: NMEA Command/Request/Acknowledge
544
 */
545
static gps_mask_t hnd_126208(struct gps_device_t *session UNUSED)
546
0
{
547
0
    return 0;
548
0
}
549
550
551
/*
552
 *   PGN 126464: ISO Transmit/Receive PGN List
553
 */
554
static gps_mask_t hnd_126464(struct gps_device_t *session UNUSED)
555
0
{
556
0
    return 0;
557
0
}
558
559
/*
560
 *   PGN 126720: Maretron proprietary, used by Garmin, etc.:
561
 */
562
static gps_mask_t hnd_126720(struct gps_device_t *session UNUSED)
563
0
{
564
0
    unsigned char *bu = session->lexer.outbuffer;
565
566
0
    unsigned word0 = getleu16(bu, 0);
567
0
    unsigned mfg = word0 & 0x07ff;           // 11 bits Manufacturer Code
568
    // 2 bits reserved
569
0
    unsigned indus = (word0 >> 13) & 0x07;   // 3 bits Industry Code
570
0
    unsigned prop_id = bu[2];                // 8 bits Proprietary ID
571
0
    unsigned cmd = bu[3];                    // 8 bits Command
572
573
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
574
0
             "NMEA2000: pgn 126720 mfg %u indus %u prop %u cmd %u (%s/%s)\n",
575
0
             mfg, indus, prop_id, cmd,
576
0
             val2str(mfg, mfg_ids),
577
0
             val2str(indus, indus_ids));
578
0
    return 0;
579
0
}
580
581
static const struct vlist_t time_sources[] = {
582
    {0, "GPS"},
583
    {1, "GLONASS"},
584
    {2, "Radio Station"},
585
    {3, "Local Cesium clock"},
586
    {4, "Local Rubidium clock"},
587
    {5, "Local Crystal clock"},
588
    {0, NULL}
589
};
590
591
/*
592
 * PGN: 126992 / 00370020 / 1F010 - 8 - System Time
593
 *
594
 *  Field #1: SID
595
 *                  Bits: 8
596
 *                  Signed: false
597
 *  Field #2: Source
598
 *                  Bits: 4
599
 *                  Type: Lookup table
600
 *                  Signed: false
601
 *                  Lookup: 0=GPS
602
 *                  Lookup: 1=GLONASS
603
 *                  Lookup: 2=Radio Station
604
 *                  Lookup: 3=Local Cesium clock
605
 *                  Lookup: 4=Local Rubidium clock
606
 *                  Lookup: 5=Local Crystal clock
607
 *  Field #3: Reserved - Reserved
608
 *                  Bits: 4
609
 *                  Type: Binary data
610
 *                  Signed: false
611
 *  Field #4: Date - Days since January 1, 1970
612
 *                  Bits: 16
613
 *                  Units: days
614
 *                  Type: Date
615
 *                  Resolution: 1
616
 *                  Signed: false
617
 *  Field #5: Time - Seconds since midnight
618
 *                  Bits: 32
619
 *                  Units: s
620
 *                  Type: Time
621
 *                  Resolution: 0.0001
622
 *                  Signed: false
623
 *
624
 */
625
static gps_mask_t hnd_126992(struct gps_device_t *session)
626
0
{
627
0
    unsigned char *bu = session->lexer.outbuffer;
628
629
0
    unsigned sid = bu[0];
630
0
    unsigned source = bu[1] & 0x0f;
631
0
    uint64_t usecs = getleu32(bu, 4) * 100UL;   // time of day in us
632
633
0
    USTOTS(&session->newdata.time, usecs);
634
0
    session->newdata.time.tv_sec += (time_t)(getleu16(bu, 2) * 24 * 60 * 60);
635
636
    // casts for 32 bit time_t
637
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
638
0
             "NMEA2000: pgn 126992 sid %u source %u (%s) time %lld %09ld\n",
639
0
             sid, source, val2str(source, time_sources),
640
0
             (long long)session->newdata.time.tv_sec,
641
0
             (long)session->newdata.time.tv_nsec);
642
643
0
    return TIME_SET | get_mode(session);
644
0
}
645
646
647
/*
648
 *   PGN 126996: ISO Product Information
649
 */
650
static gps_mask_t hnd_126996(struct gps_device_t *session UNUSED)
651
0
{
652
0
    return 0;
653
0
}
654
655
656
/*
657
 *   PGN 127245: NAV Rudder
658
 */
659
static gps_mask_t hnd_127245(struct gps_device_t *session UNUSED)
660
0
{
661
0
    return 0;
662
0
}
663
664
665
/*
666
 *   PGN 127250: NAV Vessel Heading
667
 */
668
static gps_mask_t hnd_127250(struct gps_device_t *session UNUSED)
669
0
{
670
0
    unsigned char *bu = session->lexer.outbuffer;
671
0
    int aux;
672
673
0
    session->gpsdata.attitude.heading = getleu16(bu, 1) * RAD_2_DEG * 0.0001;
674
//  printf("ATT 0:%8.3f\n",session->gpsdata.attitude.heading);
675
0
    aux = getles16(bu, 3);
676
0
    if (0x07fff != aux) {
677
0
        session->gpsdata.attitude.heading += aux * RAD_2_DEG * 0.0001;
678
0
    }
679
//  printf("ATT 1:%8.3f %6x\n",session->gpsdata.attitude.heading, aux);
680
0
    aux = getles16(bu, 5);
681
0
    if (0x07fff != aux) {
682
0
        session->gpsdata.attitude.heading += aux * RAD_2_DEG * 0.0001;
683
0
    }
684
//  printf("ATT 2:%8.3f %6x\n",session->gpsdata.attitude.heading, aux);
685
686
0
    return ONLINE_SET | ATTITUDE_SET;
687
0
}
688
689
690
/*
691
 *   PGN 127258: GNSS Magnetic Variation
692
 *
693
 *   1 Sequence ID
694
 *   2 Variation Source
695
 *   3 Reserved Bits
696
 *   4 Age of Service (Date)
697
 *   5 Variation
698
 *   6 Reserved B
699
 */
700
static gps_mask_t hnd_127258(struct gps_device_t *session UNUSED)
701
0
{
702
    // FIXME?  Get magnetic variation
703
0
    return 0;
704
0
}
705
706
707
static const struct vlist_t dc_types[] = {
708
    {0, "Battery"},
709
    {1, "Alternator"},
710
    {2, "Convertor"},
711
    {3, "Solar cell"},
712
    {4, "Wind generator"},
713
    {0, NULL},
714
};
715
716
/*
717
 *   PGN 127506: PWR DC Detailed Status
718
 */
719
static gps_mask_t hnd_127506(struct gps_device_t *session)
720
0
{
721
0
    unsigned char *bu = session->lexer.outbuffer;
722
723
0
    unsigned sid = bu[0];
724
0
    unsigned instance = bu[1];
725
0
    unsigned dc_type = bu[2];
726
0
    unsigned charge = bu[3];
727
0
    unsigned health = bu[4];
728
0
    unsigned timer = getles16(bu, 5);
729
0
    unsigned ripple = getles16(bu, 7);
730
0
    unsigned cap = getles16(bu, 9);
731
732
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
733
0
             "NMEA2000: pgn 127506 sid %u instance %u DC type %u (%s) "
734
0
             "charge %u health %u time %u ripple %u cap %u\n",
735
0
             sid, instance, dc_type, val2str(dc_type, dc_types),
736
0
             charge, health, timer, ripple, cap);
737
0
    return 0;
738
0
}
739
740
741
/*
742
 *   PGN 127508: PWR Battery Status
743
 */
744
static gps_mask_t hnd_127508(struct gps_device_t *session UNUSED)
745
0
{
746
0
    return 0;
747
0
}
748
749
750
/*
751
 *   PGN 127513: PWR Battery Configuration Status
752
 */
753
static gps_mask_t hnd_127513(struct gps_device_t *session UNUSED)
754
0
{
755
0
    return 0;
756
0
}
757
758
759
/*
760
 *   PGN 128259: NAV Speed
761
 */
762
static gps_mask_t hnd_128259(struct gps_device_t *session UNUSED)
763
0
{
764
0
    return 0;
765
0
}
766
767
768
/*
769
 *   PGN 128267: NAV Water Depth
770
 */
771
static gps_mask_t hnd_128267(struct gps_device_t *session)
772
0
{
773
0
    unsigned char *bu = session->lexer.outbuffer;
774
775
0
    unsigned sid = bu[0];
776
0
    double offset= getleu16(bu, 5) / 1000.0;
777
0
    unsigned range = bu[7];
778
0
    session->gpsdata.attitude.depth = getleu32(bu, 1) / 100.0 ;
779
780
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
781
0
             "NMEA2000: pgn 128267 sid %u depth %.2f offset %.3f range %u\n",
782
0
             sid, session->gpsdata.attitude.depth, offset, range);
783
0
    return ONLINE_SET | ATTITUDE_SET;
784
0
}
785
786
787
/*
788
 *   PGN 128275: NAV Distance Log
789
 */
790
static gps_mask_t hnd_128275(struct gps_device_t *session UNUSED)
791
0
{
792
0
    return 0;
793
0
}
794
795
796
/*
797
 *   PGN 129283: NAV Cross Track Error
798
 */
799
static gps_mask_t hnd_129283(struct gps_device_t *session UNUSED)
800
0
{
801
0
    return 0;
802
0
}
803
804
805
/*
806
 *   PGN 129284: NAV Navigation Data
807
 */
808
static gps_mask_t hnd_129284(struct gps_device_t *session UNUSED)
809
0
{
810
0
    return 0;
811
0
}
812
813
814
/*
815
 *   PGN 129285: NAV Navigation - Route/WP Information
816
 */
817
static gps_mask_t hnd_129285(struct gps_device_t *session UNUSED)
818
0
{
819
0
    return 0;
820
0
}
821
822
823
/*
824
 *   PGN 129025: GNSS Position Rapid Update
825
 */
826
static gps_mask_t hnd_129025(struct gps_device_t *session)
827
0
{
828
0
    unsigned char *bu = session->lexer.outbuffer;
829
830
0
    session->newdata.latitude = getles32(bu, 0) * 1e-7;
831
0
    session->newdata.longitude = getles32(bu, 4) * 1e-7;
832
833
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
834
0
             "NMEA2000: pgn 129025 lat %.4f lon %.4f\n",
835
0
             session->newdata.latitude,
836
0
             session->newdata.longitude);
837
0
    return LATLON_SET | get_mode(session);
838
0
}
839
840
841
static const struct vlist_t cog_refs[] = {
842
    {0, "True"},
843
    {1, "Magnetic"},
844
    {2, "Error"},
845
    {0, NULL},
846
};
847
848
/*
849
 *   PGN 129026: GNSS COG and SOG Rapid Update
850
 */
851
static gps_mask_t hnd_129026(struct gps_device_t *session)
852
0
{
853
0
    unsigned char *bu = session->lexer.outbuffer;
854
855
0
    unsigned cog_ref = bu[1] & 0x03;
856
0
    session->driver.nmea2000.sid[0] = bu[0];
857
858
0
    session->newdata.track = getleu16(bu, 2) * 1e-4 * RAD_2_DEG;
859
0
    session->newdata.speed = getleu16(bu, 4) * 1e-2;
860
861
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
862
0
             "NMEA2000: pgn 129026 sid %u ref %u (%s) COG %.3f SOG %.3f\n",
863
0
             session->driver.nmea2000.sid[0], cog_ref,
864
0
             val2str(cog_ref, cog_refs),
865
0
             session->newdata.track, session->newdata.speed);
866
0
    return SPEED_SET | TRACK_SET | get_mode(session);
867
0
}
868
869
static const struct vlist_t integritys[] = {
870
    {0, "No integrity checking"},
871
    {1, "Safe"},
872
    {2, "Caution"},
873
    {3, "Unsafe"},
874
    {0, NULL}
875
};
876
877
static const struct vlist_t gns_methods[] = {
878
    {0, "no GNSS"},
879
    {1, "GNSS fix"},
880
    {2, "DGNSS fix"},
881
    {3, "Precise GNSS"},
882
    {4, "RTK Fixed Integer"},
883
    {5, "RTK float"},
884
    {6, "Estimated (DR) mode"},
885
    {7, "Manual Input"},
886
    {8, "Simulate mode"},
887
    {0, NULL}
888
};
889
890
static const struct vlist_t gnss_types[] = {
891
    {0, "GPS"},
892
    {1, "GLONASS"},
893
    {2, "GPS+GLONASS"},
894
    {3, "GPS+SBAS/WAAS"},
895
    {4, "GPS+SBAS/WAAS+GLONASS"},
896
    {5, "Chayka"},
897
    {6, "integrated"},
898
    {7, "surveyed"},
899
    {8, "Galileo"},
900
    {0, NULL}
901
};
902
903
/*
904
 * PGN: 129029 / 00374005 / 1F805 - 51 - GNSS Position Data
905
 *
906
 *      The last 3 fields repeat until the data is exhausted.
907
 *
908
 *   Field #1: SID
909
 *                   Bits: 8
910
 *                   Signed: false
911
 *   Field #2: Date - Days since January 1, 1970
912
 *                   Bits: 16
913
 *                   Units: days
914
 *                   Type: Date
915
 *                   Resolution: 1
916
 *                   Signed: false
917
 *   Field #3: Time - Seconds since midnight
918
 *                   Bits: 32
919
 *                   Units: s
920
 *                   Type: Time
921
 *                   Resolution: 0.0001
922
 *                   Signed: false
923
 *   Field #4: Latitude
924
 *                   Bits: 64
925
 *                   Units: deg
926
 *                   Type: Latitude
927
 *                   Resolution: 0.0000000000000001
928
 *                   Signed: true
929
 *   Field #5: Longitude
930
 *                   Bits: 64
931
 *                   Units: deg
932
 *                   Type: Longitude
933
 *                   Resolution: 0.0000000000000001
934
 *                   Signed: true
935
 *   Field #6: Altitude - Altitude referenced to WGS-84
936
 *                   Bits: 64
937
 *                   Units: m
938
 *                   Resolution: 1e-06
939
 *                   Signed: true
940
 *   Field #7: GNSS type
941
 *                   Bits: 4
942
 *                   Type: Lookup table
943
 *                   Signed: false
944
 *                   Lookup: 0=GPS
945
 *                   Lookup: 1=GLONASS
946
 *                   Lookup: 2=GPS+GLONASS
947
 *                   Lookup: 3=GPS+SBAS/WAAS
948
 *                   Lookup: 4=GPS+SBAS/WAAS+GLONASS
949
 *                   Lookup: 5=Chayka
950
 *                   Lookup: 6=integrated
951
 *                   Lookup: 7=surveyed
952
 *                   Lookup: 8=Galileo
953
 *   Field #8: Method
954
 *                   Bits: 4
955
 *                   Type: Lookup table
956
 *                   Signed: false
957
 *                   Lookup: 0=no GNSS
958
 *                   Lookup: 1=GNSS fix
959
 *                   Lookup: 2=DGNSS fix
960
 *                   Lookup: 3=Precise GNSS
961
 *                   Lookup: 4=RTK Fixed Integer
962
 *                   Lookup: 5=RTK float
963
 *                   Lookup: 6=Estimated (DR) mode
964
 *                   Lookup: 7=Manual Input
965
 *                   Lookup: 8=Simulate mode
966
 *   Field #9: Integrity
967
 *                   Bits: 2
968
 *                   Type: Lookup table
969
 *                   Signed: false
970
 *                   Lookup: 0=No integrity checking
971
 *                   Lookup: 1=Safe
972
 *                   Lookup: 2=Caution
973
 *   Field #10: Reserved - Reserved
974
 *                   Bits: 6
975
 *                   Type: Binary data
976
 *                   Signed: false
977
 *   Field #11: Number of SVs - Number of satellites used in solution
978
 *                   Bits: 8
979
 *                   Signed: false
980
 *   Field #12: HDOP - Horizontal dilution of precision
981
 *                   Bits: 16
982
 *                   Resolution: 0.01
983
 *                   Signed: true
984
 *   Field #13: PDOP - Probable dilution of precision
985
 *                   Bits: 16
986
 *                   Resolution: 0.01
987
 *                   Signed: true
988
 *   Field #14: Geoidal Separation - Geoidal Separation
989
 *                   Bits: 32
990
 *                   Units: m
991
 *                   Resolution: 0.01
992
 *                   Signed: true
993
 *   Field #15: Reference Stations - Number of reference stations
994
 *                   Bits: 8
995
 *                   Signed: false
996
 *   Field #16: Reference Station Type
997
 *                   Bits: 4
998
 *                   Type: Lookup table
999
 *                   Signed: false
1000
 *                   Lookup: 0=GPS
1001
 *                   Lookup: 1=GLONASS
1002
 *                   Lookup: 2=GPS+GLONASS
1003
 *                   Lookup: 3=GPS+SBAS/WAAS
1004
 *                   Lookup: 4=GPS+SBAS/WAAS+GLONASS
1005
 *                   Lookup: 5=Chayka
1006
 *                   Lookup: 6=integrated
1007
 *                   Lookup: 7=surveyed
1008
 *                   Lookup: 8=Galileo
1009
 *   Field #17: Reference Station ID
1010
 *                   Bits: 12
1011
 *                   Units:
1012
 *                   Signed: false
1013
 *   Field #18: Age of DGNSS Corrections
1014
 *                   Bits: 16
1015
 *                   Units: s
1016
 *                   Resolution: 0.01
1017
 *                   Signed: false
1018
 *
1019
 */
1020
static gps_mask_t hnd_129029(struct gps_device_t *session)
1021
0
{
1022
0
    unsigned char *bu = session->lexer.outbuffer;
1023
0
    gps_mask_t mask = 0;
1024
0
    uint64_t usecs;                           // time of day in us
1025
0
    unsigned gns_method = (bu[31] >> 4) & 0x0f;
1026
0
    unsigned gnss_type = bu[31] & 0x0f;
1027
0
    unsigned integrity = bu[32] & 0x03;
1028
0
    unsigned refs = bu[40];
1029
1030
0
    session->driver.nmea2000.sid[3]  = bu[0];
1031
1032
    // field 3 is time of day in 0.1 ms
1033
0
    usecs = getleu32(bu, 3) * (uint64_t)100;
1034
0
    USTOTS(&session->newdata.time, usecs);
1035
    // add in the date from field 2
1036
0
    session->newdata.time.tv_sec += (time_t)(getleu16(bu,1) * 24 * 60 * 60);
1037
0
    mask |= TIME_SET;
1038
1039
0
    session->newdata.latitude = getles64(bu, 7) * 1e-16;
1040
0
    session->newdata.longitude = getles64(bu, 15) * 1e-16;
1041
0
    mask |= LATLON_SET;
1042
1043
0
    session->newdata.altHAE = getles64(bu, 23) * 1e-6;
1044
0
    mask |= ALTITUDE_SET;
1045
1046
0
    switch (gns_method) {
1047
0
    case 0:
1048
0
        session->newdata.status = STATUS_UNK;
1049
0
        break;
1050
0
    case 1:
1051
0
        session->newdata.status = STATUS_GPS;
1052
0
        break;
1053
0
    case 2:
1054
0
        session->newdata.status = STATUS_DGPS;
1055
0
        break;
1056
0
    case 3:
1057
0
        session->newdata.status = STATUS_PPS_FIX;
1058
0
        break;
1059
0
    case 4:
1060
0
        session->newdata.status = STATUS_RTK_FIX;
1061
0
        break;
1062
0
    case 5:
1063
0
        session->newdata.status = STATUS_RTK_FLT;
1064
0
        break;
1065
0
    case 6:
1066
0
        session->newdata.status = STATUS_DR;
1067
0
        break;
1068
0
    case 7:
1069
0
        session->newdata.status = STATUS_TIME;
1070
0
        break;
1071
0
    case 8:
1072
0
        session->newdata.status = STATUS_SIM;
1073
0
        break;
1074
0
    default:
1075
0
        session->newdata.status = STATUS_UNK;
1076
0
        break;
1077
0
    }
1078
0
    mask |= STATUS_SET;
1079
1080
0
    session->newdata.geoid_sep = getles32(bu, 38) / 100.0;
1081
1082
0
    session->gpsdata.satellites_used = (int)bu[33];
1083
1084
0
    session->gpsdata.dop.hdop = getleu16(bu, 34) * 1e-2;
1085
0
    session->gpsdata.dop.pdop = getleu16(bu, 36) * 1e-2;
1086
0
    mask |= DOP_SET;
1087
1088
0
    session->newdata.geoid_sep = getles32(bu, 38) / 100;
1089
1090
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
1091
0
             "NMEA2000: pgn 129029 SA %u sid %u lat %.2f lon %.2f HAE %.2f "
1092
0
             "type %u(%s) method %u(%s) integrity %u(%s) "
1093
0
             "hdop:%5.2f pdop:%5.2f sep %.2f refs %u\n",
1094
0
             session->driver.nmea2000.source_addr,
1095
0
             session->driver.nmea2000.sid[3],
1096
0
             session->newdata.latitude,
1097
0
             session->newdata.longitude,
1098
0
             session->newdata.altHAE,
1099
0
             gnss_type, val2str(gnss_type, gnss_types),
1100
0
             gns_method, val2str(gns_method, gns_methods),
1101
0
             integrity, val2str(integrity, integritys),
1102
0
             session->gpsdata.dop.hdop,
1103
0
             session->gpsdata.dop.pdop,
1104
0
             session->newdata.geoid_sep, refs);
1105
0
    return mask | get_mode(session);
1106
0
}
1107
1108
1109
/*
1110
 *   PGN 129038: AIS Class A Position Report
1111
 */
1112
static gps_mask_t hnd_129038(struct gps_device_t *session)
1113
0
{
1114
0
    unsigned char *bu = session->lexer.outbuffer;
1115
0
    size_t len  = session->lexer.outbuflen;
1116
0
    struct ais_t *ais =  &session->gpsdata.ais;
1117
1118
0
    if (0 != decode_ais_header(session->context, bu, len, ais, 0xffffffffU)) {
1119
0
        ais->type1.lon = (int)scale_int(getles32(bu, 5),
1120
0
                                        (int64_t)(SHIFT32 *.06L));
1121
0
        ais->type1.lat = (int)scale_int(getles32(bu, 9),
1122
0
                                        (int64_t)(SHIFT32 *.06L));
1123
0
        ais->type1.accuracy  = (bool)         ((bu[13] >> 0) & 0x01);
1124
0
        ais->type1.raim      = (bool)         ((bu[13] >> 1) & 0x01);
1125
0
        ais->type1.second    = (unsigned int) ((bu[13] >> 2) & 0x3f);
1126
0
        ais->type1.course = (unsigned int)ais_direction(
1127
0
                                       (unsigned int)getleu16(bu, 14), 10.0);
1128
0
        ais->type1.speed = (unsigned int)(getleu16(bu, 16) *
1129
0
                                          MPS_TO_KNOTS * 0.01 / 0.1);
1130
0
        ais->type1.radio     = (unsigned int) (getleu32(bu, 18) & 0x7ffff);
1131
0
        ais->type1.heading =
1132
0
            (unsigned int)ais_direction((unsigned int)getleu16(bu, 21), 1.0);
1133
0
        ais->type1.turn = ais_turn_rate((int)getles16(bu, 23));
1134
0
        ais->type1.status    = (unsigned int) ((bu[25] >> 0) & 0x0f);
1135
0
        ais->type1.maneuver  = 0;  // Not transmitted ????
1136
0
        decode_ais_channel_info(bu, len, 163, session);
1137
1138
0
        return ONLINE_SET | AIS_SET;
1139
0
    }
1140
0
    return 0;
1141
0
}
1142
1143
1144
/*
1145
 *   PGN 129039: AIS Class B Position Report
1146
 */
1147
static gps_mask_t hnd_129039(struct gps_device_t *session)
1148
0
{
1149
0
    unsigned char *bu = session->lexer.outbuffer;
1150
0
    size_t len  = session->lexer.outbuflen;
1151
0
    struct ais_t *ais =  &session->gpsdata.ais;
1152
1153
0
    if (0 != decode_ais_header(session->context, bu, len, ais, 0xffffffffU)) {
1154
0
        ais->type18.lon = (int)scale_int(getles32(bu, 5),
1155
0
                                         (int64_t)(SHIFT32 *.06L));
1156
0
        ais->type18.lat = (int)scale_int(getles32(bu, 9),
1157
0
                                         (int64_t)(SHIFT32 *.06L));
1158
0
        ais->type18.accuracy = (bool)         ((bu[13] >> 0) & 0x01);
1159
0
        ais->type18.raim     = (bool)         ((bu[13] >> 1) & 0x01);
1160
0
        ais->type18.second   = (unsigned int) ((bu[13] >> 2) & 0x3f);
1161
0
        ais->type18.course =
1162
0
            (unsigned int)ais_direction((unsigned int) getleu16(bu, 14), 10.0);
1163
0
        ais->type18.speed = (unsigned int)(getleu16(bu, 16) *
1164
0
                                           MPS_TO_KNOTS * 0.01 / 0.1);
1165
0
        ais->type18.radio    = (unsigned int) (getleu32(bu, 18) & 0x7ffff);
1166
0
        ais->type18.heading =
1167
0
            (unsigned int)ais_direction((unsigned int) getleu16(bu, 21), 1.0);
1168
0
        ais->type18.reserved = 0;
1169
0
        ais->type18.regional = (unsigned int) ((bu[24] >> 0) & 0x03);
1170
0
        ais->type18.cs       = (bool)         ((bu[24] >> 2) & 0x01);
1171
0
        ais->type18.display  = (bool)         ((bu[24] >> 3) & 0x01);
1172
0
        ais->type18.dsc      = (bool)         ((bu[24] >> 4) & 0x01);
1173
0
        ais->type18.band     = (bool)         ((bu[24] >> 5) & 0x01);
1174
0
        ais->type18.msg22    = (bool)         ((bu[24] >> 6) & 0x01);
1175
0
        ais->type18.assigned = (bool)         ((bu[24] >> 7) & 0x01);
1176
0
        decode_ais_channel_info(bu, len, 163, session);
1177
1178
0
        return ONLINE_SET | AIS_SET;
1179
0
    }
1180
0
    return 0;
1181
0
}
1182
1183
1184
/*
1185
 *   PGN 129040: AIS Class B Extended Position Report
1186
 *
1187
 *  No test case for this message at the moment
1188
 */
1189
static gps_mask_t hnd_129040(struct gps_device_t *session)
1190
0
{
1191
0
    unsigned char *bu = session->lexer.outbuffer;
1192
0
    size_t len  = session->lexer.outbuflen;
1193
0
    struct ais_t *ais =  &session->gpsdata.ais;
1194
1195
0
    if (0 != decode_ais_header(session->context, bu, len, ais, 0xffffffffU)) {
1196
0
        uint16_t length, beam, to_bow, to_starboard;
1197
1198
0
        ais->type19.lon = (int)scale_int(getles32(bu, 5),
1199
0
                                         (int64_t)(SHIFT32 *.06L));
1200
0
        ais->type19.lat = (int)scale_int(getles32(bu, 9),
1201
0
                                         (int64_t)(SHIFT32 *.06L));
1202
0
        ais->type19.accuracy     = (bool)         ((bu[13] >> 0) & 0x01);
1203
0
        ais->type19.raim         = (bool)         ((bu[13] >> 1) & 0x01);
1204
0
        ais->type19.second       = (unsigned int) ((bu[13] >> 2) & 0x3f);
1205
0
        ais->type19.course =
1206
0
            (unsigned int)ais_direction((unsigned int)getleu16(bu, 14), 10.0);
1207
0
        ais->type19.speed =
1208
0
            (unsigned int)(getleu16(bu, 16) * MPS_TO_KNOTS * 0.01 / 0.1);
1209
0
        ais->type19.reserved     = (unsigned int) ((bu[18] >> 0) & 0xff);
1210
0
        ais->type19.regional     = (unsigned int) ((bu[19] >> 0) & 0x0f);
1211
0
        ais->type19.shiptype     = (unsigned int) ((bu[20] >> 0) & 0xff);
1212
0
        ais->type19.heading =
1213
0
           (unsigned int)  ais_direction((unsigned int) getleu16(bu, 21), 1.0);
1214
0
        length                   =                 getleu16(bu, 24);
1215
0
        beam                     =                 getleu16(bu, 26);
1216
0
        to_starboard             =                 getleu16(bu, 28);
1217
0
        to_bow                   =                 getleu16(bu, 30);
1218
0
        if ((0xffff == length) ||
1219
0
            (0xffff == to_bow)) {
1220
0
            length       = 0;
1221
0
            to_bow       = 0;
1222
0
        }
1223
0
        if ((0xffff == beam) ||
1224
0
            (0xffff == to_starboard)) {
1225
0
            beam         = 0;
1226
0
            to_starboard = 0;
1227
0
        }
1228
0
        ais->type19.to_bow       = (unsigned int) (to_bow / 10);
1229
0
        ais->type19.to_stern     = (unsigned int) ((length-to_bow) / 10);
1230
0
        ais->type19.to_port      = (unsigned int) ((beam-to_starboard) / 10);
1231
0
        ais->type19.to_starboard = (unsigned int) (to_starboard / 10);
1232
0
        ais->type19.epfd         = (unsigned int) ((bu[23] >> 4) & 0x0f);
1233
0
        ais->type19.dte          = (unsigned int) ((bu[52] >> 0) & 0x01);
1234
0
        ais->type19.assigned     = (bool)         ((bu[52] >> 1) & 0x01);
1235
0
        strlcpy(ais->type19.shipname, (char *)&bu[32],
1236
0
                sizeof(ais->type19.shipname));
1237
0
        decode_ais_channel_info(bu, len, 422, session);
1238
1239
0
        return ONLINE_SET | AIS_SET;
1240
0
    }
1241
0
    return 0;
1242
0
}
1243
1244
1245
static const int mode_tab[] = {MODE_NO_FIX, MODE_2D, MODE_3D, MODE_NO_FIX,
1246
                               MODE_NO_FIX, MODE_NO_FIX, MODE_NO_FIX,
1247
                               MODE_NO_FIX};
1248
1249
static const struct vlist_t gnss_modes[] = {
1250
    {0, "1D"},
1251
    {1, "2D"},
1252
    {2, "3D"},
1253
    {3, "Auto"},
1254
    {0, NULL},
1255
};
1256
1257
/*
1258
 *   PGN 129539: GNSS DOPs
1259
 */
1260
static gps_mask_t hnd_129539(struct gps_device_t *session)
1261
0
{
1262
0
    unsigned char *bu = session->lexer.outbuffer;
1263
0
    gps_mask_t mask = 0;
1264
0
    unsigned int req_mode;
1265
0
    unsigned int act_mode;
1266
1267
0
    session->driver.nmea2000.sid[1]  = bu[0];
1268
1269
0
    session->driver.nmea2000.mode_valid |= 1;
1270
1271
0
    req_mode = (unsigned int)((bu[1] >> 0) & 0x07);
1272
0
    act_mode = (unsigned int)((bu[1] >> 3) & 0x07);
1273
1274
    /* This is a workaround for some GARMIN plotter,
1275
     * actual mode auto makes no sense for me! */
1276
0
    if ((3 == act_mode) &&
1277
0
        (3 != req_mode)) {
1278
0
        act_mode = req_mode;
1279
0
    }
1280
1281
0
    session->driver.nmea2000.mode    = mode_tab[act_mode];
1282
1283
0
    session->gpsdata.dop.hdop        = getleu16(bu, 2) * 1e-2;
1284
0
    session->gpsdata.dop.vdop        = getleu16(bu, 4) * 1e-2;
1285
0
    session->gpsdata.dop.tdop        = getleu16(bu, 6) * 1e-2;
1286
0
    mask                            |= DOP_SET;
1287
1288
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
1289
0
             "NMEA2000: pgn 129539 SA %u sid %u req %u(%s) act %u(%s) "
1290
0
             "hdop %5.2f vdop %5.2f tdop %5.2f\n",
1291
0
             session->driver.nmea2000.source_addr,
1292
0
             session->driver.nmea2000.sid[1],
1293
0
             req_mode, val2str(req_mode, gnss_modes),
1294
0
             act_mode, val2str(act_mode, gnss_modes),
1295
0
             session->gpsdata.dop.hdop,
1296
0
             session->gpsdata.dop.vdop,
1297
0
             session->gpsdata.dop.tdop);
1298
1299
0
    return mask | get_mode(session);
1300
0
}
1301
1302
static const struct vlist_t range_modes[] = {
1303
    {0, "Range residuals used"},
1304
    {1, "Range residuals calculated"},
1305
    {0, NULL}
1306
};
1307
1308
static const struct vlist_t svts[] = {
1309
    {0, "Not tracked"},
1310
    {1, "Tracked"},
1311
    {2, "Used"},
1312
    {3, "Not tracked+Diff"},
1313
    {4, "Tracked+Diff"},
1314
    {5, "Used+Diff"},
1315
    {0, NULL}
1316
};
1317
1318
/*
1319
 *   PGN 129540: GNSS Satellites in View
1320
 */
1321
static gps_mask_t hnd_129540(struct gps_device_t *session)
1322
0
{
1323
0
    unsigned char *bu = session->lexer.outbuffer;
1324
0
    size_t len  = session->lexer.outbuflen;
1325
0
    int    l1;
1326
0
    size_t expected_len;
1327
0
    unsigned range_mode = bu[1] & 0x03;
1328
1329
0
    session->driver.nmea2000.sid[2]           = bu[0];
1330
0
    session->gpsdata.satellites_visible       = (int)bu[2];
1331
0
    if (MAXCHANNELS <= session->gpsdata.satellites_visible) {
1332
        // Handle a CVE for overrunning skyview[]
1333
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
1334
0
                 "NMEA2000: pgn 129540 SA %u Too many sats %d\n",
1335
0
                 session->driver.nmea2000.source_addr,
1336
0
                 session->gpsdata.satellites_visible);
1337
0
        session->gpsdata.satellites_visible = MAXCHANNELS;
1338
0
    }
1339
0
    expected_len = 3 + (12 * session->gpsdata.satellites_visible);
1340
0
    if (len != expected_len) {
1341
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
1342
0
                 "NMEA2000: pgn 129540 SA %u wrong length %zu s/b %zu\n",
1343
0
                 session->driver.nmea2000.source_addr,
1344
0
                 len, expected_len);
1345
0
        return 0;
1346
0
    }
1347
1348
0
    memset(session->gpsdata.skyview, '\0', sizeof(session->gpsdata.skyview));
1349
0
    for (l1 = 0; l1 < session->gpsdata.satellites_visible; l1++) {
1350
0
        int offset = 3 + (12 * l1);
1351
0
        double elev  = getles16(bu, offset + 1) * 1e-4 * RAD_2_DEG;
1352
0
        double azi   = getleu16(bu, offset + 3) * 1e-4 * RAD_2_DEG;
1353
0
        double snr   = getles16(bu, offset + 5) * 1e-2;
1354
1355
0
        unsigned svt = bu[offset + 11] & 0x0f;
1356
1357
0
        session->gpsdata.skyview[l1].elevation  = elev;
1358
0
        session->gpsdata.skyview[l1].azimuth    = azi;
1359
0
        session->gpsdata.skyview[l1].ss         = snr;
1360
0
        session->gpsdata.skyview[l1].PRN        = (int16_t)bu[offset];
1361
0
        session->gpsdata.skyview[l1].used = false;
1362
0
        if ((2 == svt) ||
1363
0
            (5 == svt)) {
1364
0
            session->gpsdata.skyview[l1].used = true;
1365
0
        }
1366
0
        GPSD_LOG(LOG_IO, &session->context->errout,
1367
0
                 "NMEA2000: pgn 129540 PRN %d svt %u(%s)\n",
1368
0
                 session->gpsdata.skyview[l1].PRN,
1369
0
                 svt, val2str(svt, svts));
1370
0
    }
1371
0
    session->driver.nmea2000.mode_valid |= 2;
1372
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
1373
0
             "NMEA2000: pgn 129540 SA %u sid %u mode %u(%s) seen %u\n",
1374
0
             session->driver.nmea2000.source_addr,
1375
0
             session->driver.nmea2000.sid[2],
1376
0
             range_mode, val2str(range_mode, range_modes),
1377
0
             session->gpsdata.satellites_visible);
1378
0
    return SATELLITE_SET | USED_IS;
1379
0
}
1380
1381
1382
/*
1383
 *   PGN 129793: AIS UTC and Date Report
1384
 */
1385
static gps_mask_t hnd_129793(struct gps_device_t *session)
1386
0
{
1387
0
    unsigned char *bu = session->lexer.outbuffer;
1388
0
    size_t len  = session->lexer.outbuflen;
1389
0
    struct ais_t *ais =  &session->gpsdata.ais;
1390
1391
0
    if (0 != decode_ais_header(session->context, bu, len, ais, 0xffffffffU)) {
1392
0
        uint32_t  time;
1393
0
        uint32_t  date;
1394
0
        time_t    date1;
1395
0
        struct tm date2;
1396
1397
0
        ais->type4.lon = (int)scale_int(getles32(bu, 5),
1398
0
                                        (int64_t)(SHIFT32 *.06L));
1399
0
        ais->type4.lat = (int)scale_int(getles32(bu, 9),
1400
0
                                        (int64_t)(SHIFT32 *.06L));
1401
0
        ais->type4.accuracy     = (bool)         ((bu[13] >> 0) & 0x01);
1402
0
        ais->type4.raim         = (bool)         ((bu[13] >> 1) & 0x01);
1403
1404
0
        time = getleu32(bu, 14);
1405
0
        if (0xffffffff != time) {
1406
0
            time                = time / 10000;
1407
0
            ais->type4.second   = time % 60; time = time / 60;
1408
0
            ais->type4.minute   = time % 60; time = time / 60;
1409
0
            ais->type4.hour     = time % 24;
1410
0
        } else {
1411
0
            ais->type4.second   = AIS_SECOND_NOT_AVAILABLE;
1412
0
            ais->type4.minute   = AIS_MINUTE_NOT_AVAILABLE;
1413
0
            ais->type4.hour     = AIS_HOUR_NOT_AVAILABLE;
1414
0
        }
1415
1416
0
        ais->type4.radio        = (unsigned int) (getleu32(bu, 18) & 0x7ffff);
1417
1418
0
        date = getleu16(bu, 21);
1419
0
        if (0xffff != date) {
1420
0
            date1 = (time_t)date * (24L *60L *60L);
1421
0
            (void) gmtime_r(&date1, &date2);
1422
0
            ais->type4.year     = (unsigned int) (date2.tm_year + 1900);
1423
0
            ais->type4.month    = (unsigned int) (date2.tm_mon + 1);
1424
0
            ais->type4.day      = (unsigned int) (date2.tm_mday);
1425
0
        } else {
1426
0
            ais->type4.day      = AIS_DAY_NOT_AVAILABLE;
1427
0
            ais->type4.month    = AIS_MONTH_NOT_AVAILABLE;
1428
0
            ais->type4.year     = AIS_YEAR_NOT_AVAILABLE;
1429
0
        }
1430
1431
0
        ais->type4.epfd         = (unsigned int) ((bu[23] >> 4) & 0x0f);
1432
1433
0
        decode_ais_channel_info(bu, len, 163, session);
1434
1435
0
        return ONLINE_SET | AIS_SET;
1436
0
    }
1437
0
    return 0;
1438
0
}
1439
1440
1441
/*
1442
 *   PGN 129794: AIS Class A Static and Voyage Related Data
1443
 */
1444
static gps_mask_t hnd_129794(struct gps_device_t *session)
1445
0
{
1446
0
    unsigned char *bu = session->lexer.outbuffer;
1447
0
    size_t len  = session->lexer.outbuflen;
1448
0
    struct ais_t *ais =  &session->gpsdata.ais;
1449
1450
0
    if (0 != decode_ais_header(session->context, bu, len, ais, 0xffffffffU)) {
1451
0
        uint16_t  length, beam, to_bow, to_starboard, date;
1452
0
        int       l;
1453
0
        uint32_t  time;
1454
0
        time_t    date1;
1455
0
        struct tm date2;
1456
0
        int       cpy_stop;
1457
1458
0
        ais->type5.ais_version   = (unsigned int) ((bu[73] >> 0) & 0x03);
1459
0
        ais->type5.imo           = (unsigned int)  getleu32(bu,  5);
1460
0
        if (0xffffffffU == ais->type5.imo) {
1461
0
            ais->type5.imo       = 0;
1462
0
        }
1463
0
        ais->type5.shiptype      = (unsigned int) ((bu[36] >> 0) & 0xff);
1464
0
        length                   =                 getleu16(bu, 37);
1465
0
        beam                     =                 getleu16(bu, 39);
1466
0
        to_starboard             =                 getleu16(bu, 41);
1467
0
        to_bow                   =                 getleu16(bu, 43);
1468
0
        if ((0xffff == length) ||
1469
0
            (0xffff == to_bow)) {
1470
0
            length       = 0;
1471
0
            to_bow       = 0;
1472
0
        }
1473
0
        if ((0xffff == beam) ||
1474
0
            (0xffff == to_starboard)) {
1475
0
            beam         = 0;
1476
0
            to_starboard = 0;
1477
0
        }
1478
0
        ais->type5.to_bow        = (unsigned int) (to_bow/10);
1479
0
        ais->type5.to_stern      = (unsigned int) ((length-to_bow) / 10);
1480
0
        ais->type5.to_port       = (unsigned int) ((beam-to_starboard) / 10);
1481
0
        ais->type5.to_starboard  = (unsigned int) (to_starboard / 10);
1482
0
        ais->type5.epfd          = (unsigned int) ((bu[73] >> 2) & 0x0f);
1483
0
        date                     =                 getleu16(bu, 45);
1484
0
        time                     =                 getleu32(bu, 47);
1485
0
        date1                    = (time_t)       (date * 24 * 60 * 60);
1486
0
        (void) gmtime_r(&date1, &date2);
1487
0
        ais->type5.month         = (unsigned int) (date2.tm_mon + 1);
1488
0
        ais->type5.day           = (unsigned int) (date2.tm_mday);
1489
0
        ais->type5.minute        = (unsigned int) (time/(10000 * 60));
1490
0
        ais->type5.hour          = (unsigned int) (ais->type5.minute / 60);
1491
0
        ais->type5.minute =
1492
0
            (unsigned int)(ais->type5.minute-(ais->type5.hour * 60));
1493
1494
0
        ais->type5.draught       = (unsigned int) (getleu16(bu, 51) / 10);
1495
0
        ais->type5.dte           = (unsigned int) ((bu[73] >> 6) & 0x01);
1496
1497
0
        for (l = 0, cpy_stop = 0; l < 7; l++) {
1498
0
            char next;
1499
1500
0
            next = (char) bu[9+l];
1501
0
            if ((' ' > next) ||
1502
0
                (0x7e < next)) {
1503
0
                cpy_stop = 1;
1504
0
            }
1505
0
            if (0 == cpy_stop) {
1506
0
                ais->type5.callsign[l] = next;
1507
0
            } else {
1508
0
                ais->type5.callsign[l] = 0;
1509
0
            }
1510
0
        }
1511
0
        ais->type5.callsign[7]   = (char) 0;
1512
1513
0
        for (l = 0, cpy_stop = 0; l < AIS_SHIPNAME_MAXLEN; l++) {
1514
0
            char next;
1515
1516
0
            next = (char) bu[16+l];
1517
0
            if ((next < ' ') ||
1518
0
                (next > 0x7e)) {
1519
0
                cpy_stop = 1;
1520
0
            }
1521
0
            if (cpy_stop == 0) {
1522
0
                ais->type5.shipname[l] = next;
1523
0
            } else {
1524
0
                ais->type5.shipname[l] = 0;
1525
0
            }
1526
0
        }
1527
0
        ais->type5.shipname[AIS_SHIPNAME_MAXLEN] = (char) 0;
1528
1529
0
        for (l = 0, cpy_stop = 0; l < 20; l++) {
1530
0
            char next;
1531
1532
0
            next = (char) bu[53+l];
1533
0
            if ((next < ' ') ||
1534
0
                (next > 0x7e)) {
1535
0
                cpy_stop = 1;
1536
0
            }
1537
0
            if (cpy_stop == 0) {
1538
0
                ais->type5.destination[l] = next;
1539
0
            } else {
1540
0
                ais->type5.destination[l] = 0;
1541
0
            }
1542
0
        }
1543
0
        ais->type5.destination[20] = (char) 0;
1544
#if NMEA2000_DEBUG_AIS
1545
        printf("AIS: MMSI:  %09u\n",
1546
               ais->mmsi);
1547
        printf("AIS: name:  %-20.20s i:%8u c:%-8.8s b:%6u s:%6u p:%6u"
1548
               "s:%6u dr:%4.1f\n",
1549
               ais->type5.shipname,
1550
               ais->type5.imo,
1551
               ais->type5.callsign,
1552
               ais->type5.to_bow,
1553
               ais->type5.to_stern,
1554
               ais->type5.to_port,
1555
               ais->type5.to_starboard,
1556
               ais->type5.draught / 10.0);
1557
        printf("AIS: arrival:%-20.20s at %02u-%02u-%04d %02u:%0u\n",
1558
               ais->type5.destination,
1559
               ais->type5.day,
1560
               ais->type5.month,
1561
               date2.tm_year + 1900,
1562
               ais->type5.hour,
1563
               ais->type5.minute);
1564
#endif  // end of #if NMEA2000_DEBUG_AIS
1565
0
        decode_ais_channel_info(bu, len, 592, session);
1566
0
        return ONLINE_SET | AIS_SET;
1567
0
    }
1568
0
    return 0;
1569
0
}
1570
1571
1572
/*
1573
 *   PGN 129798: AIS SAR Aircraft Position Report
1574
 *
1575
 * No test case for this message at the moment
1576
 */
1577
static gps_mask_t hnd_129798(struct gps_device_t *session)
1578
0
{
1579
0
    unsigned char *bu = session->lexer.outbuffer;
1580
0
    size_t len  = session->lexer.outbuflen;
1581
0
    struct ais_t *ais =  &session->gpsdata.ais;
1582
1583
0
    if (0 != decode_ais_header(session->context, bu, len, ais, 0xffffffffU)) {
1584
0
        ais->type9.lon = (int)scale_int(getles32(bu, 5),
1585
0
                                        (int64_t)(SHIFT32 *.06L));
1586
0
        ais->type9.lat = (int)scale_int(getles32(bu, 9),
1587
0
                                        (int64_t)(SHIFT32 *.06L));
1588
0
        ais->type9.accuracy  = (bool)         ((bu[13] >> 0) & 0x01);
1589
0
        ais->type9.raim      = (bool)         ((bu[13] >> 1) & 0x01);
1590
0
        ais->type9.second    = (unsigned int) ((bu[13] >> 2) & 0x3f);
1591
0
        ais->type9.course =
1592
0
            (unsigned int)ais_direction((unsigned int)getleu16(bu, 14), 10.0);
1593
0
        ais->type9.speed =
1594
0
            (unsigned int)(getleu16(bu, 16) * MPS_TO_KNOTS * 0.01 / 0.1);
1595
0
        ais->type9.radio     = (unsigned int) (getleu32(bu, 18) & 0x7ffff);
1596
0
        ais->type9.alt       = (unsigned int) (getleu64(bu, 21)/1000000);
1597
0
        ais->type9.regional  = (unsigned int) ((bu[29] >> 0) & 0xff);
1598
0
        ais->type9.dte       = (unsigned int) ((bu[30] >> 0) & 0x01);
1599
//      ais->type9.spare     = (bu[30] >> 1) & 0x7f;
1600
0
        ais->type9.assigned  = 0;  // Not transmitted ????
1601
0
        decode_ais_channel_info(bu, len, 163, session);
1602
1603
0
        return ONLINE_SET | AIS_SET;
1604
0
    }
1605
0
    return 0;
1606
0
}
1607
1608
1609
/*
1610
 *   PGN 129802: AIS Safety Related Broadcast Message
1611
 *
1612
 * No test case for this message at the moment
1613
 */
1614
static gps_mask_t hnd_129802(struct gps_device_t *session)
1615
0
{
1616
0
    unsigned char *bu = session->lexer.outbuffer;
1617
0
    size_t len  = session->lexer.outbuflen;
1618
0
    struct ais_t *ais =  &session->gpsdata.ais;
1619
1620
0
    if (0 != decode_ais_header(session->context, bu, len, ais, 0x3fffffff)) {
1621
//      ais->type14.channel = (bu[ 5] >> 0) & 0x1f;
1622
0
        strlcpy(ais->type14.text, (char *)&bu[6], sizeof(ais->type14.text));
1623
0
        decode_ais_channel_info(bu, len, 40, session);
1624
1625
0
        return ONLINE_SET | AIS_SET;
1626
0
    }
1627
0
    return 0;
1628
0
}
1629
1630
1631
/*
1632
 *   PGN 129809: AIS Class B CS Static Data Report, Part A
1633
 */
1634
static gps_mask_t hnd_129809(struct gps_device_t *session)
1635
0
{
1636
0
    unsigned char *bu = session->lexer.outbuffer;
1637
0
    size_t len  = session->lexer.outbuflen;
1638
0
    struct ais_t *ais =  &session->gpsdata.ais;
1639
1640
0
    if (0 != decode_ais_header(session->context, bu, len, ais, 0xffffffffU)) {
1641
0
        int index   = session->driver.aivdm.context[0].type24_queue.index;
1642
0
        struct ais_type24a_t *saveptr =
1643
0
            &session->driver.aivdm.context[0].type24_queue.ships[index];
1644
1645
0
        GPSD_LOG(LOG_PROG, &session->context->errout,
1646
0
                 "NMEA2000: AIS message 24A from %09u stashed.\n",
1647
0
                 ais->mmsi);
1648
1649
0
        strlcpy(ais->type24.shipname, (char *)&bu[5],
1650
0
                sizeof(ais->type24.shipname));
1651
0
        strlcpy(saveptr->shipname, (char *)&bu[5], sizeof(saveptr->shipname));
1652
1653
0
        saveptr->mmsi = ais->mmsi;
1654
1655
0
        index += 1;
1656
0
        index %= MAX_TYPE24_INTERLEAVE;
1657
0
        session->driver.aivdm.context[0].type24_queue.index = index;
1658
1659
0
        decode_ais_channel_info(bu, len, 200, session);
1660
1661
0
        ais->type24.part = part_a;
1662
0
        return ONLINE_SET | AIS_SET;
1663
0
    }
1664
0
    return 0;
1665
0
}
1666
1667
1668
/*
1669
 *   PGN 129810: AIS Class B CS Static Data Report, Part B
1670
 */
1671
static gps_mask_t hnd_129810(struct gps_device_t *session)
1672
0
{
1673
0
    unsigned char *bu = session->lexer.outbuffer;
1674
0
    size_t len  = session->lexer.outbuflen;
1675
0
    struct ais_t *ais =  &session->gpsdata.ais;
1676
1677
0
    if (0 != decode_ais_header(session->context, bu, len, ais, 0xffffffffU)) {
1678
0
        int i;
1679
1680
0
        ais->type24.shiptype = (unsigned int) ((bu[ 5] >> 0) & 0xff);
1681
1682
0
        strlcpy(ais->type24.vendorid, (char *)&bu[6],
1683
0
                sizeof(ais->type24.vendorid));
1684
0
        strlcpy(ais->type24.callsign, (char *)&bu[13],
1685
0
                sizeof(ais->type24.callsign));
1686
1687
0
        ais->type24.model = 0;
1688
0
        ais->type24.serial = 0;
1689
1690
0
        if (AIS_AUXILIARY_MMSI(ais->mmsi)) {
1691
0
            ais->type24.mothership_mmsi   = (unsigned int)getleu32(bu, 28);
1692
0
        } else {
1693
0
            uint16_t length, beam, to_bow, to_starboard;
1694
1695
0
            length                        =                 getleu16(bu, 20);
1696
0
            beam                          =                 getleu16(bu, 22);
1697
0
            to_starboard                  =                 getleu16(bu, 24);
1698
0
            to_bow                        =                 getleu16(bu, 26);
1699
0
            if ((length == 0xffff) || (to_bow       == 0xffff)) {
1700
0
                length       = 0;
1701
0
                to_bow       = 0;
1702
0
            }
1703
0
            if ((beam   == 0xffff) || (to_starboard == 0xffff)) {
1704
0
                beam         = 0;
1705
0
                to_starboard = 0;
1706
0
            }
1707
0
            ais->type24.dim.to_bow   = (unsigned int) (to_bow/10);
1708
0
            ais->type24.dim.to_stern = (unsigned int) ((length-to_bow)/10);
1709
0
            ais->type24.dim.to_port  = (unsigned int) ((beam-to_starboard)/10);
1710
0
            ais->type24.dim.to_starboard  = (unsigned int) (to_starboard/10);
1711
0
        }
1712
1713
0
        for (i = 0; i < MAX_TYPE24_INTERLEAVE; i++) {
1714
0
            if (session->driver.aivdm.context[0].type24_queue.ships[i].mmsi ==
1715
0
                ais->mmsi) {
1716
0
                strlcpy(ais->type24.shipname,
1717
0
                       (char *)session->driver.aivdm.context[0].type24_queue.ships[i].shipname,
1718
0
                       sizeof(ais->type24.shipname));
1719
1720
0
                GPSD_LOG(LOG_PROG, &session->context->errout,
1721
0
                         "NMEA2000: AIS 24B from %09u matches a 24A.\n",
1722
0
                         ais->mmsi);
1723
                // prevent false match if a 24B is repeated
1724
0
                session->driver.aivdm.context[0].type24_queue.ships[i].mmsi = 0;
1725
#if NMEA2000_DEBUG_AIS
1726
                printf("AIS: MMSI:  %09u\n", ais->mmsi);
1727
                printf("AIS: name:  %-20.20s v:%-8.8s c:%-8.8s b:%6u "
1728
                       "s:%6u p:%6u s:%6u\n",
1729
                       ais->type24.shipname,
1730
                       ais->type24.vendorid,
1731
                       ais->type24.callsign,
1732
                       ais->type24.dim.to_bow,
1733
                       ais->type24.dim.to_stern,
1734
                       ais->type24.dim.to_port,
1735
                       ais->type24.dim.to_starboard);
1736
#endif  // of #if NMEA2000_DEBUG_AIS
1737
1738
0
                decode_ais_channel_info(bu, len, 264, session);
1739
0
                ais->type24.part = both;
1740
0
                return ONLINE_SET | AIS_SET;
1741
0
            }
1742
0
        }
1743
#if NMEA2000_DEBUG_AIS
1744
        printf("AIS: MMSI  :  %09u\n", ais->mmsi);
1745
        printf("AIS: vendor:  %-8.8s c:%-8.8s b:%6u s:%6u p:%6u s:%6u\n",
1746
               ais->type24.vendorid,
1747
               ais->type24.callsign,
1748
               ais->type24.dim.to_bow,
1749
               ais->type24.dim.to_stern,
1750
               ais->type24.dim.to_port,
1751
               ais->type24.dim.to_starboard);
1752
#endif  // of #if NMEA2000_DEBUG_AIS
1753
0
        decode_ais_channel_info(bu, len, 264, session);
1754
0
        ais->type24.part = part_b;
1755
0
        return ONLINE_SET | AIS_SET;
1756
0
    }
1757
0
    return 0;
1758
0
}
1759
1760
1761
/*
1762
 *   PGN 130306: NAV Wind Data
1763
 */
1764
static gps_mask_t hnd_130306(struct gps_device_t *session UNUSED)
1765
0
{
1766
0
    return 0;
1767
0
}
1768
1769
1770
/*
1771
 *   PGN 130310: NAV Water Temp., Outside Air Temp., Atmospheric Pressure
1772
 */
1773
static gps_mask_t hnd_130310(struct gps_device_t *session UNUSED)
1774
0
{
1775
0
    return 0;
1776
0
}
1777
1778
1779
/*
1780
 *   PGN 130311: NAV Environmental Parameters
1781
 */
1782
static gps_mask_t hnd_130311(struct gps_device_t *session UNUSED)
1783
0
{
1784
0
    return 0;
1785
0
}
1786
1787
1788
// keep list sorted!
1789
static const PGN pgnlst[] = {{ 59392, 0, 0, hnd_059392, "ISO Acknowledgment"},
1790
                             { 60928, 0, 0, hnd_060928, "ISO Address Claim"},
1791
                             {126208, 0, 0, hnd_126208,
1792
                              "NMEA Command/Request/Acknowledge"},
1793
                             {126464, 1, 0, hnd_126464,
1794
                              "ISO Transmit/Receive PGN List"},
1795
                             {126720, 1, 0, hnd_126720,
1796
                              "Maretron proprietary"},
1797
                             {126992, 0, 0, hnd_126992, "GNSS System Time"},
1798
                             {126996, 1, 0, hnd_126996,
1799
                              "ISO Product Information"},
1800
                             {127245, 0, 4, hnd_127245, "NAV Rudder"},
1801
                             {127250, 0, 4, hnd_127250, "NAV Vessel Heading"},
1802
                             {127258, 0, 0, hnd_127258,
1803
                             "GNSS Magnetic Variation"},
1804
                             {127258, 0, 0, hnd_127258, "NAV Vessel Heading"},
1805
                             {127506, 1, 3, hnd_127506,
1806
                             "PWR DC Detailed Status"},
1807
                             {127508, 1, 3, hnd_127508, "PWR Battery Status"},
1808
                             {127513, 1, 3, hnd_127513,
1809
                              "PWR Battery Configuration Status"},
1810
                             {128259, 0, 4, hnd_128259, "NAV Speed"},
1811
                             {128267, 0, 4, hnd_128267, "NAV Water Depth"},
1812
                             {128275, 1, 4, hnd_128275, "NAV Distance Log"},
1813
                             {129025, 0, 1, hnd_129025,
1814
                              "GNSS Position Rapid Update"},
1815
                             {129026, 0, 1, hnd_129026,
1816
                              "GNSS COG and SOG Rapid Update"},
1817
                             {129029, 1, 1, hnd_129029,
1818
                              "GNSS Position Data"},
1819
                             {129038, 1, 2, hnd_129038,
1820
                              "AIS Class A Position Report"},
1821
                             {129039, 1, 2, hnd_129039,
1822
                              "AIS Class B Position Report"},
1823
                             {129040, 1, 2, hnd_129040,
1824
                              "AIS Class B Extended Position Report"},
1825
                             {129283, 0, 0, hnd_129283,
1826
                              "NAV Cross Track Error"},
1827
                             {129284, 1, 0, hnd_129284, "NAV Navigation Data"},
1828
                             {129285, 1, 0, hnd_129285,
1829
                              "NAV Navigation - Route/WP Information"},
1830
                             {129539, 0, 1, hnd_129539, "GNSS DOPs"},
1831
                             {129540, 1, 1, hnd_129540,
1832
                              "GNSS Satellites in View"},
1833
                             {129793, 1, 2, hnd_129793,
1834
                              "AIS  UTC and Date report"},
1835
                             {129794, 1, 2, hnd_129794,
1836
                              "AIS Class A Static and Voyage Related Data"},
1837
                             {129798, 1, 2, hnd_129798,
1838
                              "AIS  SAR Aircraft Position Report"},
1839
                             {129802, 1, 2, hnd_129802,
1840
                              "AIS  Safety Related Broadcast Message"},
1841
                             {129809, 1, 2, hnd_129809,
1842
                              "AIS Class B CS Static Data Report, Part A"},
1843
                             {129810, 1, 2, hnd_129810,
1844
                              "AIS Class B CS Static Data Report, Part B"},
1845
                             {130306, 0, 4, hnd_130306, "NAV Wind Data"},
1846
                             {130310, 0, 4, hnd_130310,
1847
                              "NAV Water Temp., Outside Air Temp., "
1848
                              "Atmospheric Pressure"},
1849
                             {130311, 0, 4, hnd_130311,
1850
                              "NAV Environmental Parameters"},
1851
                             {0     , 0, 0, NULL, "**error**"},
1852
};
1853
1854
1855
static const PGN *search_pgnlist(unsigned int pgn)
1856
0
{
1857
0
    int l1 = 0;
1858
1859
    // since list is sorted, we can early out
1860
0
    for (l1 = 0; pgn > pgnlst[l1].pgn; l1++) {
1861
0
        if (0 == pgnlst[l1].pgn) {
1862
0
            break;
1863
0
        }
1864
0
    }
1865
0
    if (pgnlst[l1].pgn == pgn) {
1866
0
        return &pgnlst[l1];
1867
0
    }
1868
0
    return NULL;
1869
0
}
1870
1871
static void find_pgn(struct can_frame *frame, struct gps_device_t *session)
1872
0
{
1873
0
    unsigned can_net;
1874
0
    unsigned source_prio;
1875
0
    unsigned daddr;
1876
0
    unsigned source_pgn;
1877
0
    unsigned source_addr;
1878
1879
0
    session->driver.nmea2000.workpgn = NULL;
1880
0
    can_net = session->driver.nmea2000.can_net;
1881
1882
0
    GPSD_LOG(LOG_RAW, &session->context->errout,
1883
0
             "NMEA2000 find_pgn() can_id x%x can_net %u\n",
1884
0
             frame->can_id, can_net);
1885
1886
0
    if (NMEA2000_NETS <= can_net) {
1887
0
        GPSD_LOG(LOG_ERROR, &session->context->errout,
1888
0
                 "NMEA2000 find_pgn: Invalid can network %u.\n", can_net);
1889
0
        return;
1890
0
    }
1891
1892
0
    if (frame->can_id & CAN_ERR_FLAG) {
1893
0
        GPSD_LOG(LOG_ERROR, &session->context->errout,
1894
0
                 "NMEA2000 CAN_ERR_FLAG set x%x.\n", frame->can_id);
1895
0
        return;
1896
0
    }
1897
1898
0
    if (!(frame->can_id & CAN_EFF_FLAG)) {
1899
        // we got RTR or 2.0A CAN frame, not used.  SHould have been filtered.
1900
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
1901
0
                 "NMEA2000 CAN_EFF_FLAG not set x%x.\n", frame->can_id);
1902
0
        return;
1903
0
    }
1904
0
#if LOG_FILE
1905
0
    if (NULL != logFile) {
1906
0
        struct timespec  msgTime;
1907
1908
0
        clock_gettime(CLOCK_REALTIME, &msgTime);
1909
0
        (void)fprintf(logFile,
1910
0
                      "(%010lld.%06ld) can0 %08x#",
1911
0
                      (long long)msgTime.tv_sec,
1912
0
                      msgTime.tv_nsec / 1000,
1913
0
                      frame->can_id & 0x1ffffff);
1914
0
        if (0 < (frame->can_dlc & 0x0f)) {
1915
0
            int l1;
1916
0
            for (l1 = 0; l1 < (frame->can_dlc & 0x0f); l1++) {
1917
0
                (void)fprintf(logFile, "%02x", frame->data[l1]);
1918
0
            }
1919
0
        }
1920
0
        (void)fprintf(logFile, "\n");
1921
0
    }
1922
0
#endif  // of if LOG_FILE
1923
0
    source_addr = frame->can_id & 0x0ff;
1924
0
    if (NMEA2000_ADDRS <= source_addr) {
1925
0
        GPSD_LOG(LOG_PROG, &session->context->errout,
1926
0
                 "NMEA2000 ignoring SA %u.\n", source_addr);
1927
0
        return;
1928
0
    }
1929
0
    session->driver.nmea2000.can_msgcnt += 1;
1930
0
    source_pgn = (frame->can_id >> 8) & 0x1ffff;
1931
0
    source_prio = (frame->can_id >> 26) & 0x7;
1932
1933
0
    if (240 > ((source_pgn & 0x0ff00) >> 8)) {
1934
0
        daddr  = source_pgn & 0x000ff;
1935
0
        source_pgn  = source_pgn & 0x1ff00;
1936
0
    } else {
1937
0
        daddr = 0xff;
1938
0
    }
1939
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
1940
0
             "NMEA2000: source_prio %u SA %u daddr %u\n",
1941
0
             source_prio, source_addr, daddr);
1942
1943
0
    if (!session->driver.nmea2000.source_addr) {
1944
0
        unsigned int l1, l2;
1945
1946
0
        for (l1 = 0; l1 < NMEA2000_NETS; l1++) {
1947
0
            for (l2 = 0; l2 < NMEA2000_ADDRS; l2++) {
1948
0
                if (session == nmea2000_units[l1][l2]) {
1949
0
                    session->driver.nmea2000.source_addr = l2;
1950
0
                    session->driver.nmea2000.sa_valid = true;
1951
0
                    session->driver.nmea2000.can_net = l1;
1952
0
                    can_net = l1;
1953
0
                }
1954
0
            }
1955
0
        }
1956
1957
0
        session->driver.nmea2000.source_addr = source_addr;
1958
0
        session->driver.nmea2000.sa_valid = true;
1959
0
        nmea2000_units[can_net][source_addr] = session;
1960
0
    }
1961
1962
0
    if (source_addr == session->driver.nmea2000.source_addr) {
1963
        // current source_addr.  Current net???
1964
0
        const PGN *work = search_pgnlist(source_pgn);
1965
1966
0
        if (NULL == work) {
1967
0
            GPSD_LOG(LOG_WARN, &session->context->errout,
1968
0
                     "NMEA2000: PGN not found %08d %08x \n",
1969
0
                     source_pgn, source_pgn);
1970
0
        } else if (0 == work->fast) {
1971
            // not FAST, one packet is one complete message
1972
1973
0
            GPSD_LOG(LOG_DATA, &session->context->errout,
1974
0
                     "NMEA2000: pgn %6d:%s \n", work->pgn, work->name);
1975
0
            session->driver.nmea2000.workpgn = (const void *)work;
1976
0
            session->lexer.outbuflen =  frame->can_dlc & 0x0f;  // max 15
1977
0
            memcpy(session->lexer.outbuffer, frame->data,
1978
0
                   session->lexer.outbuflen);
1979
0
        } else if (0 == (frame->data[0] & 0x1f)) {
1980
            // FAST, first packet of multi packet message
1981
1982
            // max frame data 223
1983
0
            session->driver.nmea2000.fast_packet_len = frame->data[1];
1984
0
            session->driver.nmea2000.idx = frame->data[0];
1985
0
            GPSD_LOG(LOG_IO, &session->context->errout,
1986
0
                     "NMEA2000: Set idx %u SA %u flen %2x %6d\n",
1987
0
                     frame->data[0],
1988
0
                     session->driver.nmea2000.source_addr,
1989
0
                     frame->data[1],
1990
0
                     source_pgn);
1991
0
            session->lexer.inbuflen = 6;
1992
0
            session->driver.nmea2000.idx += 1;
1993
0
            memcpy(session->lexer.inbuffer, &frame->data[2], 6);
1994
0
            GPSD_LOG(LOG_DATA, &session->context->errout,
1995
0
                     "NMEA2000: pgn %6d:%s \n", work->pgn, work->name);
1996
0
        } else if (frame->data[0] == session->driver.nmea2000.idx) {
1997
            /* FAST, the expected next packet of multi packet message.
1998
             * we assume FAST packets come in sequence order.
1999
             * Not always true.
2000
             * See: https://canboat.github.io/canboat/canboat.html
2001
             * Secton: packet framing. */
2002
0
            unsigned l2;
2003
2004
            // FIXME: check inbuflen and fast_packet_len
2005
0
            l2 = session->driver.nmea2000.fast_packet_len -
2006
0
                 session->lexer.inbuflen;
2007
0
            if (223 < l2) {
2008
                // WTF??
2009
0
                l2 = 0;
2010
0
            } else if (7 < l2) {
2011
                // max 7 per packet
2012
0
                l2 = 7;
2013
0
            }
2014
            // take up to 7 bytes, of 8.  1st byte is idx.
2015
0
            memcpy(&session->lexer.inbuffer[session->lexer.inbuflen],
2016
0
                   &frame->data[1], l2);
2017
0
            session->lexer.inbuflen += l2;
2018
2019
0
            if (session->lexer.inbuflen ==
2020
0
                session->driver.nmea2000.fast_packet_len) {
2021
                // Got a complete message
2022
0
                GPSD_LOG(LOG_IO, &session->context->errout,
2023
0
                         "NMEA2000: Fast done  idx %2x/%2x SA %u "
2024
0
                         "flen %2x %6d\n",
2025
0
                         session->driver.nmea2000.idx,
2026
0
                         frame->data[0],
2027
0
                         session->driver.nmea2000.source_addr,
2028
0
                         (unsigned)session->driver.nmea2000.fast_packet_len,
2029
0
                         source_pgn);
2030
0
                session->driver.nmea2000.workpgn = (const void *)work;
2031
0
                session->lexer.outbuflen =
2032
0
                    session->driver.nmea2000.fast_packet_len;
2033
0
                memcpy(session->lexer.outbuffer, session->lexer.inbuffer,
2034
0
                       session->lexer.outbuflen);
2035
0
                session->driver.nmea2000.fast_packet_len = 0;
2036
0
            } else {
2037
                // More to come.
2038
0
                session->driver.nmea2000.idx += 1;
2039
0
            }
2040
0
        } else {
2041
            /* error? or packets out of order?
2042
             * reset FAST expected?? */
2043
0
            GPSD_LOG(LOG_WARN, &session->context->errout,
2044
0
                 "NMEA2000: Fast error idx%2x/%2x SA %u flen %2x %6d\n",
2045
0
                 session->driver.nmea2000.idx,
2046
0
                 frame->data[0],
2047
0
                 session->driver.nmea2000.source_addr,
2048
0
                 (unsigned)session->driver.nmea2000.fast_packet_len,
2049
0
                 source_pgn);
2050
0
        }
2051
0
    } else if (NULL == nmea2000_units[can_net][source_addr]) {
2052
        // unknown net/SA, add it as a new device.
2053
0
        char buffer[GPS_PATH_MAX];
2054
2055
0
        (void)snprintf(buffer, sizeof(buffer), "nmea2000://%s:%u",
2056
0
                       can_interface_name[can_net],
2057
0
                       source_addr);
2058
0
        if (NULL != gpsd_add_device) {
2059
0
            if (gpsd_add_device(buffer, true)) {
2060
0
                GPSD_LOG(LOG_INF, &session->context->errout,
2061
0
                         "NMEA2000: gpsd_add_device(%s)\n", buffer);
2062
0
            } else {
2063
0
                GPSD_LOG(LOG_ERROR, &session->context->errout,
2064
0
                         "NMEA2000: gpsd_add_device(%s) failed\n",
2065
0
                         buffer);
2066
0
            }
2067
0
        }
2068
0
    } // else, known net/SA that is not this net/SA.  Ignore it.
2069
0
}
2070
2071
2072
static ssize_t nmea2000_get(struct gps_device_t *session)
2073
0
{
2074
0
    struct can_frame frame;
2075
0
    ssize_t          status;
2076
2077
0
    errno = 0;
2078
0
    session->lexer.outbuflen = 0;
2079
    // FIXME: read() into a struct is not guaranteed in C
2080
    // sizeof(frame) === 16
2081
0
    status = read(session->gpsdata.gps_fd, &frame, sizeof(frame));
2082
0
    if ((ssize_t)sizeof(frame) == status) {
2083
0
        session->lexer.type = NMEA2000_PACKET;
2084
0
        find_pgn(&frame, session);
2085
2086
0
        return frame.can_dlc & 0x0f;
2087
0
    }
2088
0
    if (-1 == status &&
2089
0
        EAGAIN == errno &&
2090
0
        LOG_PROG > session->context->errout.debug) {
2091
        /* nothing to read, try again later
2092
         * do not log at low log levels */
2093
0
        return 0;
2094
0
    }
2095
    // long cast for 32-bit.
2096
0
    GPSD_LOG(LOG_WARN, &session->context->errout,
2097
0
             "NMEA2000 nmea2000_get() status %ld %s(%d) \n",
2098
0
             (long)status, strerror(errno), errno);
2099
0
    return 0;
2100
0
}
2101
2102
static gps_mask_t nmea2000_parse_input(struct gps_device_t *session)
2103
0
{
2104
0
    gps_mask_t mask = 0;
2105
0
    const PGN *work;
2106
0
    char buf[128];
2107
2108
0
    GPSD_LOG(LOG_RAW, &session->context->errout,
2109
0
             "NMEA2000 nmea2000_parse_input(%s)\n",
2110
0
             gps_hexdump(buf, sizeof(buf),
2111
0
                         session->lexer.outbuffer,
2112
0
                         session->lexer.outbuflen));
2113
0
    work = (const PGN *)session->driver.nmea2000.workpgn;
2114
2115
0
    if (NULL != work) {
2116
0
        print_data(session);
2117
0
        mask = (work->func)(session);
2118
0
        session->driver.nmea2000.workpgn = NULL;
2119
0
    }
2120
0
    session->lexer.outbuflen = 0;
2121
2122
0
    return mask;
2123
0
}
2124
2125
2126
int nmea2000_open(struct gps_device_t *session)
2127
0
{
2128
0
    char interface_name[GPS_PATH_MAX];
2129
0
    socket_t sock;
2130
0
    int status;
2131
0
    int source_addr = -1;
2132
0
    int can_net = -1;
2133
0
    unsigned int l;
2134
0
    struct ifreq ifr;
2135
0
    struct sockaddr_can addr;
2136
0
    char *sa_ptr = NULL;
2137
0
    can_err_mask_t err_mask;
2138
0
    int rcvbuf_size = 1000000;  // requested receiver buffer size
2139
0
    int curr_rcvbuf_size;
2140
0
    socklen_t curr_rcvbuf_size_len = sizeof(curr_rcvbuf_size);
2141
0
    struct can_filter can_filter;
2142
0
    size_t interface_name_len;
2143
2144
    // FIXME: if this was a live socket, then we left orphan fd.
2145
0
    INVALIDATE_SOCKET(session->gpsdata.gps_fd);
2146
2147
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
2148
0
             "NMEA2000 nmea2000_open(%s)\n",
2149
0
             session->gpsdata.dev.path);
2150
2151
0
    session->driver.nmea2000.can_net = 0;
2152
2153
    // skip the leading "nmea2000://"
2154
0
    (void)strlcpy(interface_name, session->gpsdata.dev.path + 11,
2155
0
                  sizeof(interface_name));
2156
2157
0
    interface_name_len = strnlen(interface_name, sizeof(interface_name));
2158
0
    for (l = 0; l < interface_name_len; l++) {
2159
0
        if (':' == interface_name[l]) {
2160
0
            sa_ptr = &interface_name[l + 1];
2161
0
            interface_name[l] = 0;
2162
0
            continue;
2163
0
        }
2164
0
        if (NULL != sa_ptr) {
2165
0
            if (0 == isdigit(interface_name[l])) {
2166
0
                GPSD_LOG(LOG_ERROR, &session->context->errout,
2167
0
                         "NMEA2000 open: Invalid char x%x in source addr.\n",
2168
0
                         interface_name[l]);
2169
0
                return -1;
2170
0
            }
2171
0
        }
2172
0
    }
2173
2174
0
    if (NULL != sa_ptr) {
2175
0
        source_addr = atoi(sa_ptr);
2176
0
        if ((0 > source_addr) ||
2177
0
            (NMEA2000_ADDRS <= source_addr)) {
2178
0
            GPSD_LOG(LOG_ERROR, &session->context->errout,
2179
0
                     "NMEA2000 open: SA %u out of range.\n",
2180
0
                     source_addr);
2181
0
            return -1;
2182
0
        }
2183
0
        for (l = 0; l < NMEA2000_NETS; l++) {
2184
0
            if (0 == strncmp(can_interface_name[l],
2185
0
                             interface_name,
2186
0
                             MIN(sizeof(interface_name),
2187
0
                                 sizeof(can_interface_name[l])))) {
2188
0
                can_net = l;
2189
0
                break;
2190
0
            }
2191
0
        }
2192
0
        if (0 > can_net) {
2193
0
            for (l = 0; l < NMEA2000_NETS; l++) {
2194
0
                if (0 == can_interface_name[l][0]) {
2195
0
                    can_net = l;
2196
0
                    break;
2197
0
                }
2198
0
            }
2199
0
        }
2200
0
        if (0 > can_net) {
2201
0
            GPSD_LOG(LOG_ERROR, &session->context->errout,
2202
0
                     "NMEA2000 open: CAN device not open: %s .\n",
2203
0
                     interface_name);
2204
0
            return -1;
2205
0
        }
2206
0
    } else {
2207
0
        for (l = 0; l < NMEA2000_NETS; l++) {
2208
0
            if (0 == strncmp(can_interface_name[l],
2209
0
                             interface_name,
2210
0
                             MIN(sizeof(interface_name),
2211
0
                                 sizeof(can_interface_name[l])))) {
2212
0
                GPSD_LOG(LOG_ERROR, &session->context->errout,
2213
0
                         "NMEA2000 open: CAN device duplicate open: %s .\n",
2214
0
                         interface_name);
2215
0
                return -1;
2216
0
            }
2217
0
        }
2218
0
        for (l = 0; l < NMEA2000_NETS; l++) {
2219
0
            if (0 == can_interface_name[l][0]) {
2220
0
                can_net = l;
2221
0
                break;
2222
0
            }
2223
0
        }
2224
0
        if (0 > can_net) {
2225
0
            GPSD_LOG(LOG_ERROR, &session->context->errout,
2226
0
                     "NMEA2000 open: Too many CAN networks open.\n");
2227
0
            return -1;
2228
0
        }
2229
0
    }
2230
2231
    // Create the socket
2232
0
    sock = socket(PF_CAN, SOCK_RAW, CAN_RAW);
2233
2234
0
    if (BAD_SOCKET(sock)) {
2235
0
        GPSD_LOG(LOG_ERROR, &session->context->errout,
2236
0
                 "NMEA2000 open: socket(PF_CAN) %s(%d).\n",
2237
0
                 strerror(errno), errno);
2238
0
        return -1;
2239
0
    }
2240
2241
0
    status = fcntl(sock, F_SETFL, O_NONBLOCK);
2242
0
    if (0 != status) {
2243
0
        GPSD_LOG(LOG_ERROR, &session->context->errout,
2244
0
                 "NMEA2000 open: fcntl(O_NONBLOCK) %s(%d).\n",
2245
0
                 strerror(errno), errno);
2246
0
        close(sock);
2247
0
        return -1;
2248
0
    }
2249
2250
    // turn on CANBUS error reporting
2251
0
    err_mask = CAN_ERR_ACK | CAN_ERR_BUSOFF | CAN_ERR_CRTL | CAN_ERR_LOSTARB |
2252
0
               CAN_ERR_PROT | CAN_ERR_RESTARTED | CAN_ERR_TRX |
2253
0
               CAN_ERR_TX_TIMEOUT;
2254
2255
0
    status = setsockopt(sock, SOL_CAN_RAW, CAN_RAW_ERR_FILTER,
2256
0
                        &err_mask, sizeof(err_mask));
2257
0
    if (0 != status) {
2258
0
        GPSD_LOG(LOG_ERROR, &session->context->errout,
2259
0
                 "NMEA2000 open: setsockopt() %s(%d)\n",
2260
0
                 strerror(errno), errno);
2261
0
    }
2262
2263
    /* enbiggen the receiver buffer size
2264
     * try SO_RCVBUFFORCE first, if we run with CAP_NET_ADMIN */
2265
0
    if (0 > setsockopt(sock, SOL_SOCKET, SO_RCVBUFFORCE,
2266
0
                       &rcvbuf_size, sizeof(rcvbuf_size))) {
2267
0
            GPSD_LOG(LOG_ERROR, &session->context->errout,
2268
0
                     "NMEA2000 open:SO_RCVBUFFORCE failed try RCVBUF. "
2269
0
                     "%s(%d)\n",
2270
0
                     strerror(errno), errno);
2271
0
            if (0 > setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
2272
0
                               &rcvbuf_size, sizeof(rcvbuf_size))) {
2273
0
                GPSD_LOG(LOG_ERROR, &session->context->errout,
2274
0
                         "NMEA2000 open:setsockopt(SO_RCVBUF) %s(%d).\n",
2275
0
                         strerror(errno), errno);
2276
0
            }
2277
0
    }
2278
0
    if (0 > getsockopt(sock, SOL_SOCKET, SO_RCVBUF,
2279
0
                   &curr_rcvbuf_size, &curr_rcvbuf_size_len)) {
2280
0
        GPSD_LOG(LOG_ERROR, &session->context->errout,
2281
0
                 "NMEA2000 open:getsockopt(SO_RCVBUF) %s(%d)\n",
2282
0
                 strerror(errno), errno);
2283
0
    } else {
2284
0
        GPSD_LOG(LOG_NOTICE, &session->context->errout,
2285
0
                 "NMEA2000 open:getsockopt(SO_RCVBUF) =  %d\n",
2286
0
                 curr_rcvbuf_size);
2287
0
    }
2288
2289
    // Locate the interface you wish to use
2290
0
    (void)strlcpy(ifr.ifr_name, interface_name, sizeof(ifr.ifr_name));
2291
    // ifr.ifr_ifindex gets filled with that device's index
2292
0
    status = ioctl(sock, SIOCGIFINDEX, &ifr);
2293
2294
0
    if (0 != status) {
2295
0
        GPSD_LOG(LOG_ERROR, &session->context->errout,
2296
0
                 "NMEA2000 open: can not find CAN device.\n");
2297
0
        close(sock);
2298
0
        return -1;
2299
0
    }
2300
2301
    // Select that CAN interface, and bind the socket to it.
2302
0
    addr.can_family = AF_CAN;
2303
0
    addr.can_ifindex = ifr.ifr_ifindex;
2304
0
    status = bind(sock, (struct sockaddr*)&addr, sizeof(addr) );
2305
0
    if (0 != status) {
2306
0
        GPSD_LOG(LOG_ERROR, &session->context->errout,
2307
0
                 "NMEA2000 open: bind failed.\n");
2308
0
        close(sock);
2309
0
        return -1;
2310
0
    }
2311
2312
0
    gpsd_switch_driver(session, "NMEA2000");
2313
0
    session->gpsdata.gps_fd = sock;
2314
0
    session->sourcetype = SOURCE_CAN;
2315
0
    session->servicetype = SERVICE_SENSOR;
2316
0
    session->driver.nmea2000.can_net = can_net;
2317
2318
0
    (void)strlcpy(can_interface_name[can_net],
2319
0
                  interface_name, sizeof(can_interface_name[0]));
2320
2321
0
    if (NULL == sa_ptr) {
2322
        //  Only include EFF CAN frames
2323
0
        can_filter.can_mask = CAN_EFF_FLAG | CAN_RTR_FLAG;
2324
0
        can_filter.can_id = CAN_EFF_FLAG;
2325
2326
0
        session->driver.nmea2000.sa_valid = false;
2327
        // no source addr, yet.
2328
0
        memset(nmea2000_units[can_net], 0, sizeof(nmea2000_units[can_net]));
2329
0
    } else {
2330
        // Only include EFF CAN frames with the specific source address
2331
0
        can_filter.can_mask = CAN_EFF_FLAG | CAN_RTR_FLAG | 0xff;
2332
0
        can_filter.can_id = CAN_EFF_FLAG | source_addr;
2333
2334
0
        nmea2000_units[can_net][source_addr] = session;
2335
0
        session->driver.nmea2000.source_addr = source_addr;
2336
0
        session->driver.nmea2000.sa_valid = true;
2337
0
    }
2338
2339
0
    status = setsockopt(sock, SOL_CAN_RAW, CAN_RAW_FILTER,
2340
0
                        &can_filter, sizeof(can_filter));
2341
0
    if (0 != status) {
2342
0
        GPSD_LOG(LOG_ERROR, &session->context->errout,
2343
0
                 "NMEA2000 open:setsockopt(CAN_RAW_FILTER) %s(%d).\n",
2344
0
                 strerror(errno), errno);
2345
0
        close(sock);
2346
0
        session->gpsdata.gps_fd = -1;
2347
0
        return -1;
2348
0
    }
2349
2350
    // how do we know the speed???
2351
0
    session->gpsdata.dev.parity = 'N';
2352
0
    session->gpsdata.dev.baudrate = 250000;
2353
0
    session->gpsdata.dev.stopbits = 0;
2354
0
    return session->gpsdata.gps_fd;
2355
0
}
2356
2357
void nmea2000_close(struct gps_device_t *session)
2358
0
{
2359
0
    if (BAD_SOCKET(session->gpsdata.gps_fd)) {
2360
0
        return;
2361
0
    }
2362
2363
    // cast for 32-bit ints.
2364
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
2365
0
             "NMEA2000: close(%ld) in nmea2000_close(%s)\n",
2366
0
             (long)session->gpsdata.gps_fd, session->gpsdata.dev.path);
2367
0
    (void)close(session->gpsdata.gps_fd);
2368
0
    INVALIDATE_SOCKET(session->gpsdata.gps_fd);
2369
2370
0
    if (session->driver.nmea2000.sa_valid) {
2371
0
        unsigned int l1, l2;
2372
2373
0
        for (l1 = 0; l1 < NMEA2000_NETS; l1++) {
2374
0
            for (l2 = 0; l2 < NMEA2000_ADDRS; l2++) {
2375
0
                if (session == nmea2000_units[l1][l2]) {
2376
0
                    memset(&session->driver.nmea2000, 0,
2377
0
                           sizeof(session->driver.nmea2000));
2378
                    nmea2000_units[l1][l2] = NULL;
2379
0
                }
2380
0
            }
2381
0
        }
2382
0
    }
2383
0
}
2384
2385
// *INDENT-OFF*
2386
const struct gps_type_t driver_nmea2000 = {
2387
    .type_name      = "NMEA2000",       // full name of type
2388
    .packet_type    = NMEA2000_PACKET,  // associated lexer packet type
2389
    .flags          = DRIVER_STICKY,    // remember this
2390
    .trigger        = NULL,             // detect their main sentence
2391
    .channels       = 12,               // not an actual GPS at all
2392
    .probe_detect   = NULL,
2393
    .get_packet     = nmea2000_get,     // how to get a packet
2394
    .parse_packet   = nmea2000_parse_input,     // how to interpret a packet
2395
    .rtcm_writer    = NULL,             // Don't send RTCM to this
2396
    .init_query     = NULL,             // non-perturbing query
2397
    .event_hook     = NULL,
2398
    .speed_switcher = NULL,             // no speed switcher
2399
    .mode_switcher  = NULL,             // no mode switcher
2400
    .rate_switcher  = NULL,             // no rate switcher
2401
    .min_cycle.tv_sec  = 1,             // not relevant, no rate switch
2402
    .min_cycle.tv_nsec = 0,             // not relevant, no rate switch
2403
    .control_send   = NULL,             // how to send control strings
2404
    .time_offset     = NULL,
2405
};
2406
// *INDENT-ON*
2407
2408
// end
2409
2410
#else   // of  defined(NMEA2000_ENABLE)
2411
/* dummy variable to some old linkers do not complain about empty
2412
 * object file */
2413
int nmea2000_dummy = 1;
2414
#endif  // of  defined(NMEA2000_ENABLE)
2415
2416
// vim: set expandtab shiftwidth=4