Coverage Report

Created: 2026-09-01 06:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gpsd/gpsd-3.27.6~dev/drivers/driver_italk.c
Line
Count
Source
1
/*
2
 * Driver for the iTalk binary protocol used by FasTrax
3
 *
4
 * Week counters are not limited to 10 bits. It's unknown what
5
 * the firmware is doing to disambiguate them, if anything; it might just
6
 * be adding a fixed offset based on a hidden epoch value, in which case
7
 * unhappy things will occur on the next rollover.
8
 *
9
 * This file is Copyright 2010 by the GPSD project
10
 * SPDX-License-Identifier: BSD-2-clause
11
 *
12
 */
13
14
#include "../include/gpsd_config.h"  // must be before all includes
15
16
#include <math.h>
17
#include <stdbool.h>
18
#include <stdio.h>
19
#include <string.h>
20
#include <unistd.h>
21
22
#include "../include/gpsd.h"
23
#if defined(ITRAX_ENABLE)
24
25
#include "../include/bits.h"
26
#include "../include/driver_italk.h"
27
#include "../include/timespec.h"
28
29
static gps_mask_t italk_parse(struct gps_device_t *, unsigned char *, size_t);
30
static gps_mask_t decode_itk_navfix(struct gps_device_t *, unsigned char *,
31
                                    size_t);
32
static gps_mask_t decode_itk_prnstatus(struct gps_device_t *, unsigned char *,
33
                                       size_t);
34
static gps_mask_t decode_itk_utcionomodel(struct gps_device_t *,
35
                                          unsigned char *, size_t);
36
static gps_mask_t decode_itk_subframe(struct gps_device_t *, unsigned char *,
37
                                      size_t);
38
39
// NAVIGATION_MSG, message id 7
40
static gps_mask_t decode_itk_navfix(struct gps_device_t *session,
41
                                    unsigned char *buf, size_t len)
