Coverage Report

Created: 2026-10-12 06:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gpsd/gpsd-3.27.6~dev/gpsd/pseudonmea.c
Line
Count
Source
1
/*
2
 * Create pseudo NMEA (and AIVDM) messaegs.
3
 *
4
 * This file is Copyright 2010 by the GPSD project
5
 * SPDX-License-Identifier: BSD-2-clause
6
 */
7
8
#include "../include/gpsd_config.h"   // must be before all includes
9
10
#include <math.h>
11
#include <stdio.h>
12
#include <stdlib.h>
13
#include <string.h>
14
#include <time.h>
15
16
#include "../include/gpsd.h"
17
#include "../include/strfuncs.h"
18
19
/*
20
 * Support for generic binary drivers.  These functions dump NMEA for passing
21
 * to the client in raw mode.  They assume that (a) the public gps.h structure
22
 * members are in a valid state, (b) that the private members hours, minutes,
23
 * and seconds have also been filled in, (c) that if the private member
24
 * mag_var is not NAN it is a magnetic variation in degrees that should be
25
 * passed on, and (d) if the private member separation does not have the
26
 * value NAN, it is a valid WGS84 geoidal separation in meters for the fix.
27
 */
28
29
0
#define BUF_SZ 20
30
/* decimal degrees to GPS-style, degrees first followed by minutes
31
 *
32
 * Return: pointer to passed in buffer (buf) with string
33
 */
34
static char *degtodm_str(double angle, const char *fmt, char *buf)
35
0
{
36
0
    if (0 == isfinite(angle)) {
37
0
        buf[0] = '\0';
38
0
    } else {
39
0
        double fraction, integer;
40
41
0
        angle = fabs(angle);
42
0
        fraction = modf(angle, &integer);
43
0
        (void)snprintf(buf, BUF_SZ, fmt, floor(angle) * 100 + fraction * 60);
44
0
    }
45
0
    return buf;
46
0
}
47
48
/* format a float/lon/alt into a string, handle NAN, INFINITE
49
 *
50
 * Return: pointer to passed in buffer (buf) with string
51
 */
52
static char *f_str(double f, const char *fmt, char *buf)
53
0
{
54
0
    if (0 == isfinite(f)) {
55
0
        buf[0] = '\0';
56
0
    } else {
57
0
        (void)snprintf(buf, BUF_SZ, fmt, f);
58
0
    }
59
0
    return buf;
60
0
}
61
62
// make size of time string buffer large to shut up paranoid cc's
63
#define TIMESTR_SZ 48
64
65
/* convert UTC to time str (hh:mm:ss.ss) and tm
66
 *
67
 * Return: pointer to passed in buffer (buf) with string
68
 */
69
static void utc_to_hhmmss(timespec_t time, char *buf, ssize_t buf_sz,
70
                          struct tm *tm)
71
0
{
72
0
    time_t integer;
73
0
    long fractional;
74
75
0
    if (0 >= time.tv_sec) {
76
0
        buf[0] = '\0';
77
0
        return;
78
0
    }
79
80
0
    integer = time.tv_sec;
81
    // round to 100ths
82
0
    fractional = (time.tv_nsec + 5000000L) / 10000000L;
83
0
    if (99 < fractional) {
84
0
        integer++;
85
0
        fractional = 0;
86
0
    }
87
88
0
    if (NULL == gmtime_r(&integer, tm)) {
89
        // WTF?
90
0
        buf[0] = '\0';
91
0
        return;
92
0
    }
93
94
95
0
    (void)snprintf(buf, buf_sz, "%02d%02d%02d.%02ld",
96
0
                   tm->tm_hour, tm->tm_min, tm->tm_sec, fractional);
97
0
    return;
98
0
}
99
100
/* dbl_to_str() -- convert a double to a string in bufp
101
 *
102
 * Return: void
103
 */
104
static void dbl_to_str(const char *fmt, double val, char *bufp, size_t len,
105
                       const char *suffix)
106
0
{
107
0
    if (0 == isfinite(val)) {
108
0
        (void)strlcat(bufp, ",", len);
109
0
    } else {
110
0
        str_appendf(bufp, len, fmt, val);
111
0
    }
112
0
    if (NULL != suffix) {
113
0
        str_appendf(bufp, len, "%s", suffix);
114
0
    }
115
0
}
116
117
/* GPS Quality Indicator values for xxGGA
118
 * Almost, not quite, the same as STATUS_* from gps.h */
