/src/gpsd/gpsd-3.25.1~dev/drivers/driver_greis.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * A Javad GNSS Receiver External Interface Specification (GREIS) driver. |
3 | | * |
4 | | * Author(s): |
5 | | * - Gregory Fong <gregory.fong@virginorbit.com> |
6 | | * |
7 | | * Documentation for GREIS can be found at: |
8 | | http://www.javad.com/downloads/javadgnss/manuals/GREIS/GREIS_Reference_Guide.pdf |
9 | | * |
10 | | * The version used for reference is that which |
11 | | * "Reflects Firmware Version 3.6.7, Last revised: August 25, 2016". |
12 | | * |
13 | | * This assumes little endian byte order in messages, which is the default, but |
14 | | * that is configurable. A future improvement could change to read the |
15 | | * information in [MF] Message Format. |
16 | | * |
17 | | * This file is Copyright 2017 Virgin Orbit |
18 | | * This file is Copyright 2017 the GPSD project |
19 | | * SPDX-License-Identifier: BSD-2-clause |
20 | | */ |
21 | | |
22 | | #include "../include/gpsd_config.h" // must be before all includes |
23 | | |
24 | | #include <assert.h> |
25 | | #include <math.h> |
26 | | #include <stdbool.h> |
27 | | #include <stdio.h> |
28 | | #include <stdlib.h> // for abs() |
29 | | #include <string.h> |
30 | | #include <sys/select.h> |
31 | | |
32 | | #include "../include/bits.h" |
33 | | #include "../include/driver_greis.h" |
34 | | #include "../include/gpsd.h" |
35 | | #include "../include/timespec.h" |
36 | | |
37 | | #if defined(GREIS_ENABLE) && defined(BINARY_ENABLE) |
38 | | |
39 | 0 | #define HEADER_LENGTH 5 |
40 | | |
41 | | static ssize_t greis_write(struct gps_device_t *session, |
42 | | const char *msg, size_t msglen); |
43 | | static const char disable_messages[] = "\%dm\%dm"; |
44 | | static const char get_vendor[] = "\%vendor\%print,/par/rcv/vendor"; |
45 | | static const char get_ver[] = "\%ver\%print,rcv/ver"; |
46 | | static const char set_update_rate_4hz[] = "\%msint\%set,/par/raw/msint,250"; |
47 | | |
48 | | // Where applicable, the order here is how these will be received per cycle. |
49 | | // TODO: stop hardcoding the cycle time, make it selectable |
50 | | static const char enable_messages_4hz[] = |
51 | | "\%em\%em,,jps/{RT,UO,GT,PV,SG,DP,SI,EL,AZ,EC,SS,ET}:0.25"; |
52 | | |
53 | | /* |
54 | | * GREIS message handlers. The checksum has been already confirmed valid in the |
55 | | * packet acceptance logic, so we don't need to retest it here. |
56 | | */ |
57 | | |
58 | | /** |
59 | | * Handle the message [RE] Reply |
60 | | */ |
61 | | static gps_mask_t greis_msg_RE(struct gps_device_t *session, |
62 | | unsigned char *buf, size_t len) |
63 | 0 | { |
64 | 0 | if (0 == memcmp(buf, "%ver%", 5)) { |
65 | 0 | strlcpy(session->subtype, (const char*)&buf[5], |
66 | 0 | sizeof(session->subtype)); |
67 | 0 | GPSD_LOG(LOG_DATA, &session->context->errout, |
68 | 0 | "GREIS: RE, ->subtype: %s\n", session->subtype); |
69 | 0 | return DEVICEID_SET; |
70 | 0 | } |
71 | | |
72 | 0 | GPSD_LOG(LOG_INFO, &session->context->errout, |
73 | 0 | "GREIS: RE %3zd, reply: %.*s\n", len, (int)len, buf); |
74 | 0 | return 0; |
75 | 0 | } |
76 | | |
77 | | /** |
78 | | * Handle the message [ER] Reply |
79 | | */ |
80 | | static gps_mask_t greis_msg_ER(struct gps_device_t *session, |
81 | | unsigned char *buf, size_t len) |
82 | 0 | { |
83 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
84 | 0 | "GREIS: ER %3zd, reply: %.*s\n", len, (int)len, buf); |
85 | 0 | return 0; |
86 | 0 | } |
87 | | |
88 | | /** |
89 | | * Handle the message [~~](RT) Receiver Time. |
90 | | */ |
91 | | static gps_mask_t greis_msg_RT(struct gps_device_t *session, |
92 | | unsigned char *buf, size_t len) |
93 | 0 | { |
94 | 0 | if (len < 5) { |
95 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
96 | 0 | "GREIS: RT bad len %zu\n", len); |
97 | 0 | return 0; |
98 | 0 | } |
99 | | |
100 | 0 | session->driver.greis.rt_tod = getleu32(buf, 0); |
101 | 0 | memset(&session->gpsdata.raw, 0, sizeof(session->gpsdata.raw)); |
102 | |
|
103 | 0 | session->driver.greis.seen_rt = true; |
104 | 0 | session->driver.greis.seen_az = false; |
105 | 0 | session->driver.greis.seen_ec = false; |
106 | 0 | session->driver.greis.seen_el = false; |
107 | 0 | session->driver.greis.seen_si = false; |
108 | 0 | GPSD_LOG(LOG_DATA, &session->context->errout, |
109 | 0 | "GREIS: RT, tod: %lu\n", |
110 | 0 | (unsigned long)session->driver.greis.rt_tod); |
111 | |
|
112 | 0 | return CLEAR_IS; |
113 | 0 | } |
114 | | |
115 | | /** |
116 | | * Handle the message [UO] GPS UTC Time Parameters. |
117 | | */ |
118 | | static gps_mask_t greis_msg_UO(struct gps_device_t *session, |
119 | | unsigned char *buf, size_t len) |
120 | 0 | { |
121 | | /* |
122 | | * For additional details on these parameters and the computation done using |
123 | | * them, refer to the Javad GREIS spec mentioned at the top of this file and |
124 | | * also to ICD-GPS-200C, Revision IRN-200C-004 April 12, 2000. At the time |
125 | | * of writing, that could be found at |
126 | | * https://www.navcen.uscg.gov/pubs/gps/icd200/ICD200Cw1234.pdf . |
127 | | */ |
128 | 0 | uint32_t tot; // Reference time of week [s] |
129 | 0 | uint16_t wnt; // Reference week number [dimensionless] |
130 | 0 | int8_t dtls; // Delta time due to leap seconds [s] |
131 | 0 | uint8_t dn; // 'Future' reference day number [1..7] |
132 | 0 | uint16_t wnlsf; // 'Future' reference week number [dimensionless] |
133 | 0 | int8_t dtlsf; // 'Future' delta time due to leap seconds [s] |
134 | |
|
135 | 0 | if (len < 24) { |
136 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
137 | 0 | "GREIS: UO bad len %zu\n", len); |
138 | 0 | return 0; |
139 | 0 | } |
140 | | |
141 | 0 | tot = getleu32(buf, 12); |
142 | 0 | wnt = getleu16(buf, 16); |
143 | 0 | dtls = getsb(buf, 18); |
144 | 0 | dn = getub(buf, 19); |
145 | 0 | wnlsf = getleu16(buf, 20); |
146 | 0 | dtlsf = getsb(buf, 22); |
147 | 0 | session->driver.greis.seen_uo = true; |
148 | | |
149 | | /* |
150 | | * See ICD-GPS-200C 20.3.3.5.2.4 "Universal Coordinated Time (UTC)". |
151 | | * I totally ripped this off of driver_navcom.c. Might want to dedupe at |
152 | | * some point. |
153 | | */ |
154 | 0 | if ((wnt % 256U) * 604800U + tot < wnlsf * 604800U + dn * 86400U) { |
155 | | // Current time is before effectivity time of the leap second event |
156 | 0 | session->context->leap_seconds = dtls; |
157 | 0 | } else { |
158 | 0 | session->context->leap_seconds = dtlsf; |
159 | 0 | } |
160 | |
|
161 | 0 | GPSD_LOG(LOG_DATA, &session->context->errout, |
162 | 0 | "GREIS: UO, leap_seconds: %d\n", session->context->leap_seconds); |
163 | |
|
164 | 0 | return 0; |
165 | 0 | } |
166 | | |
167 | | /** |
168 | | * Handle the message [GT] GPS Time. |
169 | | */ |
170 | | static gps_mask_t greis_msg_GT(struct gps_device_t *session, |
171 | | unsigned char *buf, size_t len) |
172 | 0 | { |
173 | 0 | timespec_t ts_tow; |
174 | 0 | uint32_t tow; // Time of week [ms] |
175 | 0 | uint16_t wn; // GPS week number (modulo 1024) [dimensionless] |
176 | 0 | char ts_buf[TIMESPEC_LEN]; |
177 | |
|
178 | 0 | if (len < 7) { |
179 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
180 | 0 | "GREIS: GT bad len %zu\n", len); |
181 | 0 | return 0; |
182 | 0 | } |
183 | | |
184 | 0 | if (!session->driver.greis.seen_uo) { |
185 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
186 | 0 | "GREIS: can't use GT until after UO has supplied " |
187 | 0 | "leap second data\n"); |
188 | 0 | return 0; |
189 | 0 | } |
190 | | |
191 | 0 | tow = getleu32(buf, 0); |
192 | 0 | wn = getleu16(buf, 4); |
193 | |
|
194 | 0 | MSTOTS(&ts_tow, tow); |
195 | 0 | session->newdata.time = gpsd_gpstime_resolv(session, wn, ts_tow); |
196 | |
|
197 | 0 | GPSD_LOG(LOG_DATA, &session->context->errout, |
198 | 0 | "GREIS: GT, tow: %" PRIu32 ", wn: %" PRIu16 ", time: %s Leap:%u\n", |
199 | 0 | tow, wn, |
200 | 0 | timespec_str(&session->newdata.time, ts_buf, sizeof(ts_buf)), |
201 | 0 | session->context->leap_seconds); |
202 | | |
203 | | |
204 | | // save raw.mtime, just in case |
205 | 0 | session->gpsdata.raw.mtime = session->newdata.time; |
206 | |
|
207 | 0 | return TIME_SET | NTPTIME_IS | ONLINE_SET; |
208 | 0 | } |
209 | | |
210 | | /** |
211 | | * Handle the message [PV] Cartesian Position and Velocity. |
212 | | */ |
213 | | static gps_mask_t greis_msg_PV(struct gps_device_t *session, |
214 | | unsigned char *buf, size_t len) |
215 | 0 | { |
216 | 0 | double x, y, z; // Cartesian coordinates [m] |
217 | 0 | float p_sigma; // Position spherical error probability (SEP) [m] |
218 | 0 | float vx, vy, vz; // Cartesian velocities [m/s] |
219 | 0 | float v_sigma; // Velocity SEP [m/s] |
220 | 0 | uint8_t solution_type; |
221 | 0 | gps_mask_t mask = 0; |
222 | |
|
223 | 0 | if (len < 46) { |
224 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
225 | 0 | "GREIS: PV bad len %zu\n", len); |
226 | 0 | return 0; |
227 | 0 | } |
228 | | |
229 | 0 | x = getled64((char *)buf, 0); |
230 | 0 | y = getled64((char *)buf, 8); |
231 | 0 | z = getled64((char *)buf, 16); |
232 | 0 | p_sigma = getlef32((char *)buf, 24); |
233 | 0 | vx = getlef32((char *)buf, 28); |
234 | 0 | vy = getlef32((char *)buf, 32); |
235 | 0 | vz = getlef32((char *)buf, 36); |
236 | 0 | v_sigma = getlef32((char *)buf, 40); |
237 | 0 | solution_type = getub(buf, 44); |
238 | |
|
239 | 0 | session->newdata.ecef.x = x; |
240 | 0 | session->newdata.ecef.y = y; |
241 | 0 | session->newdata.ecef.z = z; |
242 | 0 | session->newdata.ecef.pAcc = p_sigma; |
243 | 0 | session->newdata.ecef.vx = vx; |
244 | 0 | session->newdata.ecef.vy = vy; |
245 | 0 | session->newdata.ecef.vz = vz; |
246 | 0 | session->newdata.ecef.vAcc = v_sigma; |
247 | | |
248 | | // GREIS Reference Guide 3.4.2 "General Notes" part "Solution Types" |
249 | 0 | if (0 < solution_type && |
250 | 0 | 5 > solution_type) { |
251 | 0 | session->newdata.mode = MODE_3D; |
252 | 0 | if (1 < solution_type) { |
253 | 0 | session->newdata.status = STATUS_DGPS; |
254 | 0 | } else { |
255 | 0 | session->newdata.status = STATUS_GPS; |
256 | 0 | } |
257 | 0 | } |
258 | |
|
259 | 0 | GPSD_LOG(LOG_DATA, &session->context->errout, |
260 | 0 | "GREIS: PV, ECEF x=%.2f y=%.2f z=%.2f pAcc=%.2f\n", |
261 | 0 | session->newdata.ecef.x, |
262 | 0 | session->newdata.ecef.y, |
263 | 0 | session->newdata.ecef.z, |
264 | 0 | session->newdata.ecef.pAcc); |
265 | |
|
266 | 0 | GPSD_LOG(LOG_DATA, &session->context->errout, |
267 | 0 | "GREIS: PV, ECEF vx=%.2f vy=%.2f vz=%.2f vAcc=%.2f " |
268 | 0 | "solution_type: %d\n", |
269 | 0 | session->newdata.ecef.vx, |
270 | 0 | session->newdata.ecef.vy, |
271 | 0 | session->newdata.ecef.vz, |
272 | 0 | session->newdata.ecef.vAcc, |
273 | 0 | solution_type); |
274 | |
|
275 | 0 | mask |= MODE_SET | STATUS_SET | ECEF_SET | VECEF_SET; |
276 | 0 | return mask; |
277 | 0 | } |
278 | | |
279 | | /** |
280 | | * Handle the message [SG] Position and Velocity RMS Errors. |
281 | | */ |
282 | | static gps_mask_t greis_msg_SG(struct gps_device_t *session, |
283 | | unsigned char *buf, size_t len) |
284 | 0 | { |
285 | 0 | float hpos; // Horizontal position RMS error [m] |
286 | 0 | float vpos; // Vertical position RMS error [m] |
287 | 0 | float hvel; // Horizontal velocity RMS error [m/s] |
288 | 0 | float vvel; // Vertical velocity RMS error [m/s] |
289 | |
|
290 | 0 | if (len < 18) { |
291 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
292 | 0 | "GREIS: SG bad len %zu\n", len); |
293 | 0 | return 0; |
294 | 0 | } |
295 | | |
296 | 0 | hpos = getlef32((char *)buf, 0); |
297 | 0 | vpos = getlef32((char *)buf, 4); |
298 | 0 | hvel = getlef32((char *)buf, 8); |
299 | 0 | vvel = getlef32((char *)buf, 12); |
300 | | |
301 | | /* |
302 | | * All errors are RMS which can be approximated as 1 sigma, so we can just |
303 | | * use them directly. |
304 | | * |
305 | | * Compute missing items in gpsd_error_model(), not here. |
306 | | */ |
307 | 0 | session->newdata.eph = hpos; |
308 | 0 | session->newdata.epv = vpos; |
309 | 0 | session->newdata.eps = hvel; |
310 | 0 | session->newdata.epc = vvel; |
311 | |
|
312 | 0 | GPSD_LOG(LOG_DATA, &session->context->errout, |
313 | 0 | "GREIS: SG, eph: %.2f, eps: %.2f, epc: %.2f\n", |
314 | 0 | session->newdata.eph, |
315 | 0 | session->newdata.eps, session->newdata.epc); |
316 | |
|
317 | 0 | return HERR_SET | SPEEDERR_SET | CLIMBERR_SET; |
318 | 0 | } |
319 | | |
320 | | /** |
321 | | * Handle the message [DP] Dilution of Precision. |
322 | | * Note that fill_dop() will handle the unset dops later. |
323 | | */ |
324 | | static gps_mask_t greis_msg_DP(struct gps_device_t *session, |
325 | | unsigned char *buf, size_t len) |
326 | 0 | { |
327 | 0 | if (len < 18) { |
328 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
329 | 0 | "GREIS: DP bad len %zu\n", len); |
330 | 0 | return 0; |
331 | 0 | } |
332 | | |
333 | | // clear so that computed DOPs get recomputed. |
334 | 0 | gps_clear_dop(&session->gpsdata.dop); |
335 | |
|
336 | 0 | session->gpsdata.dop.hdop = getlef32((char *)buf, 0); |
337 | 0 | session->gpsdata.dop.vdop = getlef32((char *)buf, 4); |
338 | 0 | session->gpsdata.dop.tdop = getlef32((char *)buf, 8); |
339 | |
|
340 | 0 | session->gpsdata.dop.pdop = sqrt(pow(session->gpsdata.dop.hdop, 2) + |
341 | 0 | pow(session->gpsdata.dop.vdop, 2)); |
342 | |
|
343 | 0 | GPSD_LOG(LOG_DATA, &session->context->errout, |
344 | 0 | "GREIS: DP, hdop: %.2f, vdop: %.2f, tdop: %.2f, pdop: %.2f\n", |
345 | 0 | session->gpsdata.dop.hdop, session->gpsdata.dop.vdop, |
346 | 0 | session->gpsdata.dop.tdop, session->gpsdata.dop.pdop); |
347 | |
|
348 | 0 | return DOP_SET; |
349 | 0 | } |
350 | | |
351 | | /** |
352 | | * Handle the message [SI] Satellite Indices. |
353 | | * |
354 | | * This message tells us how many satellites are seen and contains their |
355 | | * Universal Satellite Identifier (USI). |
356 | | */ |
357 | | static gps_mask_t greis_msg_SI(struct gps_device_t *session, |
358 | | unsigned char *buf, size_t len) |
359 | 0 | { |
360 | 0 | int i; |
361 | |
|
362 | 0 | if (len < 1) { |
363 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
364 | 0 | "GREIS: SI bad len %zu\n", len); |
365 | 0 | return 0; |
366 | 0 | } |
367 | | |
368 | 0 | gpsd_zero_satellites(&session->gpsdata); |
369 | | // FIXME: check against MAXCHANNELS? |
370 | 0 | session->gpsdata.satellites_visible = len - 1; |
371 | 0 | for (i = 0; i < session->gpsdata.satellites_visible; i++) { |
372 | | // This isn't really PRN, this is USI. Convert it. |
373 | 0 | unsigned short PRN = getub(buf, i); |
374 | 0 | session->gpsdata.skyview[i].PRN = PRN; |
375 | | |
376 | | // fit into gnssid:svid |
377 | 0 | if (0 == PRN) { |
378 | | // skip 0 PRN |
379 | 0 | continue; |
380 | 0 | } else if (37 >= PRN) { |
381 | | // GPS, 1 .. 37 |
382 | 0 | session->gpsdata.skyview[i].gnssid = 0; |
383 | 0 | session->gpsdata.skyview[i].svid = PRN; |
384 | 0 | } else if (69 >= PRN) { |
385 | | // GLONASS, 38 .. 69 |
386 | 0 | session->gpsdata.skyview[i].gnssid = 6; |
387 | 0 | session->gpsdata.skyview[i].svid = PRN - 37; |
388 | 0 | } else if (70 == PRN) { |
389 | | // GLONASS, again, 70 |
390 | 0 | session->gpsdata.skyview[i].gnssid = 6; |
391 | 0 | session->gpsdata.skyview[i].svid = 255; |
392 | 0 | } else if (119 >= PRN) { |
393 | | // Galileo, 71 .. 119 |
394 | 0 | session->gpsdata.skyview[i].gnssid = 2; |
395 | 0 | session->gpsdata.skyview[i].svid = PRN - 70; |
396 | 0 | } else if (142 >= PRN) { |
397 | | // SBAS, 120 .. 142 |
398 | 0 | session->gpsdata.skyview[i].gnssid = 1; |
399 | 0 | session->gpsdata.skyview[i].svid = PRN - 119; |
400 | 0 | } else if ((193 <= PRN) && |
401 | 0 | (197 >= PRN)) { |
402 | | // QZSS |
403 | 0 | session->gpsdata.skyview[i].gnssid = 5; |
404 | 0 | session->gpsdata.skyview[i].svid = PRN - 192; |
405 | 0 | } else if ((211 <= PRN) && |
406 | 0 | (247 >= PRN)) { |
407 | | // BeiDou |
408 | 0 | session->gpsdata.skyview[i].gnssid = 3; |
409 | 0 | session->gpsdata.skyview[i].svid = PRN - 210; |
410 | 0 | } |
411 | 0 | session->gpsdata.raw.meas[i].obs_code[0] = '\0'; |
412 | 0 | session->gpsdata.raw.meas[i].gnssid = |
413 | 0 | session->gpsdata.skyview[i].gnssid; |
414 | 0 | session->gpsdata.raw.meas[i].svid = |
415 | 0 | session->gpsdata.skyview[i].svid; |
416 | | // GREIS does not report locktime, so assume max |
417 | 0 | session->gpsdata.raw.meas[i].locktime = LOCKMAX; |
418 | | // Make sure the unused raw fields are set consistently |
419 | 0 | session->gpsdata.raw.meas[i].sigid = 0; |
420 | 0 | session->gpsdata.raw.meas[i].snr = 0; |
421 | 0 | session->gpsdata.raw.meas[i].freqid = 0; |
422 | 0 | session->gpsdata.raw.meas[i].lli = 0; |
423 | 0 | session->gpsdata.raw.meas[i].codephase = NAN; |
424 | 0 | session->gpsdata.raw.meas[i].deltarange = NAN; |
425 | 0 | } |
426 | |
|
427 | 0 | session->driver.greis.seen_si = true; |
428 | 0 | GPSD_LOG(LOG_DATA, &session->context->errout, |
429 | 0 | "GREIS: SI, satellites_visible: %d\n", |
430 | 0 | session->gpsdata.satellites_visible); |
431 | |
|
432 | 0 | return 0; |
433 | 0 | } |
434 | | |
435 | | /** |
436 | | * Handle the message [EL] Satellite Elevations. |
437 | | */ |
438 | | static gps_mask_t greis_msg_EL(struct gps_device_t *session, |
439 | | unsigned char *buf, size_t len) |
440 | 0 | { |
441 | 0 | int i; |
442 | |
|
443 | 0 | if (!session->driver.greis.seen_si) { |
444 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
445 | 0 | "GREIS: can't use EL until after SI provides indices\n"); |
446 | 0 | return 0; |
447 | 0 | } |
448 | | |
449 | | // check against number of satellites + checksum |
450 | 0 | if (len < session->gpsdata.satellites_visible + 1U) { |
451 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
452 | 0 | "GREIS: EL bad len %zu, needed at least %d\n", len, |
453 | 0 | session->gpsdata.satellites_visible + 1); |
454 | 0 | return 0; |
455 | 0 | } |
456 | | |
457 | 0 | for (i = 0; i < session->gpsdata.satellites_visible; i++) { |
458 | 0 | short elevation; |
459 | | |
460 | | // GREIS elevation is -90 to 90 degrees |
461 | | // GREIS uses 127 for n/a |
462 | | // gpsd uses NAN for n/a, so adjust accordingly |
463 | 0 | elevation = getub(buf, i); |
464 | 0 | if (90 < abs(elevation)) { |
465 | 0 | session->gpsdata.skyview[i].elevation = (double)elevation; |
466 | 0 | } // else leave as NAN |
467 | 0 | } |
468 | |
|
469 | 0 | session->driver.greis.seen_el = true; |
470 | 0 | GPSD_LOG(LOG_DATA, &session->context->errout, "GREIS: EL\n"); |
471 | |
|
472 | 0 | return 0; |
473 | 0 | } |
474 | | |
475 | | /** |
476 | | * Handle the message [AZ] Satellite Azimuths. |
477 | | */ |
478 | | static gps_mask_t greis_msg_AZ(struct gps_device_t *session, |
479 | | unsigned char *buf, size_t len) |
480 | 0 | { |
481 | 0 | int i; |
482 | |
|
483 | 0 | if (!session->driver.greis.seen_si) { |
484 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
485 | 0 | "GREIS: can't use AZ until after SI provides indices\n"); |
486 | 0 | return 0; |
487 | 0 | } |
488 | | |
489 | | // check against number of satellites + checksum |
490 | 0 | if (len < session->gpsdata.satellites_visible + 1U) { |
491 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
492 | 0 | "GREIS: AZ bad len %zu, needed at least %d\n", len, |
493 | 0 | session->gpsdata.satellites_visible + 1); |
494 | 0 | return 0; |
495 | 0 | } |
496 | | |
497 | 0 | for (i = 0; i < session->gpsdata.satellites_visible; i++) { |
498 | 0 | short azimuth; |
499 | | |
500 | | /* GREIS azimuth is 0 to 180, multiply by 2 for 0 to 360 |
501 | | * GREIS uses 255 for n/a |
502 | | * gpsd azimuth is 0 to 359, so adjust accordingly */ |
503 | 0 | azimuth = getub(buf, i) * 2; |
504 | 0 | if (360 == azimuth) { |
505 | 0 | session->gpsdata.skyview[i].azimuth = 0; |
506 | 0 | } else if (0 <= azimuth && |
507 | 0 | 360 > azimuth) { |
508 | 0 | session->gpsdata.skyview[i].azimuth = (double)azimuth; |
509 | 0 | } // else leave as NAN |
510 | 0 | } |
511 | |
|
512 | 0 | session->driver.greis.seen_az = true; |
513 | 0 | GPSD_LOG(LOG_DATA, &session->context->errout, "GREIS: AZ\n"); |
514 | |
|
515 | 0 | return 0; |
516 | 0 | } |
517 | | |
518 | | /** |
519 | | * Handle the message [DC] Doppler (CA/L1) |
520 | | */ |
521 | | static gps_mask_t greis_msg_DC(struct gps_device_t *session, |
522 | | unsigned char *buf, size_t len) |
523 | 0 | { |
524 | 0 | int i; |
525 | 0 | size_t len_needed = (session->gpsdata.satellites_visible * 4) + 1; |
526 | |
|
527 | 0 | if (!session->driver.greis.seen_si) { |
528 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
529 | 0 | "GREIS: can't use DC until after SI provides indices\n"); |
530 | 0 | return 0; |
531 | 0 | } |
532 | | |
533 | | // check against number of satellites + checksum |
534 | 0 | if (len < len_needed) { |
535 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
536 | 0 | "GREIS: DC bad len %zu, needed at least %zu\n", len, |
537 | 0 | len_needed); |
538 | 0 | return 0; |
539 | 0 | } |
540 | | |
541 | 0 | for (i = 0; i < session->gpsdata.satellites_visible; i++) { |
542 | 0 | long int_doppler = getles32((char *)buf, i * 4); |
543 | 0 | if (0x7fffffff == int_doppler) { |
544 | | // out of range |
545 | 0 | session->gpsdata.raw.meas[i].doppler = NAN; |
546 | 0 | } else { |
547 | 0 | session->gpsdata.raw.meas[i].doppler = int_doppler * 1e-4; |
548 | 0 | } |
549 | 0 | } |
550 | |
|
551 | 0 | session->driver.greis.seen_raw = true; |
552 | 0 | GPSD_LOG(LOG_DATA, &session->context->errout, "GREIS: DC\n"); |
553 | |
|
554 | 0 | return 0; |
555 | 0 | } |
556 | | |
557 | | /** |
558 | | * Handle the message [EC] SNR (CA/L1). |
559 | | * EC really outputs CNR, but what gpsd refers to as SNR _is_ CNR. |
560 | | */ |
561 | | static gps_mask_t greis_msg_EC(struct gps_device_t *session, |
562 | | unsigned char *buf, size_t len) |
563 | 0 | { |
564 | 0 | int i; |
565 | |
|
566 | 0 | if (!session->driver.greis.seen_si) { |
567 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
568 | 0 | "GREIS: can't use EC until after SI provides indices\n"); |
569 | 0 | return 0; |
570 | 0 | } |
571 | | |
572 | | // check against number of satellites + checksum |
573 | 0 | if (len < session->gpsdata.satellites_visible + 1U) { |
574 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
575 | 0 | "GREIS: EC bad len %zu, needed at least %d\n", len, |
576 | 0 | session->gpsdata.satellites_visible + 1); |
577 | 0 | return 0; |
578 | 0 | } |
579 | | |
580 | 0 | for (i = 0; i < session->gpsdata.satellites_visible; i++) |
581 | 0 | session->gpsdata.skyview[i].ss = getub(buf, i); |
582 | |
|
583 | 0 | session->driver.greis.seen_ec = true; |
584 | 0 | GPSD_LOG(LOG_DATA, &session->context->errout, "GREIS: EC\n"); |
585 | |
|
586 | 0 | return 0; |
587 | 0 | } |
588 | | |
589 | | |
590 | | /** |
591 | | * Handle the message [P3] CA/L2 Carrier Phases, RINEX L2C |
592 | | */ |
593 | | static gps_mask_t greis_msg_P3(struct gps_device_t *session, |
594 | | unsigned char *buf, size_t len) |
595 | 0 | { |
596 | 0 | int i; |
597 | 0 | size_t len_needed = (session->gpsdata.satellites_visible * 8) + 1; |
598 | |
|
599 | 0 | if (!session->driver.greis.seen_si) { |
600 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
601 | 0 | "GREIS: can't use P3 until after SI provides indices\n"); |
602 | 0 | return 0; |
603 | 0 | } |
604 | | |
605 | | // check against number of satellites + checksum |
606 | 0 | if (len < len_needed) { |
607 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
608 | 0 | "GREIS: P3 bad len %zu, needed at least %zu\n", len, |
609 | 0 | len_needed); |
610 | 0 | return 0; |
611 | 0 | } |
612 | | |
613 | 0 | for (i = 0; i < session->gpsdata.satellites_visible; i++) { |
614 | 0 | session->gpsdata.raw.meas[i].l2c = getled64((char *)buf, i * 8); |
615 | 0 | } |
616 | |
|
617 | 0 | session->driver.greis.seen_raw = true; |
618 | 0 | GPSD_LOG(LOG_DATA, &session->context->errout, "GREIS: P3\n"); |
619 | |
|
620 | 0 | return 0; |
621 | 0 | } |
622 | | |
623 | | /** |
624 | | * Handle the message [PC] CA/L1 Carrier Phases, RINEX L1C |
625 | | */ |
626 | | static gps_mask_t greis_msg_PC(struct gps_device_t *session, |
627 | | unsigned char *buf, size_t len) |
628 | 0 | { |
629 | 0 | int i; |
630 | 0 | size_t len_needed = (session->gpsdata.satellites_visible * 8) + 1; |
631 | |
|
632 | 0 | if (!session->driver.greis.seen_si) { |
633 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
634 | 0 | "GREIS: can't use PC until after SI provides indices\n"); |
635 | 0 | return 0; |
636 | 0 | } |
637 | | |
638 | | // check against number of satellites + checksum |
639 | 0 | if (len < len_needed) { |
640 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
641 | 0 | "GREIS: PC bad len %zu, needed at least %zu\n", len, |
642 | 0 | len_needed); |
643 | 0 | return 0; |
644 | 0 | } |
645 | | |
646 | 0 | for (i = 0; i < session->gpsdata.satellites_visible; i++) { |
647 | 0 | session->gpsdata.raw.meas[i].carrierphase = getled64((char *)buf, |
648 | 0 | i * 8); |
649 | 0 | } |
650 | |
|
651 | 0 | session->driver.greis.seen_raw = true; |
652 | 0 | GPSD_LOG(LOG_DATA, &session->context->errout, "GREIS: PC\n"); |
653 | |
|
654 | 0 | return 0; |
655 | 0 | } |
656 | | |
657 | | /** |
658 | | * Handle the message [R3] CA/L2 Pseudo-range, RINEX C2C |
659 | | */ |
660 | | static gps_mask_t greis_msg_R3(struct gps_device_t *session, |
661 | | unsigned char *buf, size_t len) |
662 | 0 | { |
663 | 0 | int i; |
664 | 0 | size_t len_needed = (session->gpsdata.satellites_visible * 8) + 1; |
665 | |
|
666 | 0 | if (!session->driver.greis.seen_si) { |
667 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
668 | 0 | "GREIS: can't use R3 until after SI provides indices\n"); |
669 | 0 | return 0; |
670 | 0 | } |
671 | | |
672 | | // check against number of satellites + checksum |
673 | 0 | if (len < len_needed) { |
674 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
675 | 0 | "GREIS: R3 bad len %zu, needed at least %zu\n", len, |
676 | 0 | len_needed); |
677 | 0 | return 0; |
678 | 0 | } |
679 | | |
680 | 0 | for (i = 0; i < session->gpsdata.satellites_visible; i++) { |
681 | | // get, and convert to meters |
682 | 0 | session->gpsdata.raw.meas[i].c2c = \ |
683 | 0 | getled64((char *)buf, i * 8) * CLIGHT; |
684 | 0 | } |
685 | |
|
686 | 0 | session->driver.greis.seen_raw = true; |
687 | 0 | GPSD_LOG(LOG_DATA, &session->context->errout, "GREIS: R3\n"); |
688 | |
|
689 | 0 | return 0; |
690 | 0 | } |
691 | | |
692 | | /** |
693 | | * Handle the message [RC] Pseudo-range CA/L1, RINEX C1C |
694 | | */ |
695 | | static gps_mask_t greis_msg_RC(struct gps_device_t *session, |
696 | | unsigned char *buf, size_t len) |
697 | 0 | { |
698 | 0 | int i; |
699 | 0 | size_t len_needed = (session->gpsdata.satellites_visible * 8) + 1; |
700 | |
|
701 | 0 | if (!session->driver.greis.seen_si) { |
702 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
703 | 0 | "GREIS: can't use RC until after SI provides indices\n"); |
704 | 0 | return 0; |
705 | 0 | } |
706 | | |
707 | | // check against number of satellites + checksum |
708 | 0 | if (len < len_needed) { |
709 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
710 | 0 | "GREIS: RC bad len %zu, needed at least %zu\n", len, |
711 | 0 | len_needed); |
712 | 0 | return 0; |
713 | 0 | } |
714 | | |
715 | 0 | for (i = 0; i < session->gpsdata.satellites_visible; i++) { |
716 | | // get, and convert to meters |
717 | 0 | session->gpsdata.raw.meas[i].pseudorange = \ |
718 | 0 | getled64((char *)buf, i * 8) * CLIGHT; |
719 | 0 | } |
720 | |
|
721 | 0 | session->driver.greis.seen_raw = true; |
722 | 0 | GPSD_LOG(LOG_DATA, &session->context->errout, "GREIS: RC\n"); |
723 | |
|
724 | 0 | return 0; |
725 | 0 | } |
726 | | |
727 | | /** |
728 | | * Handle the message [SS] Satellite Navigation Status. |
729 | | */ |
730 | | static gps_mask_t greis_msg_SS(struct gps_device_t *session, |
731 | | unsigned char *buf, size_t len) |
732 | 0 | { |
733 | 0 | int i; |
734 | 0 | int used_count = 0; |
735 | |
|
736 | 0 | if (!session->driver.greis.seen_si) { |
737 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
738 | 0 | "GREIS: can't use SS until after SI provides indices\n"); |
739 | 0 | return 0; |
740 | 0 | } |
741 | | |
742 | | // check against number of satellites + solution type + checksum |
743 | 0 | if (len < session->gpsdata.satellites_visible + 2U) { |
744 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
745 | 0 | "GREIS: SI bad len %zu, needed at least %d\n", len, |
746 | 0 | session->gpsdata.satellites_visible + 2); |
747 | 0 | return 0; |
748 | 0 | } |
749 | | |
750 | 0 | for (i = 0; i < session->gpsdata.satellites_visible; i++) { |
751 | | /* |
752 | | * From the GREIS Reference Guide: "Codes [0...3], [40...62], and |
753 | | * [64...255] indicate that given satellite is used in position |
754 | | * computation and show which measurements are used. The rest of codes |
755 | | * indicate that satellite is not used in position computation and |
756 | | * indicate why this satellite is excluded from position computation." |
757 | | * Refer to Table 3-4 "Satellite Navigation Status" for the specific |
758 | | * code meanings. |
759 | | */ |
760 | 0 | uint8_t nav_status = getub(buf, i); |
761 | 0 | session->gpsdata.skyview[i].used = |
762 | 0 | (nav_status <= 3) || |
763 | 0 | (nav_status >= 40 && nav_status <= 62) || |
764 | 0 | (nav_status >= 64); |
765 | |
|
766 | 0 | if (session->gpsdata.skyview[i].used) |
767 | 0 | used_count++; |
768 | 0 | } |
769 | 0 | session->gpsdata.satellites_used = used_count; |
770 | |
|
771 | 0 | GPSD_LOG(LOG_DATA, &session->context->errout, |
772 | 0 | "GREIS: SS, satellites_used: %d\n", |
773 | 0 | session->gpsdata.satellites_used); |
774 | |
|
775 | 0 | return used_count ? USED_IS : 0; |
776 | 0 | } |
777 | | |
778 | | |
779 | | /** |
780 | | * Handle the message [::](ET) Epoch Time. |
781 | | * This should be kept as the last message in each epoch. |
782 | | */ |
783 | | static gps_mask_t greis_msg_ET(struct gps_device_t *session, |
784 | | unsigned char *buf, size_t len) |
785 | 0 | { |
786 | 0 | uint32_t tod; |
787 | 0 | gps_mask_t mask = 0; |
788 | |
|
789 | 0 | if (len < 5) { |
790 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
791 | 0 | "GREIS: ET bad len %zu\n", len); |
792 | 0 | return 0; |
793 | 0 | } |
794 | | |
795 | 0 | if (!session->driver.greis.seen_rt) { |
796 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
797 | 0 | "GREIS: got ET, but no preceding RT for epoch\n"); |
798 | 0 | return 0; |
799 | 0 | } |
800 | | |
801 | 0 | tod = getleu32(buf, 0); |
802 | 0 | if (tod != session->driver.greis.rt_tod) { |
803 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
804 | 0 | "GREIS: broken epoch, RT had %lu, but ET has %lu\n", |
805 | 0 | (unsigned long)session->driver.greis.rt_tod, |
806 | 0 | (unsigned long)tod); |
807 | 0 | return 0; |
808 | 0 | } |
809 | | |
810 | | // Skyview time does not differ from time in GT message |
811 | 0 | session->gpsdata.skyview_time.tv_sec = 0; |
812 | 0 | session->gpsdata.skyview_time.tv_nsec = 0; |
813 | |
|
814 | 0 | GPSD_LOG(LOG_DATA, &session->context->errout, |
815 | 0 | "GREIS: ET, seen: az %d, ec %d, el %d, rt %d, si %d, uo %d\n", |
816 | 0 | (int)session->driver.greis.seen_az, |
817 | 0 | (int)session->driver.greis.seen_ec, |
818 | 0 | (int)session->driver.greis.seen_el, |
819 | 0 | (int)session->driver.greis.seen_rt, |
820 | 0 | (int)session->driver.greis.seen_si, |
821 | 0 | (int)session->driver.greis.seen_uo); |
822 | | |
823 | | // Make sure we got the satellite data, then report it. |
824 | 0 | if ((session->driver.greis.seen_az && session->driver.greis.seen_ec && |
825 | 0 | session->driver.greis.seen_el && session->driver.greis.seen_si)) { |
826 | | // Skyview seen, update it. Go even if no seen_ss or none visible |
827 | 0 | mask |= SATELLITE_SET; |
828 | |
|
829 | 0 | if (session->driver.greis.seen_raw) { |
830 | 0 | mask |= RAW_IS; |
831 | 0 | } else { |
832 | 0 | session->gpsdata.raw.mtime.tv_sec = 0; |
833 | 0 | session->gpsdata.raw.mtime.tv_nsec = 0; |
834 | 0 | } |
835 | |
|
836 | 0 | } else { |
837 | 0 | session->gpsdata.raw.mtime.tv_sec = 0; |
838 | 0 | session->gpsdata.raw.mtime.tv_nsec = 0; |
839 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
840 | 0 | "GREIS: ET: missing satellite details in this epoch\n"); |
841 | 0 | } |
842 | |
|
843 | 0 | GPSD_LOG(LOG_DATA, &session->context->errout, "GREIS: ET, tod: %lu\n", |
844 | 0 | (unsigned long)tod); |
845 | | |
846 | | /* This is a good place to poll firmware version if we need it. |
847 | | * Waited until now to avoid the startup rush and out of |
848 | | * critical time path |
849 | | */ |
850 | 0 | if (0 == strnlen(session->subtype, sizeof(session->subtype))) { |
851 | | // get version |
852 | 0 | (void)greis_write(session, get_ver, sizeof(get_ver) - 1); |
853 | 0 | } |
854 | | /* The driver waits for ET to send any reports |
855 | | * Just REPORT_IS is not enough to trigger sending of reports to clients. |
856 | | * STATUS_SET seems best, if no status by now the status is no fix */ |
857 | 0 | return mask | REPORT_IS | STATUS_SET; |
858 | 0 | } |
859 | | |
860 | | struct dispatch_table_entry { |
861 | | char id0; |
862 | | char id1; |
863 | | gps_mask_t (*handler)(struct gps_device_t *, unsigned char *, size_t); |
864 | | }; |
865 | | |
866 | | static struct dispatch_table_entry dispatch_table[] = { |
867 | | {':', ':', greis_msg_ET}, |
868 | | {'A', 'Z', greis_msg_AZ}, |
869 | | {'D', 'C', greis_msg_DC}, |
870 | | {'D', 'P', greis_msg_DP}, |
871 | | {'E', 'C', greis_msg_EC}, |
872 | | {'E', 'R', greis_msg_ER}, |
873 | | {'E', 'L', greis_msg_EL}, |
874 | | {'G', 'T', greis_msg_GT}, |
875 | | {'R', '3', greis_msg_R3}, |
876 | | {'R', 'C', greis_msg_RC}, |
877 | | {'P', '3', greis_msg_P3}, |
878 | | {'P', 'C', greis_msg_PC}, |
879 | | {'P', 'V', greis_msg_PV}, |
880 | | {'R', 'E', greis_msg_RE}, |
881 | | {'S', 'G', greis_msg_SG}, |
882 | | {'S', 'I', greis_msg_SI}, |
883 | | {'S', 'S', greis_msg_SS}, |
884 | | {'U', 'O', greis_msg_UO}, |
885 | | {'~', '~', greis_msg_RT}, |
886 | | }; |
887 | | |
888 | 0 | #define dispatch_table_size (sizeof(dispatch_table) / sizeof(dispatch_table[0])) |
889 | | |
890 | | /** |
891 | | * Parse the data from the device |
892 | | */ |
893 | | static gps_mask_t greis_dispatch(struct gps_device_t *session, |
894 | | unsigned char *buf, size_t len) |
895 | 0 | { |
896 | 0 | size_t i; |
897 | 0 | char id0, id1; |
898 | |
|
899 | 0 | if (len == 0) |
900 | 0 | return 0; |
901 | | |
902 | | /* |
903 | | * This is set because the device reliably signals end of cycle. |
904 | | * The core library zeroes it just before it calls each driver's |
905 | | * packet analyzer. |
906 | | */ |
907 | 0 | session->cycle_end_reliable = true; |
908 | | |
909 | | // Length should have already been checked in packet.c, but just in case |
910 | 0 | if (len < HEADER_LENGTH) { |
911 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
912 | 0 | "GREIS: Packet length %zu shorter than min length\n", len); |
913 | 0 | return 0; |
914 | 0 | } |
915 | | |
916 | | // we may need to dump the raw packet |
917 | 0 | GPSD_LOG(LOG_RAW, &session->context->errout, |
918 | 0 | "GREIS: raw packet id '%c%c'\n", buf[0], buf[1]); |
919 | |
|
920 | 0 | id0 = buf[0]; |
921 | 0 | id1 = buf[1]; |
922 | 0 | len -= HEADER_LENGTH; |
923 | 0 | buf += HEADER_LENGTH; |
924 | |
|
925 | 0 | for (i = 0; i < dispatch_table_size; i++) { |
926 | 0 | struct dispatch_table_entry *entry = &dispatch_table[i]; |
927 | |
|
928 | 0 | if (id0 == entry->id0 && id1 == entry->id1) { |
929 | 0 | return entry->handler(session, buf, len); |
930 | 0 | } |
931 | 0 | } |
932 | | |
933 | 0 | GPSD_LOG(LOG_WARN, &session->context->errout, |
934 | 0 | "GREIS: unknown packet id '%c%c' length %zu\n", id0, id1, len); |
935 | 0 | return 0; |
936 | 0 | } |
937 | | |
938 | | /********************************************************** |
939 | | * |
940 | | * Externally called routines below here |
941 | | * |
942 | | **********************************************************/ |
943 | | |
944 | | /** |
945 | | * Write data to the device with checksum. |
946 | | * Returns number of bytes written on successful write, -1 otherwise. |
947 | | */ |
948 | | static ssize_t greis_write(struct gps_device_t *session, |
949 | | const char *msg, size_t msglen) |
950 | 0 | { |
951 | 0 | char checksum_str[3] = {0}; |
952 | 0 | ssize_t count; |
953 | |
|
954 | 0 | if (session->context->readonly) { |
955 | | // readonly mode, do not write anything |
956 | 0 | return -1; |
957 | 0 | } |
958 | | |
959 | 0 | if (NULL == msg) { |
960 | | /* We do sometimes write zero length to wake up GPS, |
961 | | * so just test for NULL msg, not zero length message */ |
962 | 0 | GPSD_LOG(LOG_ERROR, &session->context->errout, |
963 | 0 | "GREIS: nothing to write\n"); |
964 | 0 | return -1; |
965 | 0 | } |
966 | | |
967 | | // Account for length + checksum marker + checksum + \r + \n + \0 |
968 | 0 | if (msglen + 6 > sizeof(session->msgbuf)) { |
969 | 0 | GPSD_LOG(LOG_ERROR, &session->context->errout, |
970 | 0 | "GREIS: msgbuf is smaller than write length %zu\n", msglen); |
971 | 0 | return -1; |
972 | 0 | } |
973 | | |
974 | 0 | memcpy(&session->msgbuf[0], msg, msglen); |
975 | |
|
976 | 0 | if (msglen == 0) { |
977 | | // This is a dummy write, don't give a checksum. |
978 | 0 | session->msgbuf[0] = '\n'; |
979 | 0 | session->msgbuflen = 1; |
980 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
981 | 0 | "GREIS: Dummy write\n"); |
982 | 0 | } else { |
983 | 0 | unsigned char checksum; |
984 | |
|
985 | 0 | session->msgbuflen = msglen; |
986 | 0 | session->msgbuf[session->msgbuflen++] = '@'; // checksum marker |
987 | | |
988 | | // calculate checksum with @, place at end, and set length to write |
989 | 0 | checksum = greis_checksum((unsigned char *)session->msgbuf, |
990 | 0 | session->msgbuflen); |
991 | 0 | (void)snprintf(checksum_str, sizeof(checksum_str), "%02X", checksum); |
992 | 0 | session->msgbuf[session->msgbuflen++] = checksum_str[0]; |
993 | 0 | session->msgbuf[session->msgbuflen++] = checksum_str[1]; |
994 | 0 | session->msgbuf[session->msgbuflen++] = '\r'; |
995 | 0 | session->msgbuf[session->msgbuflen++] = '\n'; |
996 | |
|
997 | 0 | GPSD_LOG(LOG_PROG, &session->context->errout, |
998 | 0 | "GREIS: Writing command '%.*s', checksum: %s\n", |
999 | 0 | (int)msglen, msg, checksum_str); |
1000 | 0 | } |
1001 | 0 | session->msgbuf[session->msgbuflen] = '\0'; |
1002 | 0 | count = gpsd_write(session, session->msgbuf, session->msgbuflen); |
1003 | |
|
1004 | 0 | if (count != (ssize_t)session->msgbuflen) { |
1005 | 0 | return -1; |
1006 | 0 | } // else |
1007 | | |
1008 | 0 | return count; |
1009 | 0 | } |
1010 | | |
1011 | | /** |
1012 | | * Write data to the device, doing any required padding or checksumming |
1013 | | */ |
1014 | | static ssize_t greis_control_send(struct gps_device_t *session, |
1015 | | char *msg, size_t msglen) |
1016 | 0 | { |
1017 | 0 | return greis_write(session, msg, msglen); |
1018 | 0 | } |
1019 | | |
1020 | | static void greis_event_hook(struct gps_device_t *session, event_t event) |
1021 | 0 | { |
1022 | 0 | if (session->context->readonly || |
1023 | 0 | session->context->passive) { |
1024 | 0 | return; |
1025 | 0 | } |
1026 | | |
1027 | 0 | if (event == event_wakeup) { |
1028 | | /* |
1029 | | * Code to make the device ready to communicate. Only needed if the |
1030 | | * device is in some kind of sleeping state, and only shipped to |
1031 | | * RS232C, so that gpsd won't send strings to unidentified USB devices |
1032 | | * that might not be GPSes at all. |
1033 | | */ |
1034 | | |
1035 | | /* |
1036 | | * Disable any existing messages, then request vendor for |
1037 | | * identification. |
1038 | | */ |
1039 | 0 | (void)greis_write(session, disable_messages, |
1040 | 0 | sizeof(disable_messages) - 1); |
1041 | 0 | (void)greis_write(session, get_vendor, sizeof(get_vendor) - 1); |
1042 | 0 | } else if (event == event_identified || |
1043 | 0 | event == event_reactivate) { |
1044 | | /* |
1045 | | * Fires when the first full packet is recognized from a previously |
1046 | | * unidentified device OR the device is reactivated after close. The |
1047 | | * session.lexer counter is zeroed. |
1048 | | * |
1049 | | * TODO: If possible, get the software version and store it in |
1050 | | * session->subtype. |
1051 | | */ |
1052 | 0 | (void)greis_write(session, disable_messages, |
1053 | 0 | sizeof(disable_messages) - 1); |
1054 | 0 | (void)greis_write(session, set_update_rate_4hz, |
1055 | 0 | sizeof(set_update_rate_4hz) - 1); |
1056 | 0 | (void)greis_write(session, enable_messages_4hz, |
1057 | 0 | sizeof(enable_messages_4hz) - 1); |
1058 | | |
1059 | | // Store (expected) cycle time (seconds) |
1060 | 0 | session->gpsdata.dev.cycle.tv_sec = 0; |
1061 | 0 | session->gpsdata.dev.cycle.tv_nsec = 250000000L; |
1062 | 0 | } else if (event == event_driver_switch) { |
1063 | | /* |
1064 | | * Fires when the driver on a device is changed *after* it |
1065 | | * has been identified. |
1066 | | */ |
1067 | 0 | } else if (event == event_deactivate) { |
1068 | | /* |
1069 | | * Fires when the device is deactivated. Use this to revert |
1070 | | * whatever was done at event_identified and event_configure |
1071 | | * time. |
1072 | | */ |
1073 | 0 | (void)greis_write(session, disable_messages, |
1074 | 0 | sizeof(disable_messages) - 1); |
1075 | 0 | } |
1076 | 0 | } |
1077 | | |
1078 | | /** |
1079 | | * This is the entry point to the driver. When the packet sniffer recognizes |
1080 | | * a packet for this driver, it calls this method which passes the packet to |
1081 | | * the binary processor or the nmea processor, depending on the session type. |
1082 | | */ |
1083 | | static gps_mask_t greis_parse_input(struct gps_device_t *session) |
1084 | 0 | { |
1085 | 0 | if (GREIS_PACKET == session->lexer.type) { |
1086 | 0 | return greis_dispatch(session, session->lexer.outbuffer, |
1087 | 0 | session->lexer.outbuflen); |
1088 | 0 | } |
1089 | 0 | if (NMEA_PACKET == session->lexer.type) { |
1090 | 0 | return nmea_parse((char *)session->lexer.outbuffer, session); |
1091 | 0 | } |
1092 | 0 | return 0; |
1093 | 0 | } |
1094 | | |
1095 | | /** |
1096 | | * Set port operating mode, speed, parity, stopbits etc. here. |
1097 | | * Note: parity is passed as 'N'/'E'/'O', but you should program |
1098 | | * defensively and allow 0/1/2 as well. |
1099 | | */ |
1100 | | static bool greis_set_speed(struct gps_device_t *session, |
1101 | | speed_t speed, char parity, int stopbits) |
1102 | 0 | { |
1103 | | // change on current port |
1104 | 0 | static const char set_rate[] = "set,/par/cur/term/rate,"; |
1105 | 0 | static const char set_parity[] = "set,/par/cur/term/parity,"; |
1106 | 0 | static const char set_stops[] = "set,/par/cur/term/stops,"; |
1107 | 0 | static const char parity_none[] = "N"; |
1108 | 0 | static const char parity_even[] = "even"; |
1109 | 0 | static const char parity_odd[] = "odd"; |
1110 | |
|
1111 | 0 | char command[BUFSIZ] = {0}; |
1112 | 0 | const char *selected_parity = NULL; |
1113 | |
|
1114 | 0 | switch (parity) { |
1115 | 0 | case 'N': |
1116 | 0 | case 0: |
1117 | 0 | selected_parity = parity_none; |
1118 | 0 | break; |
1119 | 0 | case 'E': |
1120 | 0 | case 1: |
1121 | 0 | selected_parity = parity_even; |
1122 | 0 | break; |
1123 | 0 | case 'O': |
1124 | 0 | case 2: |
1125 | 0 | selected_parity = parity_odd; |
1126 | 0 | break; |
1127 | 0 | default: |
1128 | 0 | return false; |
1129 | 0 | } |
1130 | | |
1131 | 0 | (void)snprintf(command, sizeof(command) - 1, "%s%lu && %s%s && %s%d", |
1132 | 0 | set_rate, (unsigned long)speed, set_parity, selected_parity, |
1133 | 0 | set_stops, stopbits); |
1134 | 0 | return (bool)greis_write(session, command, |
1135 | 0 | strnlen(command, sizeof(command))); |
1136 | 0 | } |
1137 | | |
1138 | | #if 0 |
1139 | | /** |
1140 | | * TODO: Switch between NMEA and binary mode |
1141 | | */ |
1142 | | static void greis_set_mode(struct gps_device_t *session, int mode) |
1143 | | { |
1144 | | if (mode == MODE_NMEA) { |
1145 | | // send a mode switch control string |
1146 | | } else { |
1147 | | // send a mode switch control string |
1148 | | } |
1149 | | } |
1150 | | #endif |
1151 | | |
1152 | | #if 0 // TODO |
1153 | | static double greis_time_offset(struct gps_device_t *session) |
1154 | | { |
1155 | | /* |
1156 | | * If NTP notification is enabled, the GPS will occasionally NTP |
1157 | | * its notion of the time. This will lag behind actual time by |
1158 | | * some amount which has to be determined by observation vs. (say |
1159 | | * WWVB radio broadcasts) and, furthermore, may differ by baud |
1160 | | * rate. This method is for computing the NTP fudge factor. If |
1161 | | * it's absent, an offset of 0.0 will be assumed, effectively |
1162 | | * falling back on what's in ntp.conf. When it returns NAN, |
1163 | | * nothing will be sent to NTP. |
1164 | | */ |
1165 | | return MAGIC_CONSTANT; |
1166 | | } |
1167 | | #endif |
1168 | | |
1169 | | // This is everything we export |
1170 | | // *INDENT-OFF* |
1171 | | const struct gps_type_t driver_greis = { |
1172 | | // Full name of type |
1173 | | .type_name = "GREIS", |
1174 | | // Associated lexer packet type |
1175 | | .packet_type = GREIS_PACKET, |
1176 | | // Driver type flags |
1177 | | .flags = DRIVER_STICKY, |
1178 | | // Response string that identifies device (not active) |
1179 | | .trigger = NULL, |
1180 | | // Number of satellite channels supported by the device |
1181 | | .channels = 128, |
1182 | | // Startup-time device detector |
1183 | | .probe_detect = NULL, |
1184 | | // Packet getter (using default routine) |
1185 | | .get_packet = packet_get1, |
1186 | | // Parse message packets |
1187 | | .parse_packet = greis_parse_input, |
1188 | | // non-perturbing initial query (e.g. for version) |
1189 | | .init_query = NULL, |
1190 | | // fire on various lifetime events |
1191 | | .event_hook = greis_event_hook, |
1192 | | // Speed (baudrate) switch |
1193 | | .speed_switcher = greis_set_speed, |
1194 | | #if 0 // TODO |
1195 | | // Switch to NMEA mode |
1196 | | .mode_switcher = greis_set_mode, |
1197 | | #endif |
1198 | | // Message delivery rate switcher (not active) |
1199 | | .rate_switcher = NULL, |
1200 | | /* Minimum cycle time of the device. |
1201 | | * Default is 1/100, but this is tunable using /par/raw/msint . */ |
1202 | | .min_cycle.tv_sec = 0, |
1203 | | .min_cycle.tv_nsec = 10000000, |
1204 | | // Control string sender - should provide checksum and headers/trailer |
1205 | | .control_send = greis_control_send, |
1206 | | .time_offset = NULL, |
1207 | | // *INDENT-ON* |
1208 | | }; |
1209 | | #endif // defined(GREIS_ENABLE) && defined(BINARY_ENABLE) |
1210 | | // vim: set expandtab shiftwidth=4 |