42
1.92k
{
43
1.92k
    unsigned short flags, pflags;
44
1.92k
    timespec_t ts_tow;
45
1.92k
    uint32_t tow;            // Time of week [ms]
46
1.92k
    char ts_buf[TIMESPEC_LEN];
47
48
1.92k
    gps_mask_t mask = 0;
49
1.92k
    if (296 != len) {
50
1.40k
        GPSD_LOG(LOG_PROG, &session->context->errout,
51
1.40k
                 "ITALK: bad NAV_FIX (len %zu, should be 296)\n",
52
1.40k
                 len);
53
1.40k
        return -1;
54
1.40k
    }
55
56
514
    flags = (unsigned short) getleu16(buf, 7 + 4);
57
    //cflags = (unsigned short) getleu16(buf, 7 + 6);
58
514
    pflags = (unsigned short) getleu16(buf, 7 + 8);
59
60
514
    session->newdata.status = STATUS_UNK;
61
514
    session->newdata.mode = MODE_NO_FIX;
62
514
    mask = ONLINE_SET | MODE_SET | STATUS_SET | CLEAR_IS;
63
64
    // just bail out if this fix is not marked valid
65
514
    if (0 != (pflags & FIX_FLAG_MASK_INVALID)
66
413
        || 0 == (flags & FIXINFO_FLAG_VALID)) {
67
185
        return mask;
68
185
    }
69
70
329
    tow = getleu32(buf, 7 + 84);   // tow in ms
71
329
    MSTOTS(&ts_tow, tow);
72
329
    session->newdata.time = gpsd_gpstime_resolv(session,
73
329
        (unsigned short) getles16(buf, 7 + 82), ts_tow);
74
329
    mask |= TIME_SET | NTPTIME_IS;
75
76
329
    session->newdata.ecef.x = (double)(getles32(buf, 7 + 96) / 100.0);
77
329
    session->newdata.ecef.y = (double)(getles32(buf, 7 + 100) / 100.0);
78
329
    session->newdata.ecef.z = (double)(getles32(buf, 7 + 104) / 100.0);
79
329
    session->newdata.ecef.vx = (double)(getles32(buf, 7 + 186) / 1000.0);
80
329
    session->newdata.ecef.vy = (double)(getles32(buf, 7 + 190) / 1000.0);
81
329
    session->newdata.ecef.vz = (double)(getles32(buf, 7 + 194) / 1000.0);
82
329
    mask |= ECEF_SET | VECEF_SET;
83
    /* this eph does not look right, badly documented.
84
     * let gpsd_error_model() handle it
85
     * session->newdata.eph = (double)(getles32(buf, 7 + 252) / 100.0);
86
     */
87
329
    session->newdata.eps = (double)(getles32(buf, 7 + 254) / 100.0);
88
    // compute epx/epy in gpsd_error_model(), not here
89
329
    mask |= HERR_SET;
90
91
329
#define MAX(a,b) (((a) > (b)) ? (a) : (b))
92
329
    session->gpsdata.satellites_used =
93
329
        (int)MAX(getleu16(buf, 7 + 12), getleu16(buf, 7 + 14));
94
329
    mask |= USED_IS;
95
96
329
    if (flags & FIX_CONV_DOP_VALID) {
97
142
        session->gpsdata.dop.hdop = (double)(getleu16(buf, 7 + 56) / 100.0);
98
142
        session->gpsdata.dop.gdop = (double)(getleu16(buf, 7 + 58) / 100.0);
99
142
        session->gpsdata.dop.pdop = (double)(getleu16(buf, 7 + 60) / 100.0);
100
142
        session->gpsdata.dop.vdop = (double)(getleu16(buf, 7 + 62) / 100.0);
101
142
        session->gpsdata.dop.tdop = (double)(getleu16(buf, 7 + 64) / 100.0);
102
142
        mask |= DOP_SET;
103
142
    }
104
105
329
    if (0 == (pflags & FIX_FLAG_MASK_INVALID) &&
106
329
        0 != (flags & FIXINFO_FLAG_VALID)) {
107
329
        if (pflags & FIX_FLAG_3DFIX) {
108
97
            session->newdata.mode = MODE_3D;
109
232
        } else {
110
232
            session->newdata.mode = MODE_2D;
111
232
        }
112
113
329
        if (pflags & FIX_FLAG_DGPS_CORRECTION) {
114
184
            session->newdata.status = STATUS_DGPS;
115
184
        } else {
116
145
            session->newdata.status = STATUS_GPS;
117
145
        }
118
329
    }
119
120
329
    GPSD_LOG(LOG_DATA, &session->context->errout,
121
329
             "NAV_FIX: time=%s, ecef x:%.2f y:%.2f z:%.2f altHAE=%.2f "
122
329
             "speed=%.2f track=%.2f climb=%.2f mode=%d status=%d gdop=%.2f "
123
329
             "pdop=%.2f hdop=%.2f vdop=%.2f tdop=%.2f\n",
124
329
             timespec_str(&session->newdata.time, ts_buf, sizeof(ts_buf)),
125
329
             session->newdata.ecef.x,
126
329
             session->newdata.ecef.y, session->newdata.ecef.z,
127
329
             session->newdata.altHAE, session->newdata.speed,
128
329
             session->newdata.track, session->newdata.climb,
129
329
             session->newdata.mode, session->newdata.status,
130
329
             session->gpsdata.dop.gdop, session->gpsdata.dop.pdop,
131
329
             session->gpsdata.dop.hdop, session->gpsdata.dop.vdop,
132
329
             session->gpsdata.dop.tdop);
133
329
    return mask;
134
514
}
135
136
static gps_mask_t decode_itk_prnstatus(struct gps_device_t *session,
137
                                       unsigned char *buf, size_t len)
