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