119
0
#define FIX_QUALITY_INVALID 0
120
0
#define FIX_QUALITY_GPS 1
121
0
#define FIX_QUALITY_DGPS 2
122
#define FIX_QUALITY_PPS 3
123
0
#define FIX_QUALITY_RTK 4
124
0
#define FIX_QUALITY_RTK_FLT 5
125
0
#define FIX_QUALITY_DR 6
126
0
#define FIX_QUALITY_MANUAL 7
127
0
#define FIX_QUALITY_SIMULATED 8
128
129
/*
130
 * If possible, create a $GPGGA message (full time, position and fix data)
131
 * from the fix data, taking care about the validity of subsidiary fields
132
 * like HDOP and altitude.
133
 *
134
 * looks like this is only called from net_ntrip.c and nmea_tpv_dump()
135
 *
136
 * Return: length of generated GGA string at bufp
137
 */
138
int gpsd_position_fix_dump(struct gps_device_t *session,
139
                           char bufp[], size_t len)
140
0
{
141
0
    struct tm tm;
142
0
    char time_str[TIMESTR_SZ];
143
0
    unsigned char fixquality;
144
0
    char lat_str[BUF_SZ];
145
0
    char lon_str[BUF_SZ];
146
0
    int blen = 0;          // length, so far, of string in bufp
147
148
0
    *bufp = '\0';
149
150
    // Maybe send an empty $GGA on no fix?
151
0
    if (MODE_NO_FIX >= session->gpsdata.fix.mode) {
152
0
        return 0;
153
0
    }
154
155
0
    utc_to_hhmmss(session->gpsdata.fix.time, time_str, sizeof(time_str), &tm);
156
0
    switch(session->gpsdata.fix.status) {
157
0
    case STATUS_UNK:
158
        // should not happen as we send no GGA when NO_FIX
159
0
        FALLTHROUGH
160
0
    case STATUS_GPS:
161
0
        fixquality = FIX_QUALITY_GPS;
162
0
        break;
163
0
    case STATUS_DGPS:
164
0
        fixquality = FIX_QUALITY_DGPS;
165
0
        break;
166
0
    case STATUS_RTK_FIX:
167
0
        fixquality = FIX_QUALITY_RTK;
168
0
        break;
169
0
    case STATUS_RTK_FLT:
170
0
        fixquality = FIX_QUALITY_RTK_FLT;
171
0
        break;
172
0
    case STATUS_DR:
173
0
        fixquality = FIX_QUALITY_DR;
174
0
        break;
175
0
    case STATUS_GNSSDR:
176
        /* NMEA doesn't know GNSS+DR from DR, so report GNSS fix
177
         * What does it even mean? */
178
0
        fixquality = FIX_QUALITY_GPS;
179
0
        break;
180
0
    case STATUS_TIME:
181
0
        fixquality = FIX_QUALITY_MANUAL;
182
0
        break;
183
0
    case STATUS_SIM:
184
0
        fixquality = FIX_QUALITY_SIMULATED;
185
0
        break;
186
0
    default:
187
0
        fixquality = FIX_QUALITY_INVALID;
188
0
        break;
189
0
    }
190
191
    /* Use lat/lon precision .7 because u-blox ZED-F9P does.
192
     * Use lat 4 digits of integer, lon 5 digits, because:
193
     * http://sapos.geonord-od.de:2101/ wants that minimum */
194
0
    blen = snprintf(bufp, len,
195
0
                    "$GPGGA,%s,%s,%c,%s,%c,%d,%02d,",
196
0
                    time_str,
197
0
                    degtodm_str(session->gpsdata.fix.latitude, "%012.7f",
198
0
                                lat_str),
199
0
                    ((session->gpsdata.fix.latitude > 0) ? 'N' : 'S'),
200
0
                    degtodm_str(session->gpsdata.fix.longitude, "%013.7f",
201
0
                                lon_str),
202
0
                    ((session->gpsdata.fix.longitude > 0) ? 'E' : 'W'),
203
0
                    fixquality,
204
0
                    session->gpsdata.satellites_used);
205
0
    dbl_to_str("%.2f,", session->gpsdata.dop.hdop, bufp + blen, len - blen,
206
0
               NULL);
207
0
    dbl_to_str("%.2f,", session->gpsdata.fix.altMSL, bufp, len, "M,");
208
0
    dbl_to_str("%.3f,", session->gpsdata.fix.geoid_sep, bufp, len, "M,");
209
    // Age of correction data, and Differential base station ID
210
0
    if (0.0 > session->gpsdata.fix.dgps_age ||
211
0
        0 > session->gpsdata.fix.dgps_station) {
212
        // n/a
213
0
        (void)strlcat(bufp, ",", len);
214
0
    } else {
215
0
        char dgps_str[BUF_SZ];
216
217
        // Warning, u-blox dgps_station may be 16 bits.
218
0
        (void)snprintf(dgps_str, BUF_SZ, "%.1f,%04d",
219
0
                       session->gpsdata.fix.dgps_age,
220
0
                       session->gpsdata.fix.dgps_station);
221
0
    }
222
0
    nmea_add_checksum(bufp);
223
224
0
    return( strnlen(bufp, len));
225
0
}
226
227
228
static void gpsd_transit_fix_dump(struct gps_device_t *session,
229
                                  char bufp[], size_t len)