138
1.03k
{
139
1.03k
    gps_mask_t mask = 0;
140
1.03k
    unsigned int i, nsv, nchan, st;
141
1.03k
    uint32_t msec;
142
1.03k
    timespec_t ts_tow;
143
1.03k
    char ts_buf[TIMESPEC_LEN];
144
145
1.03k
    if (62 > len) {
146
171
        GPSD_LOG(LOG_PROG, &session->context->errout,
147
171
                 "ITALK: runt PRN_STATUS (len=%zu)\n", len);
148
171
        return mask;
149
171
    }
150
151
868
    msec = getleu32(buf, 7 + 6);
152
153
868
    MSTOTS(&ts_tow, msec);
154
155
868
    session->gpsdata.skyview_time = gpsd_gpstime_resolv(session,
156
868
        (unsigned short)getleu16(buf, 7 + 4), ts_tow);
157
868
    gpsd_zero_satellites(&session->gpsdata);
158
868
    nchan = (unsigned int)getleu16(buf, 7 + 50);
159
868
    if (nchan > MAX_NR_VISIBLE_PRNS) {
160
533
        nchan = MAX_NR_VISIBLE_PRNS;
161
533
    }
162
9.90k
    for (i = st = nsv = 0; i < nchan; i++) {
163
9.04k
        unsigned int off = 7 + 52 + 10 * i;
164
9.04k
        unsigned short flags;
165
9.04k
        bool used;
166
167
9.04k
        flags = (unsigned short) getleu16(buf, off);
168
9.04k
        used = (bool)(flags & PRN_FLAG_USE_IN_NAV);
169
9.04k
        session->gpsdata.skyview[st].PRN =
170
9.04k
            (short)(getleu16(buf, off + 4) & 0xff);
171
9.04k
        session->gpsdata.skyview[st].elevation =
172
9.04k
            (double)(getles16(buf, off + 6) & 0xff);
173
9.04k
        session->gpsdata.skyview[st].azimuth =
174
9.04k
            (double)(getles16(buf, off + 8) & 0xff);
175
9.04k
        session->gpsdata.skyview[st].ss =
176
9.04k
            (double)(getleu16(buf, off + 2) & 0xff);
177
9.04k
        session->gpsdata.skyview[st].used = used;
178
9.04k
        if (session->gpsdata.skyview[st].PRN > 0) {
179
3.98k
            st++;
180
3.98k
            if (used) {
181
2.40k
                nsv++;
182
2.40k
            }
183
3.98k
        }
184
9.04k
    }
185
868
    session->gpsdata.satellites_visible = (int)st;
186
868
    if (MAXCHANNELS < session->gpsdata.satellites_visible) {
187
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
188
0
                "PRN_STTUS: too many satellites %d\n",
189
0
                 session->gpsdata.satellites_visible);
190
0
        session->gpsdata.satellites_visible = MAXCHANNELS;
191
0
    }
192
868
    session->gpsdata.satellites_used = (int)nsv;
193
868
    mask = USED_IS | SATELLITE_SET;
194
195
868
    GPSD_LOG(LOG_DATA, &session->context->errout,
196
868
             "PRN_STATUS: time=%s visible=%d used=%d "
197
868
             "mask={USED|SATELLITE}\n",
198
868
             timespec_str(&session->newdata.time, ts_buf, sizeof(ts_buf)),
199
868
             session->gpsdata.satellites_visible,
200
868
             session->gpsdata.satellites_used);
201
202
868
    return mask;
203
1.03k
}
204
205
static gps_mask_t decode_itk_utcionomodel(struct gps_device_t *session,
206
                                          unsigned char *buf, size_t len)
207
960
{
208
960
    int leap;
209
960
    unsigned short flags;
210
960
    timespec_t ts_tow;
211
960
    uint32_t tow;            // Time of week [ms]
212
960
    char ts_buf[TIMESPEC_LEN];
213
214
960
    if (64 != len) {
215
446
        GPSD_LOG(LOG_PROG, &session->context->errout,
216
446
                 "ITALK: bad UTC_IONO_MODEL (len %zu, should be 64)\n",
217
446
                 len);
218
446
        return 0;
219
446
    }
220
221
514
    flags = (unsigned short) getleu16(buf, 7);
222
514
    if (0 == (flags & UTC_IONO_MODEL_UTCVALID)) {
223
134
        return 0;
224
134
    }
225
226
380
    leap = (int)getleu16(buf, 7 + 24);
227
380
    if (session->context->leap_seconds < leap) {
228
136
        session->context->leap_seconds = leap;
229
136
    }
230
231
380
    tow = getleu32(buf, 7 + 38);    // in ms
232
380
    MSTOTS(&ts_tow, tow);
233
380
    session->newdata.time = gpsd_gpstime_resolv(session,
234
380
        (unsigned short) getleu16(buf, 7 + 36), ts_tow);
235
380
    GPSD_LOG(LOG_DATA, &session->context->errout,
236
380
             "UTC_IONO_MODEL: time=%s mask={TIME}\n",
237
380
             timespec_str(&session->newdata.time, ts_buf, sizeof(ts_buf)));
238
380
    return TIME_SET | NTPTIME_IS;
239
514
}
240
241
static gps_mask_t decode_itk_subframe(struct gps_device_t *session,
242
                                      unsigned char *buf, size_t len)