230
0
{
231
0
    char time_str[TIMESTR_SZ];
232
0
    char time2_str[TIMESTR_SZ];
233
0
    char lat_str[BUF_SZ];
234
0
    char lon_str[BUF_SZ];
235
0
    char speed_str[BUF_SZ];
236
0
    char track_str[BUF_SZ];
237
0
    char var_str[BUF_SZ];
238
0
    char *var_dir = "";
239
0
    struct tm tm;
240
0
    char valid;
241
242
0
    utc_to_hhmmss(session->gpsdata.fix.time, time_str, sizeof(time_str), &tm);
243
0
    if ('\0' == time_str[0]) {
244
0
        time2_str[0] = '\0';
245
0
    } else {
246
0
        tm.tm_mon++;
247
0
        tm.tm_year %= 100;
248
249
0
        (void)snprintf(time2_str, sizeof(time2_str),
250
0
                       "%02d%02d%02d",
251
0
                       tm.tm_mday, tm.tm_mon, tm.tm_year);
252
0
    }
253
0
    if (0 == isfinite(session->gpsdata.fix.magnetic_var)) {
254
0
        var_str[0] = '\0';
255
0
    } else {
256
0
        f_str(session->gpsdata.fix.magnetic_var, "%.1f", var_str),
257
0
        var_dir = (session->gpsdata.fix.magnetic_var > 0) ? "E" : "W";
258
0
    }
259
260
0
    if (MODE_2D > session->gpsdata.fix.mode) {
261
0
        valid = 'V';      // Status: Warning
262
0
    } else {
263
0
        valid = 'A';      // Status: Valid
264
0
    }
265
266
    // FIXME! add in "Mode Indicator" after var_dir
267
    // Use lat %011.7f and lon %12.7f, because u-blox ZED-F9P does.
268
0
    (void)snprintf(bufp, len,
269
0
                   "$GPRMC,%s,%c,%s,%c,%s,%c,%s,%s,%s,%s,%s",
270
0
                   time_str,
271
0
                   valid,
272
0
                   degtodm_str(session->gpsdata.fix.latitude, "%011.7f",
273
0
                               lat_str),
274
0
                   ((session->gpsdata.fix.latitude > 0) ? 'N' : 'S'),
275
0
                   degtodm_str(session->gpsdata.fix.longitude, "%012.7f",
276
0
                               lon_str),
277
0
                   ((session->gpsdata.fix.longitude > 0) ? 'E' : 'W'),
278
0
                   f_str(session->gpsdata.fix.speed * MPS_TO_KNOTS, "%.4f",
279
0
                            speed_str),
280
0
                   f_str(session->gpsdata.fix.track, "%.3f", track_str),
281
0
                   time2_str,
282
0
                   var_str, var_dir);
283
0
    nmea_add_checksum(bufp);
284
0
}
285
286
static void gpsd_binary_satellite_dump(struct gps_device_t *session,
287
                                       char bufp[], size_t len)
288
0
{
289
0
    unsigned i;             // index into skyview[]
290
0
    unsigned j;             // index into GPGSV
291
0
    char *bufp2 = bufp;
292
0
    unsigned satellites_visible = 0;
293
0
    bufp[0] = '\0';
294
295
0
    if (MAXCHANNELS < session->gpsdata.satellites_visible) {
296
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
297
0
                "gpsd_binary_satellite_dump() too many satellites %d\n",
298
0
                 session->gpsdata.satellites_visible);
299
0
        session->gpsdata.satellites_visible = MAXCHANNELS;
300
0
    }
301
    // check skyview[] for valid sats first
302
0
    for (i = 0; i < session->gpsdata.satellites_visible; i++) {
303
0
        if (1 > session->gpsdata.skyview[i].PRN) {
304
            // bad PRN, ignore
305
0
            continue;
306
0
        }
307
0
        if (0 == isfinite(session->gpsdata.skyview[i].elevation) ||
308
0
            90 < fabs(session->gpsdata.skyview[i].elevation)) {
309
            // bad elevation, ignore
310
0
            continue;
311
0
        }
312
0
        if (0 == isfinite(session->gpsdata.skyview[i].azimuth) ||
313
0
            0 > session->gpsdata.skyview[i].azimuth ||
314
0
            359 < session->gpsdata.skyview[i].azimuth) {
315
            // bad azimuth, ignore
316
0
            continue;
317
0
        }
318
0
        satellites_visible++;
319
0
    }
320
0
    for (i = 0, j= 0; i < session->gpsdata.satellites_visible; i++) {
321
0
        if (1 > session->gpsdata.skyview[i].PRN) {
322
            // bad prn, skip
323
0
            continue;
324
0
        }
325
0
        if (0 == isfinite(session->gpsdata.skyview[i].elevation) ||
326
0
            90 < fabs(session->gpsdata.skyview[i].elevation)) {
327
            // bad elevation, ignore
328
0
            continue;
329
0
        }
330
0
        if (0 == isfinite(session->gpsdata.skyview[i].azimuth) ||
331
0
            0 > session->gpsdata.skyview[i].azimuth ||
332
0
            359 < session->gpsdata.skyview[i].azimuth) {
333
            // bad azimuth, ignore
334
0
            continue;
335
0
        }
336
0
        if (0 == isfinite(session->gpsdata.skyview[i].ss)) {
337
            // bad snr, ignore
338
0
            continue;
339
0
        }
340
0
        if (0 == (j % 4)) {
341
0
            bufp2 = bufp + strlen(bufp);
342
0
            str_appendf(bufp, len,
343
0
                            "$GPGSV,%d,%d,%02d",
344
0
                            ((satellites_visible - 1) / 4) + 1, (j / 4) + 1,
345
0
                            satellites_visible);
346
0
        }
347
0
        str_appendf(bufp, len, ",%02d,%02.0f,%03.0f,%02.0f",
348
0
                    session->gpsdata.skyview[i].PRN,
349
0
                    session->gpsdata.skyview[i].elevation,
350
0
                    session->gpsdata.skyview[i].azimuth,
351
0
                    session->gpsdata.skyview[i].ss);
352
353
0
        if (3 == (j % 4) ||
354
0
            (satellites_visible - 1) == j) {
355
0
            nmea_add_checksum(bufp2);
356
0
        }
357
0
        j++;
358
0
    }
359
360
0
#ifdef ZODIAC_ENABLE
361
0
    if (ZODIAC_PACKET == session->lexer.type &&
362
0
        0 != session->driver.zodiac.Zs[0]) {
363
0
        bufp2 = bufp + strlen(bufp);
364
0
        str_appendf(bufp, len, "$PRWIZCH");
365
0
        for (i = 0; i < ZODIAC_CHANNELS; i++) {
366
0
            str_appendf(bufp, len,
367
0
                        ",%02u,%X",
368
0
                        session->driver.zodiac.Zs[i],
369
0
                        session->driver.zodiac.Zv[i] & 0x0f);
370
0
        }
371
0
        nmea_add_checksum(bufp2);
372
0
    }
373
0
#endif  // ZODIAC_ENABLE
374
0
}
375
376
static void gpsd_binary_quality_dump(struct gps_device_t *session,
377
                                     char bufp[], size_t len)