243
5.51k
{
244
5.51k
    unsigned short flags, prn, sf;
245
5.51k
    unsigned int i;
246
5.51k
    uint32_t words[10];
247
248
5.51k
    if (64 != len) {
249
322
        GPSD_LOG(LOG_PROG, &session->context->errout,
250
322
                 "ITALK: bad SUBFRAME (len %zu, should be 64)\n", len);
251
322
        return 0;
252
322
    }
253
254
5.19k
    flags = (unsigned short) getleu16(buf, 7 + 4);
255
5.19k
    prn = (unsigned short) getleu16(buf, 7 + 6);
256
5.19k
    sf = (unsigned short) getleu16(buf, 7 + 8);
257
5.19k
    GPSD_LOG(LOG_PROG, &session->context->errout,
258
5.19k
             "iTalk 50B SUBFRAME prn %u sf %u - decode %s %s\n",
259
5.19k
             prn, sf,
260
5.19k
             (flags & SUBFRAME_WORD_FLAG_MASK) ? "error" : "ok",
261
5.19k
             (flags & SUBFRAME_GPS_PREAMBLE_INVERTED) ? "(inverted)" : "");
262
5.19k
    if (flags & SUBFRAME_WORD_FLAG_MASK) {
263
135
        return 0;       // don't try decode an erroneous packet
264
135
    }
265
266
    /*
267
     * Timo says "SUBRAME message contains decoded navigation message subframe
268
     * words with parity checking done but parity bits still present."
269
     */
270
55.6k
    for (i = 0; i < 10; i++) {
271
50.5k
        words[i] = (uint32_t)(getleu32(buf, 7 + 14 + 4 * i) >> 6) & 0xffffff;
272
50.5k
    }
273
274
5.05k
    return gpsd_interpret_subframe(session, GNSSID_GPS, prn, words);
275
5.19k
}
276
277
static gps_mask_t decode_itk_pseudo(struct gps_device_t *session,
278
                                    unsigned char *buf, size_t len)
279
893
{
280
893
    unsigned short flags, n, i;
281
893
    unsigned int tow;             // time of week, in ms
282
893
    timespec_t ts_tow;
283
284
893
    n = (unsigned short) getleu16(buf, 7 + 4);
285
893
    if (1 > n ||
286
756
        MAXCHANNELS < n ) {
287
339
        GPSD_LOG(LOG_INF, &session->context->errout,
288
339
                 "ITALK: bad PSEUDO channel count\n");
289
339
        return 0;
290
339
    }
291
292
554
    if ((size_t)((n + 1) * 36) != len) {
293
221
        GPSD_LOG(LOG_WARN, &session->context->errout,
294
221
                 "ITALK: bad PSEUDO len %zu\n", len);
295
221
       return 0;
296
221
    }
297
298
333
    GPSD_LOG(LOG_PROG, &session->context->errout, "iTalk PSEUDO [%u]\n", n);
299
333
    flags = getleu16(buf, 7 + 6);
300
333
    if ((flags & 0x3) != 0x3) {
301
69
        return 0; // bail if measurement time not valid.
302
69
    }
303
304
264
    tow = getleu32(buf, 7 + 38);
305
264
    MSTOTS(&ts_tow, tow);
306
264
    session->newdata.time = gpsd_gpstime_resolv(session,
307
264
        (unsigned short)getleu16((char *)buf, 7 + 8), ts_tow);
308
309
264
    session->gpsdata.raw.mtime = session->newdata.time;
310
311
    // this is so we can tell which never got set
312
60.9k
    for (i = 0; i < MAXCHANNELS; i++) {
313
60.7k
        session->gpsdata.raw.meas[i].svid = 0;
314
60.7k
    }
315
785
    for (i = 0; i < n; i++){
316
521
        session->gpsdata.skyview[i].PRN =
317
521
            getleu16(buf, 7 + 26 + (i*36)) & 0xff;
318
521
        session->gpsdata.skyview[i].ss =
319
521
            getleu16(buf, 7 + 26 + (i*36 + 2)) & 0x3f;
320
521
        session->gpsdata.raw.meas[i].satstat =
321
521
            getleu32(buf, 7 + 26 + (i*36 + 4));
322
521
        session->gpsdata.raw.meas[i].pseudorange =
323
521
            getled64((char *)buf, 7 + 26 + (i*36 + 8));
324
521
        session->gpsdata.raw.meas[i].doppler =
325
521
            getled64((char *)buf, 7 + 26 + (i*36 + 16));
326
521
        session->gpsdata.raw.meas[i].carrierphase =
327
521
            getleu16(buf, 7 + 26 + (i*36 + 28));
328
329
521
        session->gpsdata.raw.meas[i].codephase = NAN;
330
521
        session->gpsdata.raw.meas[i].deltarange = NAN;
331
521
    }
332
    // return RAW_IS; The above decode does not give reasonable results
333
264
    return 0;         // do not report valid until decode is fixed
334
333
}
335
336
static gps_mask_t italk_parse(struct gps_device_t *session,
337
                              unsigned char *buf, size_t len)