378
0
{
379
0
    char *bufp2;
380
0
    bufp[0] = '\0';
381
382
0
    if (NULL != session->device_type) {
383
0
        unsigned i, j;
384
0
        unsigned max_channels = session->device_type->channels;
385
0
        int mode = session->gpsdata.fix.mode;
386
387
        // GPGSA commonly has exactly 12 channels, enforce that as a MAX
388
0
        if (12 < max_channels) {
389
            // what to do with the excess channels?
390
0
            max_channels = 12;
391
0
        }
392
393
0
        bufp2 = bufp + strlen(bufp);
394
        // fix.mode can be zero, legal values are only 1, 2 or 3
395
0
        if (MODE_NO_FIX > mode) {
396
0
            mode = MODE_NO_FIX;    // 1
397
0
        } else if (MODE_3D < mode) {
398
0
            mode = MODE_3D;        // 3
399
0
        }
400
        // We don't know if we are manual (M) or automatic (A), force "A".
401
0
        (void)snprintf(bufp, len, "$GPGSA,%c,%d,", 'A', mode);
402
0
        j = 0;
403
0
        for (i = 0; i < max_channels; i++) {
404
0
            if (true == session->gpsdata.skyview[i].used){
405
0
                str_appendf(bufp, len, "%d,", session->gpsdata.skyview[i].PRN);
406
0
                j++;
407
0
            }
408
0
        }
409
0
        for (i = j; i < max_channels; i++) {
410
            // fill out the empty slots
411
0
            (void)strlcat(bufp, ",", len);
412
0
        }
413
0
        if (MODE_NO_FIX == session->gpsdata.fix.mode) {
414
0
            (void)strlcat(bufp, ",,,", len);
415
0
        } else {
416
            // output the DOPs, NaN as blanks
417
0
            if (0 != isfinite(session->gpsdata.dop.pdop)) {
418
0
                str_appendf(bufp, len, "%.1f,", session->gpsdata.dop.pdop);
419
0
            } else {
420
0
                (void)strlcat(bufp, ",", len);
421
0
            }
422
0
            if (0 != isfinite(session->gpsdata.dop.hdop)) {
423
0
                str_appendf(bufp, len, "%.1f,", session->gpsdata.dop.hdop);
424
0
            } else {
425
0
                (void)strlcat(bufp, ",", len);
426
0
            }
427
0
            if (0 != isfinite(session->gpsdata.dop.vdop)) {
428
0
                str_appendf(bufp, len, "%.1f*", session->gpsdata.dop.vdop);
429
0
            } else {
430
0
                (void)strlcat(bufp, "*", len);
431
0
            }
432
0
        }
433
0
        nmea_add_checksum(bufp2);
434
0
    }
435
436
    /* create $GPGBS if we have time, epx and epy.  Optional epv.
437
     * Not really kosher, not have enough info to compute the RAIM
438
     */
439
0
    if (0 != isfinite(session->gpsdata.fix.epx) &&
440
0
        0 != isfinite(session->gpsdata.fix.epy) &&
441
0
        0 != session->gpsdata.fix.time.tv_sec) {
442
443
0
        struct tm tm;
444
0
        char time_str[TIMESTR_SZ];
445
0
        char epv_str[BUF_SZ];
446
447
0
        (void)utc_to_hhmmss(session->gpsdata.fix.time,
448
0
                            time_str, sizeof(time_str), &tm);
449
450
0
        bufp2 = bufp + strlen(bufp);
451
0
        str_appendf(bufp, len,
452
0
                    "$GPGBS,%s,%.3f,%.3f,%s,,,,",
453
0
                    time_str,
454
0
                    session->gpsdata.fix.epx,
455
0
                    session->gpsdata.fix.epy,
456
0
                    f_str(session->gpsdata.fix.epv, "%.3f", epv_str));
457
0
        nmea_add_checksum(bufp2);
458
0
    }
459
0
}
460
461
// Dump $GPZDA if we have time and a fix
462
// return length added
463
static ssize_t gpsd_binary_time_dump(struct gps_device_t *session,
464
                                     char bufp[], size_t len)
465
0
{
466
467
0
    ssize_t blen = 0;
468
469
0
    if (MODE_NO_FIX < session->gpsdata.fix.mode &&
470
0
        0 <= session->gpsdata.fix.time.tv_sec) {
471
        // shut up clang about uninitialized
472
0
        struct tm tm = {0};
473
0
        char time_str[TIMESTR_SZ];
474
475
0
        utc_to_hhmmss(session->gpsdata.fix.time, time_str, sizeof(time_str),
476
0
                      &tm);
477
        /* There used to be confusion, but we now know NMEA times are UTC,
478
         * when available. */
479
0
        blen = snprintf(bufp, len,
480
0
                        "$GPZDA,%s,%02d,%02d,%04d,00,00",
481
0
                        time_str,
482
0
                        tm.tm_mday,
483
0
                        tm.tm_mon + 1,
484
0
                        tm.tm_year + 1900);
485
0
        nmea_add_checksum(bufp);
486
0
        blen += 5;
487
0
    }
488
0
    return blen;
489
0
}
490
491
static void gpsd_binary_almanac_dump(struct gps_device_t *session,
492
                                     char bufp[], size_t len)
493
0
{
494
0
    if (session->gpsdata.subframe.is_almanac) {
495
0
        (void)snprintf(bufp, len,
496
0
            "$GPALM,1,1,%02d,%04d,%02x,%04x,%02x,%04x,%04x,%05x,"
497
0
            "%06x,%06x,%06x,%03x,%03x",
498
0
            (int)session->gpsdata.subframe.sub5.almanac.sv,
499
0
            (int)session->context->gps_week % 1024,
500
0
            (unsigned int)session->gpsdata.subframe.sub5.almanac.svh,
501
0
            (unsigned int)session->gpsdata.subframe.sub5.almanac.e,
502
0
            (unsigned int)session->gpsdata.subframe.sub5.almanac.toa,
503
0
            (unsigned int)session->gpsdata.subframe.sub5.almanac.deltai,
504
0
            (unsigned int)session->gpsdata.subframe.sub5.almanac.Omegad,
505
0
            (unsigned int)session->gpsdata.subframe.sub5.almanac.sqrtA,
506
0
            (unsigned int)session->gpsdata.subframe.sub5.almanac.omega,
507
0
            (unsigned int)session->gpsdata.subframe.sub5.almanac.Omega0,
508
0
            (unsigned int)session->gpsdata.subframe.sub5.almanac.M0,
509
0
            (unsigned int)session->gpsdata.subframe.sub5.almanac.af0,
510
0
            (unsigned int)session->gpsdata.subframe.sub5.almanac.af1);
511
0
        nmea_add_checksum(bufp);
512
0
    }
513
0
}
514
515
#ifdef AIVDM_ENABLE
516
517
0
#define GETLEFT(a) (((a%6) == 0) ? 0 : (6 - (a%6)))
518
519
static void gpsd_binary_ais_dump(struct gps_device_t *session,
520
                                 char bufp[], size_t len)
521
0
{
522
0
    char type[8] = "!AIVDM";
523
0
    unsigned char data[256];
524
0
    unsigned int msg1, msg2;
525
0
    char numc[4];
526
0
    char channel;
527
0
    unsigned int left;
528
0
    unsigned int datalen;
529
0
    unsigned int offset;
530
531
0
    channel = 'A';
532
0
    if ('B' == session->driver.aivdm.ais_channel) {
533
0
        channel = 'B';
534
0
    }
535
536
0
    memset(data, 0, sizeof(data));
537
0
    datalen = ais_binary_encode(&session->gpsdata.ais, &data[0], 0);
538
0
    if ((6 * 60) < datalen) {
539
0
        static int number1 = 0;
540
541
0
        msg1 = datalen / (6 * 60);
542
0
        if (0 != (datalen % (6 * 60))) {
543
0
            msg1 += 1;
544
0
        }
545
0
        numc[0] = '0' + (char)(number1 & 0x0f);
546
0
        numc[1] = '\0';
547
0
        number1 += 1;
548
0
        if (9 < number1) {
549
0
            number1 = 0;
550
0
        }
551
0
        offset = 0;
552
0
        for (msg2 = 1; msg2 <= msg1; msg2++) {
553
0
            unsigned char old;
554
555
0
            old = '\0';
556
0
            if (60 < strlen((char *)&data[(msg2 - 1) * 60])) {
557
0
                old = data[(msg2 - 0) * 60];
558
0
                data[(msg2 - 0) * 60] = '\0';
559
0
            }
560
0
            if ((6 * 60) <= datalen) {
561
0
                left = 0;
562
0
                datalen -= 6 * 60;
563
0
            } else {
564
0
                left = GETLEFT(datalen);
565
0
            }
566
0
            (void)snprintf(&bufp[offset], len-offset,
567
0
                           "%s,%u,%u,%s,%c,%s,%u",
568
0
                           type,
569
0
                           msg1,
570
0
                           msg2,
571
0
                           numc,
572
0
                           channel,
573
0
                           (char *)&data[(msg2-1)*60],
574
0
                           left);
575
576
0
            nmea_add_checksum(&bufp[offset]);
577
0
            if ((unsigned char)'\0' != old) {
578
0
                data[(msg2 - 0) * 60] = old;
579
0
            }
580
0
            offset = (unsigned int)strlen(bufp);
581
0
        }
582
0
    } else if (0 < datalen) {
583
0
        msg1 = 1;
584
0
        msg2 = 1;
585
0
        numc[0] = '\0';
586
0
        left = GETLEFT(datalen);
587
0
        (void)snprintf(bufp, len,
588
0
                       "%s,%u,%u,%s,%c,%s,%u",
589
0
                       type,
590
0
                       msg1,
591
0
                       msg2,
592
0
                       numc,
593
0
                       channel,
594
0
                       (char *)data,
595
0
                       left);
596
597
0
        nmea_add_checksum(bufp);
598
0
    }
599
600
0
    if (24 == session->gpsdata.ais.type) {
601
0
        msg1 = 1;
602
0
        msg2 = 1;
603
0
        numc[0] = '\0';
604
605
0
        memset(data, 0, sizeof(data));
606
0
        datalen = ais_binary_encode(&session->gpsdata.ais, &data[0], 1);
607
0
        if (0 < datalen) {
608
0
            left = GETLEFT(datalen);
609
0
            offset = (unsigned int)strlen(bufp);
610
0
            (void)snprintf(&bufp[offset], len-offset,
611
0
                           "%s,%u,%u,%s,%c,%s,%u",
612
0
                           type,
613
0
                           msg1,
614
0
                           msg2,
615
0
                           numc,
616
0
                           channel,
617
0
                           (char *)data,
618
0
                           left);
619
0
        nmea_add_checksum(bufp+offset);
620
0
        }
621
0
    }
622
0
}
623
#endif  // AIVDM_ENABLE
624
625
/* nmea_tpv_dump()
626
 * dump current fix as NMEA
627
 *
628
 * Return: void
629
 */