338
16.8k
{
339
16.8k
    unsigned int type;
340
16.8k
    gps_mask_t mask = 0;
341
342
16.8k
    if (0 == len) {
343
0
        return 0;
344
0
    }
345
346
16.8k
    type = (unsigned int) getub(buf, 4);
347
    // we may need to dump the raw packet
348
16.8k
    GPSD_LOG(LOG_RAW, &session->context->errout,
349
16.8k
             "raw italk packet type 0x%02x\n", type);
350
351
16.8k
    session->cycle_end_reliable = true;
352
353
16.8k
    switch (type) {
354
1.92k
    case ITALK_NAV_FIX:
355
1.92k
        GPSD_LOG(LOG_DATA, &session->context->errout,
356
1.92k
                 "iTalk NAV_FIX len %zu\n", len);
357
1.92k
        mask = decode_itk_navfix(session, buf, len) | (CLEAR_IS | REPORT_IS);
358
1.92k
        break;
359
1.03k
    case ITALK_PRN_STATUS:
360
1.03k
        GPSD_LOG(LOG_DATA, &session->context->errout,
361
1.03k
                 "iTalk PRN_STATUS len %zu\n", len);
362
1.03k
        mask = decode_itk_prnstatus(session, buf, len);
363
1.03k
        break;
364
960
    case ITALK_UTC_IONO_MODEL:
365
960
        GPSD_LOG(LOG_DATA, &session->context->errout,
366
960
                 "iTalk UTC_IONO_MODEL len %zu\n", len);
367
960
        mask = decode_itk_utcionomodel(session, buf, len);
368
960
        break;
369
370
352
    case ITALK_ACQ_DATA:
371
352
        GPSD_LOG(LOG_DATA, &session->context->errout,
372
352
                 "iTalk ACQ_DATA len %zu\n", len);
373
352
        break;
374
171
    case ITALK_TRACK:
375
171
        GPSD_LOG(LOG_DATA, &session->context->errout,
376
171
                 "iTalk TRACK len %zu\n", len);
377
171
        break;
378
893
    case ITALK_PSEUDO:
379
893
        GPSD_LOG(LOG_DATA, &session->context->errout,
380
893
                 "iTalk PSEUDO len %zu\n", len);
381
893
        mask = decode_itk_pseudo(session, buf, len);
382
893
        break;
383
164
    case ITALK_RAW_ALMANAC:
384
164
        GPSD_LOG(LOG_DATA, &session->context->errout,
385
164
                 "iTalk RAW_ALMANAC len %zu\n", len);
386
164
        break;
387
141
    case ITALK_RAW_EPHEMERIS:
388
141
        GPSD_LOG(LOG_DATA, &session->context->errout,
389
141
                 "iTalk RAW_EPHEMERIS len %zu\n", len);
390
141
        break;
391
5.51k
    case ITALK_SUBFRAME:
392
5.51k
        mask = decode_itk_subframe(session, buf, len);
393
5.51k
        break;
394
137
    case ITALK_BIT_STREAM:
395
137
        GPSD_LOG(LOG_DATA, &session->context->errout,
396
137
                 "iTalk BIT_STREAM len %zu\n", len);
397
137
        break;
398
399
148
    case ITALK_AGC:
400
294
    case ITALK_SV_HEALTH:
401
426
    case ITALK_PRN_PRED:
402
569
    case ITALK_FREQ_PRED:
403
737
    case ITALK_DBGTRACE:
404
875
    case ITALK_START:
405
1.02k
    case ITALK_STOP:
406
1.16k
    case ITALK_SLEEP:
407
1.30k
    case ITALK_STATUS:
408
1.44k
    case ITALK_ITALK_CONF:
409
1.58k
    case ITALK_SYSINFO:
410
1.72k
    case ITALK_ITALK_TASK_ROUTE:
411
1.85k
    case ITALK_PARAM_CTRL:
412
1.98k
    case ITALK_PARAMS_CHANGED:
413
2.15k
    case ITALK_START_COMPLETED:
414
2.28k
    case ITALK_STOP_COMPLETED:
415
2.42k
    case ITALK_LOG_CMD:
416
2.56k
    case ITALK_SYSTEM_START:
417
2.72k
    case ITALK_STOP_SEARCH:
418
2.86k
    case ITALK_SEARCH:
419
3.00k
    case ITALK_PRED_SEARCH:
420
3.17k
    case ITALK_SEARCH_DONE:
421
3.37k
    case ITALK_TRACK_DROP:
422
3.51k
    case ITALK_TRACK_STATUS:
423
3.65k
    case ITALK_HANDOVER_DATA:
424
3.79k
    case ITALK_CORE_SYNC:
425
3.92k
    case ITALK_WAAS_RAWDATA:
426
4.49k
    case ITALK_ASSISTANCE:
427
4.62k
    case ITALK_PULL_FIX:
428
4.82k
    case ITALK_MEMCTRL:
429
4.95k
    case ITALK_STOP_TASK:
430
4.95k
        GPSD_LOG(LOG_DATA, &session->context->errout,
431
4.95k
                 "iTalk not processing packet: id 0x%02x length %zu\n",
432
4.95k
                 type, len);
433
4.95k
        break;
434
646
    default:
435
646
        GPSD_LOG(LOG_DATA, &session->context->errout,
436
16.8k
                 "iTalk unknown packet: id 0x%02x length %zu\n",
437
16.8k
                 type, len);
438
16.8k
    }
439
440
16.8k
    return mask | ONLINE_SET;
441
16.8k
}
442
443
444
static gps_mask_t italk_parse_input(struct gps_device_t *session)
445
16.8k
{
446
16.8k
    if (ITALK_PACKET == session->lexer.type) {
447
16.8k
        return italk_parse(session, session->lexer.outbuffer,
448
16.8k
                           session->lexer.outbuflen);
449
16.8k
    }
450
0
    if (NMEA_PACKET == session->lexer.type) {
451
0
        return nmea_parse((char *)session->lexer.outbuffer, session);
452
0
    }
453
0
    return 0;
454
0
}
455
456
#ifdef __UNUSED__
457
// send a "ping". it may help us detect an itrax more quickly
458
static void italk_ping(struct gps_device_t *session)
459
{
460
    char *ping = "<?>";
461
    (void)gpsd_write(session, ping, 3);
462
}
463
#endif  // __UNUSED__
464
465
// *INDENT-OFF*
466
const struct gps_type_t driver_italk =
467
{
468
    .type_name      = "iTalk",          // full name of type
469
    .packet_type    = ITALK_PACKET,     // associated lexer packet type
470
    .flags          = DRIVER_STICKY,    // no rollover or other flags
471
    .trigger        = NULL,             // recognize the type
472
    .channels       = 12,               // consumer-grade GPS
473
    .probe_detect   = NULL,             // how to detect at startup time
474
    .get_packet     = packet_get1,      // use generic packet grabber
475
    .parse_packet   = italk_parse_input,// parse message packets
476
    .rtcm_writer    = gpsd_write,       // send RTCM data straight
477
    .init_query     = NULL,             // non-perturbing initial query
478
    .event_hook     = NULL,             // lifetime event handler
479
    .speed_switcher = NULL,             // no speed switcher
480
    .mode_switcher  = NULL,             // no mode switcher
481
    .rate_switcher  = NULL,             // no sample-rate switcher
482
    .min_cycle.tv_sec  = 1,             // not relevant, no rate switch
483
    .min_cycle.tv_nsec = 0,             // not relevant, no rate switch
484
    .control_send   = NULL,             // no control string sender
485
    .time_offset     = NULL,            // no method for NTP fudge factor
486
};
487
// *INDENT-ON*
488
#endif  // defined(ITRAX_ENABLE)
489
// vim: set expandtab shiftwidth=4