630
void nmea_tpv_dump(struct gps_device_t *session,
631
                   char bufp[], size_t len)
632
0
{
633
0
    ssize_t blen = 0;
634
0
    bufp[0] = '\0';
635
636
    // maybe all we need is REPORT_IS?
637
0
    if (0 != (session->gpsdata.set & (TIME_SET | REPORT_IS))) {
638
0
        blen = gpsd_binary_time_dump(session, bufp, len);
639
0
    }
640
0
    if (0 != (session->gpsdata.set & (LATLON_SET | MODE_SET | REPORT_IS))) {
641
0
        blen += gpsd_position_fix_dump(session, bufp + blen, len - blen);
642
0
        gpsd_transit_fix_dump(session, bufp + blen, len - blen);
643
0
    }
644
0
    if (0 != (session->gpsdata.set &
645
0
              (MODE_SET | DOP_SET | USED_IS | HERR_SET | REPORT_IS))) {
646
0
        gpsd_binary_quality_dump(session, bufp + strlen(bufp),
647
0
                                 len - strlen(bufp));
648
0
    }
649
0
}
650
651
void nmea_sky_dump(struct gps_device_t *session,
652
                   char bufp[], size_t len)
653
0
{
654
0
    bufp[0] = '\0';
655
0
    if (0 != (session->gpsdata.set & SATELLITE_SET)) {
656
0
        gpsd_binary_satellite_dump(session, bufp + strlen(bufp),
657
0
                                   len - strlen(bufp));
658
0
    }
659
0
}
660
661
// FIXME: this shim should go away...
662
void nmea_subframe_dump(struct gps_device_t *session,
663
                        char bufp[], size_t len)
664
0
{
665
0
    bufp[0] = '\0';
666
0
    if (0 != (session->gpsdata.set & SUBFRAME_SET)) {
667
0
        size_t buflen = strnlen(bufp, len);
668
0
        gpsd_binary_almanac_dump(session, bufp + buflen, len - buflen);
669
0
    }
670
0
}
671
672
#ifdef AIVDM_ENABLE
673
void nmea_ais_dump(struct gps_device_t *session,
674
                   char bufp[], size_t len)
675
0
{
676
0
    bufp[0] = '\0';
677
0
    if (0 != (session->gpsdata.set & AIS_SET)) {
678
0
        gpsd_binary_ais_dump(session, bufp + strlen(bufp), len - strlen(bufp));
679
0
    }
680
0
}
681
#endif  // AIVDM_ENABLE
682
683
// vim: set expandtab shiftwidth=4