PX4 Firmware
PX4 Autopilot Software http://px4.io
ashtech.cpp
Go to the documentation of this file.
1 /****************************************************************************
2  *
3  * Copyright (c) 2012-2018 PX4 Development Team. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in
13  * the documentation and/or other materials provided with the
14  * distribution.
15  * 3. Neither the name PX4 nor the names of its contributors may be
16  * used to endorse or promote products derived from this software
17  * without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
26  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  *
32  ****************************************************************************/
33 
34 #include <stdlib.h>
35 #include <stdio.h>
36 #include <math.h>
37 #include <string.h>
38 #include <ctime>
39 
40 #include "ashtech.h"
41 #include "rtcm.h"
42 
43 #ifndef M_PI_F
44 # define M_PI_F 3.14159265358979323846f
45 #endif
46 
47 #define MIN(X,Y) ((X) < (Y) ? (X) : (Y))
48 #define ASH_UNUSED(x) (void)x;
49 
50 //#define ASH_DEBUG(...) {GPS_WARN(__VA_ARGS__);}
51 #define ASH_DEBUG(...) {/*GPS_WARN(__VA_ARGS__);*/}
52 
53 GPSDriverAshtech::GPSDriverAshtech(GPSCallbackPtr callback, void *callback_user,
54  struct vehicle_gps_position_s *gps_position,
55  struct satellite_info_s *satellite_info, float heading_offset) :
56  GPSBaseStationSupport(callback, callback_user),
57  _satellite_info(satellite_info),
58  _gps_position(gps_position),
59  _heading_offset(heading_offset)
60 {
61  decodeInit();
62 }
63 
65 {
66  if (_rtcm_parsing) {
67  delete (_rtcm_parsing);
68  }
69 }
70 
71 /*
72  * All NMEA descriptions are taken from
73  * http://www.trimble.com/OEM_ReceiverHelp/V4.44/en/NMEA-0183messages_MessageOverview.html
74  */
75 
77 {
78  char *endp;
79 
80  if (len < 7) {
81  return 0;
82  }
83 
84  int uiCalcComma = 0;
85 
86  for (int i = 0 ; i < len; i++) {
87  if (_rx_buffer[i] == ',') { uiCalcComma++; }
88  }
89 
90  char *bufptr = (char *)(_rx_buffer + 6);
91  int ret = 0;
92 
93  if ((memcmp(_rx_buffer + 3, "ZDA,", 3) == 0) && (uiCalcComma == 6)) {
94  /*
95  UTC day, month, and year, and local time zone offset
96  An example of the ZDA message string is:
97 
98  $GPZDA,172809.456,12,07,1996,00,00*45
99 
100  ZDA message fields
101  Field Meaning
102  0 Message ID $GPZDA
103  1 UTC
104  2 Day, ranging between 01 and 31
105  3 Month, ranging between 01 and 12
106  4 Year
107  5 Local time zone offset from GMT, ranging from 00 through 13 hours
108  6 Local time zone offset from GMT, ranging from 00 through 59 minutes
109  7 The checksum data, always begins with *
110  Fields 5 and 6 together yield the total offset. For example, if field 5 is -5 and field 6 is +15, local time is 5 hours and 15 minutes earlier than GMT.
111  */
112  double ashtech_time = 0.0;
113  int day = 0, month = 0, year = 0, local_time_off_hour = 0, local_time_off_min = 0;
114  ASH_UNUSED(local_time_off_min);
115  ASH_UNUSED(local_time_off_hour);
116 
117  if (bufptr && *(++bufptr) != ',') { ashtech_time = strtod(bufptr, &endp); bufptr = endp; }
118 
119  if (bufptr && *(++bufptr) != ',') { day = strtol(bufptr, &endp, 10); bufptr = endp; }
120 
121  if (bufptr && *(++bufptr) != ',') { month = strtol(bufptr, &endp, 10); bufptr = endp; }
122 
123  if (bufptr && *(++bufptr) != ',') { year = strtol(bufptr, &endp, 10); bufptr = endp; }
124 
125  if (bufptr && *(++bufptr) != ',') { local_time_off_hour = strtol(bufptr, &endp, 10); bufptr = endp; }
126 
127  if (bufptr && *(++bufptr) != ',') { local_time_off_min = strtol(bufptr, &endp, 10); bufptr = endp; }
128 
129 
130  int ashtech_hour = static_cast<int>(ashtech_time / 10000);
131  int ashtech_minute = static_cast<int>((ashtech_time - ashtech_hour * 10000) / 100);
132  double ashtech_sec = static_cast<double>(ashtech_time - ashtech_hour * 10000 - ashtech_minute * 100);
133 
134  /*
135  * convert to unix timestamp
136  */
137  struct tm timeinfo = {};
138  timeinfo.tm_year = year - 1900;
139  timeinfo.tm_mon = month - 1;
140  timeinfo.tm_mday = day;
141  timeinfo.tm_hour = ashtech_hour;
142  timeinfo.tm_min = ashtech_minute;
143  timeinfo.tm_sec = int(ashtech_sec);
144  timeinfo.tm_isdst = 0;
145 
146 #ifndef NO_MKTIME
147  time_t epoch = mktime(&timeinfo);
148 
149  if (epoch > GPS_EPOCH_SECS) {
150  uint64_t usecs = static_cast<uint64_t>((ashtech_sec - static_cast<uint64_t>(ashtech_sec))) * 1000000;
151 
152  // FMUv2+ boards have a hardware RTC, but GPS helps us to configure it
153  // and control its drift. Since we rely on the HRT for our monotonic
154  // clock, updating it from time to time is safe.
155 
156  timespec ts{};
157  ts.tv_sec = epoch;
158  ts.tv_nsec = usecs * 1000;
159 
160  setClock(ts);
161 
162  _gps_position->time_utc_usec = static_cast<uint64_t>(epoch) * 1000000ULL;
163  _gps_position->time_utc_usec += usecs;
164 
165  } else {
167  }
168 
169 #else
171 #endif
172 
174  }
175 
176  else if ((memcmp(_rx_buffer + 3, "GGA,", 3) == 0) && (uiCalcComma == 14) && !_got_pashr_pos_message) {
177  /*
178  Time, position, and fix related data
179  An example of the GBS message string is:
180 
181  $GPGGA,172814.0,3723.46587704,N,12202.26957864,W,2,6,1.2,18.893,M,-25.669,M,2.0,0031*4F
182 
183  Note - The data string exceeds the ASHTECH standard length.
184  GGA message fields
185  Field Meaning
186  0 Message ID $GPGGA
187  1 UTC of position fix
188  2 Latitude
189  3 Direction of latitude:
190  N: North
191  S: South
192  4 Longitude
193  5 Direction of longitude:
194  E: East
195  W: West
196  6 GPS Quality indicator:
197  0: Fix not valid
198  1: GPS fix
199  2: Differential GPS fix, OmniSTAR VBS
200  4: Real-Time Kinematic, fixed integers
201  5: Real-Time Kinematic, float integers, OmniSTAR XP/HP or Location RTK
202  7 Number of SVs in use, range from 00 through to 24+
203  8 HDOP
204  9 Orthometric height (MSL reference)
205  10 M: unit of measure for orthometric height is meters
206  11 Geoid separation
207  12 M: geoid separation measured in meters
208  13 Age of differential GPS data record, Type 1 or Type 9. Null field when DGPS is not used.
209  14 Reference station ID, range 0000-4095. A null field when any reference station ID is selected and no corrections are received1.
210  15
211  The checksum data, always begins with *
212  Note - If a user-defined geoid model, or an inclined
213  */
214  double ashtech_time = 0.0, lat = 0.0, lon = 0.0, alt = 0.0;
215  int num_of_sv = 0, fix_quality = 0;
216  double hdop = 99.9;
217  char ns = '?', ew = '?';
218 
219  ASH_UNUSED(ashtech_time);
220  ASH_UNUSED(num_of_sv);
221  ASH_UNUSED(hdop);
222 
223  if (bufptr && *(++bufptr) != ',') { ashtech_time = strtod(bufptr, &endp); bufptr = endp; }
224 
225  if (bufptr && *(++bufptr) != ',') { lat = strtod(bufptr, &endp); bufptr = endp; }
226 
227  if (bufptr && *(++bufptr) != ',') { ns = *(bufptr++); }
228 
229  if (bufptr && *(++bufptr) != ',') { lon = strtod(bufptr, &endp); bufptr = endp; }
230 
231  if (bufptr && *(++bufptr) != ',') { ew = *(bufptr++); }
232 
233  if (bufptr && *(++bufptr) != ',') { fix_quality = strtol(bufptr, &endp, 10); bufptr = endp; }
234 
235  if (bufptr && *(++bufptr) != ',') { num_of_sv = strtol(bufptr, &endp, 10); bufptr = endp; }
236 
237  if (bufptr && *(++bufptr) != ',') { hdop = strtod(bufptr, &endp); bufptr = endp; }
238 
239  if (bufptr && *(++bufptr) != ',') { alt = strtod(bufptr, &endp); bufptr = endp; }
240 
241  if (ns == 'S') {
242  lat = -lat;
243  }
244 
245  if (ew == 'W') {
246  lon = -lon;
247  }
248 
249  /* convert from degrees, minutes and seconds to degrees * 1e7 */
250  _gps_position->lat = static_cast<int>((int(lat * 0.01) + (lat * 0.01 - int(lat * 0.01)) * 100.0 / 60.0) * 10000000);
251  _gps_position->lon = static_cast<int>((int(lon * 0.01) + (lon * 0.01 - int(lon * 0.01)) * 100.0 / 60.0) * 10000000);
252  _gps_position->alt = static_cast<int>(alt * 1000);
254 
255  if (fix_quality <= 0) {
256  _gps_position->fix_type = 0;
257 
258  } else {
259  /*
260  * in this NMEA message float integers (value 5) mode has higher value than fixed integers (value 4), whereas it provides lower quality,
261  * and since value 3 is not being used, I "moved" value 5 to 3 to add it to _gps_position->fix_type
262  */
263  if (fix_quality == 5) { fix_quality = 3; }
264 
265  /*
266  * fix quality 1 means just a normal 3D fix, so I'm subtracting 1 here. This way we'll have 3 for auto, 4 for DGPS, 5 for floats, 6 for fixed.
267  */
268  _gps_position->fix_type = 3 + fix_quality - 1;
269  }
270 
272 
273  _gps_position->vel_m_s = 0; /**< GPS ground speed (m/s) */
274  _gps_position->vel_n_m_s = 0; /**< GPS ground speed in m/s */
275  _gps_position->vel_e_m_s = 0; /**< GPS ground speed in m/s */
276  _gps_position->vel_d_m_s = 0; /**< GPS ground speed in m/s */
278  0; /**< Course over ground (NOT heading, but direction of movement) in rad, -PI..PI */
279  _gps_position->vel_ned_valid = true; /**< Flag to indicate if NED speed is valid */
281  ret = 1;
282 
283  } else if (memcmp(_rx_buffer, "$GPHDT,", 7) == 0 && uiCalcComma == 2) {
284  /*
285  Heading message
286  Example $GPHDT,121.2,T*35
287 
288  f1 Last computed heading value, in degrees (0-359.99)
289  T “T” for “True”
290  */
291 
292  float heading = 0.f;
293 
294  if (bufptr && *(++bufptr) != ',') {
295  heading = strtof(bufptr, &endp); bufptr = endp;
296 
297  ASH_DEBUG("heading update: %.3f", (double)heading);
298 
299  heading *= M_PI_F / 180.0f; // deg to rad, now in range [0, 2pi]
300  heading -= _heading_offset; // range: [-pi, 3pi]
301 
302  if (heading > M_PI_F) {
303  heading -= 2.f * M_PI_F; // final range is [-pi, pi]
304  }
305 
306  _gps_position->heading = heading;
307  }
308 
309  } else if ((memcmp(_rx_buffer, "$PASHR,POS,", 11) == 0) && (uiCalcComma == 18)) {
310  _got_pashr_pos_message = true;
311  /*
312  Example $PASHR,POS,2,10,125410.00,5525.8138702,N,03833.9587380,E,131.555,1.0,0.0,0.007,-0.001,2.0,1.0,1.7,1.0,*34
313 
314  $PASHR,POS,d1,d2,m3,m4,c5,m6,c7,f8,f9,f10,f11,f12,f13,f14,f15,f16,s17*cc
315  Parameter Description Range
316  d1 Position mode 0: standalone
317  1: differential
318  2: RTK float
319  3: RTK fixed
320  5: Dead reckoning
321  9: SBAS (see NPT setting)
322  d2 Number of satellite used in position fix 0-99
323  m3 Current UTC time of position fix (hhmmss.ss) 000000.00-235959.99
324  m4 Latitude of position (ddmm.mmmmmm) 0-90 degrees 00-59.9999999 minutes
325  c5 Latitude sector N, S
326  m6 Longitude of position (dddmm.mmmmmm) 0-180 degrees 00-59.9999999 minutes
327  c7 Longitude sector E,W
328  f8 Altitude above ellipsoid +9999.000
329  f9 Differential age (data link age), seconds 0.0-600.0
330  f10 True track/course over ground in degrees 0.0-359.9
331  f11 Speed over ground in knots 0.0-999.9
332  f12 Vertical velocity in decimeters per second +999.9
333  f13 PDOP 0-99.9
334  f14 HDOP 0-99.9
335  f15 VDOP 0-99.9
336  f16 TDOP 0-99.9
337  s17 Reserved no data
338  *cc Checksum
339  */
340  bufptr = (char *)(_rx_buffer + 10);
341 
342  /*
343  * Ashtech would return empty space as coordinate (lat, lon or alt) if it doesn't have a fix yet
344  */
345  int coordinatesFound = 0;
346  double ashtech_time = 0.0, lat = 0.0, lon = 0.0, alt = 0.0;
347  int num_of_sv = 0, fix_quality = 0;
348  double track_true = 0.0, ground_speed = 0.0, age_of_corr = 0.0;
349  double hdop = 99.9, vdop = 99.9, pdop = 99.9, tdop = 99.9, vertic_vel = 0.0;
350  char ns = '?', ew = '?';
351 
352  ASH_UNUSED(ashtech_time);
353  ASH_UNUSED(num_of_sv);
354  ASH_UNUSED(age_of_corr);
355  ASH_UNUSED(pdop);
356  ASH_UNUSED(tdop);
357 
358  if (bufptr && *(++bufptr) != ',') { fix_quality = strtol(bufptr, &endp, 10); bufptr = endp; }
359 
360  if (bufptr && *(++bufptr) != ',') { num_of_sv = strtol(bufptr, &endp, 10); bufptr = endp; }
361 
362  if (bufptr && *(++bufptr) != ',') { ashtech_time = strtod(bufptr, &endp); bufptr = endp; }
363 
364  if (bufptr && *(++bufptr) != ',') {
365  /*
366  * if a coordinate is skipped (i.e. no fix), it either won't get into this block (two commas in a row)
367  * or strtod won't find anything and endp will point exactly where bufptr is. The same is for lon and alt.
368  */
369  lat = strtod(bufptr, &endp);
370 
371  if (bufptr != endp) {coordinatesFound++;}
372 
373  bufptr = endp;
374  }
375 
376  if (bufptr && *(++bufptr) != ',') { ns = *(bufptr++); }
377 
378  if (bufptr && *(++bufptr) != ',') {
379  lon = strtod(bufptr, &endp);
380 
381  if (bufptr != endp) {coordinatesFound++;}
382 
383  bufptr = endp;
384  }
385 
386  if (bufptr && *(++bufptr) != ',') { ew = *(bufptr++); }
387 
388  if (bufptr && *(++bufptr) != ',') {
389  alt = strtod(bufptr, &endp);
390 
391  if (bufptr != endp) {coordinatesFound++;}
392 
393  bufptr = endp;
394  }
395 
396  if (bufptr && *(++bufptr) != ',') { age_of_corr = strtod(bufptr, &endp); bufptr = endp; }
397 
398  if (bufptr && *(++bufptr) != ',') { track_true = strtod(bufptr, &endp); bufptr = endp; }
399 
400  if (bufptr && *(++bufptr) != ',') { ground_speed = strtod(bufptr, &endp); bufptr = endp; }
401 
402  if (bufptr && *(++bufptr) != ',') { vertic_vel = strtod(bufptr, &endp); bufptr = endp; }
403 
404  if (bufptr && *(++bufptr) != ',') { pdop = strtod(bufptr, &endp); bufptr = endp; }
405 
406  if (bufptr && *(++bufptr) != ',') { hdop = strtod(bufptr, &endp); bufptr = endp; }
407 
408  if (bufptr && *(++bufptr) != ',') { vdop = strtod(bufptr, &endp); bufptr = endp; }
409 
410  if (bufptr && *(++bufptr) != ',') { tdop = strtod(bufptr, &endp); bufptr = endp; }
411 
412  if (ns == 'S') {
413  lat = -lat;
414  }
415 
416  if (ew == 'W') {
417  lon = -lon;
418  }
419 
420  _gps_position->lat = static_cast<int>((int(lat * 0.01) + (lat * 0.01 - int(lat * 0.01)) * 100.0 / 60.0) * 10000000);
421  _gps_position->lon = static_cast<int>((int(lon * 0.01) + (lon * 0.01 - int(lon * 0.01)) * 100.0 / 60.0) * 10000000);
422  _gps_position->alt = static_cast<int>(alt * 1000);
423  _gps_position->hdop = (float)hdop;
424  _gps_position->vdop = (float)vdop;
426 
427  if (coordinatesFound < 3) {
428  _gps_position->fix_type = 0;
429 
430  } else {
431  if (fix_quality == 9 || fix_quality == 10) { // SBAS differential or BeiDou differential
432  _gps_position->fix_type = 4; // use RTCM differential
433 
434  } else if (fix_quality == 12 || fix_quality == 22) { // RTK float or RTK float dithered
435  _gps_position->fix_type = 5;
436 
437  } else if (fix_quality == 13 || fix_quality == 23) { // RTK fixed or RTK fixed dithered
438  _gps_position->fix_type = 6;
439 
440  } else {
441  _gps_position->fix_type = 3 + fix_quality;
442  }
443 
444  // we got a valid position, activate correction output if needed
448  }
449  }
450 
452 
453  float track_rad = static_cast<float>(track_true) * M_PI_F / 180.0f;
454 
455  float velocity_ms = static_cast<float>(ground_speed) / 1.9438445f; /** knots to m/s */
456  float velocity_north = static_cast<float>(velocity_ms) * cosf(track_rad);
457  float velocity_east = static_cast<float>(velocity_ms) * sinf(track_rad);
458 
459  _gps_position->vel_m_s = velocity_ms; /** GPS ground speed (m/s) */
460  _gps_position->vel_n_m_s = velocity_north; /** GPS ground speed in m/s */
461  _gps_position->vel_e_m_s = velocity_east; /** GPS ground speed in m/s */
462  _gps_position->vel_d_m_s = static_cast<float>(-vertic_vel); /** GPS ground speed in m/s */
464  track_rad; /** Course over ground (NOT heading, but direction of movement) in rad, -PI..PI */
465  _gps_position->vel_ned_valid = true; /** Flag to indicate if NED speed is valid */
467  _rate_count_vel++;
468  ret = 1;
469 
470  } else if ((memcmp(_rx_buffer + 3, "GST,", 3) == 0) && (uiCalcComma == 8)) {
471  /*
472  Position error statistics
473  An example of the GST message string is:
474 
475  $GPGST,172814.0,0.006,0.023,0.020,273.6,0.023,0.020,0.031*6A
476 
477  The Talker ID ($--) will vary depending on the satellite system used for the position solution:
478 
479  $GP - GPS only
480  $GL - GLONASS only
481  $GN - Combined
482  GST message fields
483  Field Meaning
484  0 Message ID $GPGST
485  1 UTC of position fix
486  2 RMS value of the pseudorange residuals; includes carrier phase residuals during periods of RTK (float) and RTK (fixed) processing
487  3 Error ellipse semi-major axis 1 sigma error, in meters
488  4 Error ellipse semi-minor axis 1 sigma error, in meters
489  5 Error ellipse orientation, degrees from true north
490  6 Latitude 1 sigma error, in meters
491  7 Longitude 1 sigma error, in meters
492  8 Height 1 sigma error, in meters
493  9 The checksum data, always begins with *
494  */
495  double ashtech_time = 0.0, lat_err = 0.0, lon_err = 0.0, alt_err = 0.0;
496  double min_err = 0.0, maj_err = 0.0, deg_from_north = 0.0, rms_err = 0.0;
497 
498  ASH_UNUSED(ashtech_time);
499  ASH_UNUSED(min_err);
500  ASH_UNUSED(maj_err);
501  ASH_UNUSED(deg_from_north);
502  ASH_UNUSED(rms_err);
503 
504  if (bufptr && *(++bufptr) != ',') { ashtech_time = strtod(bufptr, &endp); bufptr = endp; }
505 
506  if (bufptr && *(++bufptr) != ',') { rms_err = strtod(bufptr, &endp); bufptr = endp; }
507 
508  if (bufptr && *(++bufptr) != ',') { maj_err = strtod(bufptr, &endp); bufptr = endp; }
509 
510  if (bufptr && *(++bufptr) != ',') { min_err = strtod(bufptr, &endp); bufptr = endp; }
511 
512  if (bufptr && *(++bufptr) != ',') { deg_from_north = strtod(bufptr, &endp); bufptr = endp; }
513 
514  if (bufptr && *(++bufptr) != ',') { lat_err = strtod(bufptr, &endp); bufptr = endp; }
515 
516  if (bufptr && *(++bufptr) != ',') { lon_err = strtod(bufptr, &endp); bufptr = endp; }
517 
518  if (bufptr && *(++bufptr) != ',') { alt_err = strtod(bufptr, &endp); bufptr = endp; }
519 
520  _gps_position->eph = sqrtf(static_cast<float>(lat_err) * static_cast<float>(lat_err)
521  + static_cast<float>(lon_err) * static_cast<float>(lon_err));
522  _gps_position->epv = static_cast<float>(alt_err);
523 
525 
526  } else if ((memcmp(_rx_buffer + 3, "GSV,", 3) == 0)) {
527  /*
528  The GSV message string identifies the number of SVs in view, the PRN numbers, elevations, azimuths, and SNR values. An example of the GSV message string is:
529 
530  $GPGSV,4,1,13,02,02,213,,03,-3,000,,11,00,121,,14,13,172,05*67
531 
532  GSV message fields
533  Field Meaning
534  0 Message ID $GPGSV
535  1 Total number of messages of this type in this cycle
536  2 Message number
537  3 Total number of SVs visible
538  4 SV PRN number
539  5 Elevation, in degrees, 90 maximum
540  6 Azimuth, degrees from True North, 000 through 359
541  7 SNR, 00 through 99 dB (null when not tracking)
542  8-11 Information about second SV, same format as fields 4 through 7
543  12-15 Information about third SV, same format as fields 4 through 7
544  16-19 Information about fourth SV, same format as fields 4 through 7
545  20 The checksum data, always begins with *
546  */
547  /*
548  * currently process only gps, because do not know what
549  * Global satellite ID I should use for non GPS sats
550  */
551  bool bGPS = false;
552 
553  if (memcmp(_rx_buffer, "$GP", 3) != 0) {
554  return 0;
555 
556  } else {
557  bGPS = true;
558  }
559 
560  int all_msg_num = 0, this_msg_num = 0, tot_sv_visible = 0;
561  struct gsv_sat {
562  int svid;
563  int elevation;
564  int azimuth;
565  int snr;
566  } sat[4];
567  memset(sat, 0, sizeof(sat));
568 
569  if (bufptr && *(++bufptr) != ',') { all_msg_num = strtol(bufptr, &endp, 10); bufptr = endp; }
570 
571  if (bufptr && *(++bufptr) != ',') { this_msg_num = strtol(bufptr, &endp, 10); bufptr = endp; }
572 
573  if (bufptr && *(++bufptr) != ',') { tot_sv_visible = strtol(bufptr, &endp, 10); bufptr = endp; }
574 
575  if ((this_msg_num < 1) || (this_msg_num > all_msg_num)) {
576  return 0;
577  }
578 
579  if (this_msg_num == 0 && bGPS && _satellite_info) {
580  memset(_satellite_info->svid, 0, sizeof(_satellite_info->svid));
581  memset(_satellite_info->used, 0, sizeof(_satellite_info->used));
582  memset(_satellite_info->snr, 0, sizeof(_satellite_info->snr));
583  memset(_satellite_info->elevation, 0, sizeof(_satellite_info->elevation));
584  memset(_satellite_info->azimuth, 0, sizeof(_satellite_info->azimuth));
585  }
586 
587  int end = 4;
588 
589  if (this_msg_num == all_msg_num) {
590  end = tot_sv_visible - (this_msg_num - 1) * 4;
591  _gps_position->satellites_used = tot_sv_visible;
592 
593  if (_satellite_info) {
594  _satellite_info->count = MIN(tot_sv_visible, satellite_info_s::SAT_INFO_MAX_SATELLITES);
596  ret = 2;
597  }
598  }
599 
600  if (_satellite_info) {
601  for (int y = 0 ; y < end ; y++) {
602  if (bufptr && *(++bufptr) != ',') { sat[y].svid = strtol(bufptr, &endp, 10); bufptr = endp; }
603 
604  if (bufptr && *(++bufptr) != ',') { sat[y].elevation = strtol(bufptr, &endp, 10); bufptr = endp; }
605 
606  if (bufptr && *(++bufptr) != ',') { sat[y].azimuth = strtol(bufptr, &endp, 10); bufptr = endp; }
607 
608  if (bufptr && *(++bufptr) != ',') { sat[y].snr = strtol(bufptr, &endp, 10); bufptr = endp; }
609 
610  _satellite_info->svid[y + (this_msg_num - 1) * 4] = sat[y].svid;
611  _satellite_info->used[y + (this_msg_num - 1) * 4] = (sat[y].snr > 0);
612  _satellite_info->snr[y + (this_msg_num - 1) * 4] = sat[y].snr;
613  _satellite_info->elevation[y + (this_msg_num - 1) * 4] = sat[y].elevation;
614  _satellite_info->azimuth[y + (this_msg_num - 1) * 4] = sat[y].azimuth;
615  }
616  }
617 
618  } else if (memcmp(_rx_buffer, "$PASHR,NAK", 10) == 0) {
619  ASH_DEBUG("Nack received");
620 
623  }
624 
625  } else if (memcmp(_rx_buffer, "$PASHR,ACK", 10) == 0) {
626  ASH_DEBUG("Ack received");
627 
630  }
631 
632  } else if (memcmp(_rx_buffer, "$PASHR,PRT,", 11) == 0 && uiCalcComma == 3) {
635  _port = _rx_buffer[11];
636  ASH_DEBUG("Connected port: %c", _port);
637  }
638 
639  } else if (memcmp(_rx_buffer, "$PASHR,RID,", 11) == 0) {
642 
643  if (memcmp(_rx_buffer + 11, "MB2", 3) == 0) {
645 
646  } else {
648  }
649 
650  ASH_DEBUG("Connected board: %i", (int)_board);
651  }
652 
653  } else if (memcmp(_rx_buffer, "$PASHR,RECEIPT,", 15) == 0) {
654  // this is the response to $PASHS,POS,AVG,100
655  // example: $PASHR,RECEIPT,POS,AVG,STARTED,INTERVAL,100,114502.56,28.12.2011
658  }
659 
660  // when finished we get one of the follwing messages:
661  // - successful: $PASHR,RECEIPT,POS,AVG,100,FINISHED,114642.81,28.12.2011,5542.5178481,N,03739.2954994,E,176.334,OK,CONTINUOUS,100.20*09
662  // - unsuccessful: $PASHR,RECEIPT,POS,AVG,100,FINISHED,124628.01,28.12.2011,ERR
663  char *finished_find = strstr((char *)_rx_buffer, "FINISHED,");
664 
665  if (finished_find) {
666  const bool error = strstr((const char *)_rx_buffer, "ERR");
667  _survey_in_start = 0;
668 
669  if (error) {
670  sendSurveyInStatusUpdate(false, false);
671 
672  } else {
673  // extract the position
674  double lat = 0., lon = 0.;
675  float alt = 0.f;
676  char ns = '?', ew = '?';
677  bufptr = finished_find + 9; // skip over FINISHED,
678  // skip the next 2 arguments
679  bufptr = strstr(bufptr, ",");
680 
681  if (bufptr) { bufptr = strstr(bufptr + 1, ","); }
682 
683  if (bufptr && *(++bufptr) != ',') { lat = strtod(bufptr, &endp); bufptr = endp; }
684 
685  if (bufptr && *(++bufptr) != ',') { ns = *(bufptr++); }
686 
687  if (bufptr && *(++bufptr) != ',') { lon = strtod(bufptr, &endp); bufptr = endp; }
688 
689  if (bufptr && *(++bufptr) != ',') { ew = *(bufptr++); }
690 
691  if (bufptr && *(++bufptr) != ',') { alt = strtod(bufptr, &endp); bufptr = endp; }
692 
693  if (ns == 'S') { lat = -lat; }
694 
695  if (ew == 'W') { lon = -lon; }
696 
697  lat = int(lat * 0.01) + (lat * 0.01 - int(lat * 0.01)) * 100.0 / 60.0;
698  lon = int(lon * 0.01) + (lon * 0.01 - int(lon * 0.01)) * 100.0 / 60.0;
699 
700  sendSurveyInStatusUpdate(false, true, lat, lon, alt);
701 
703  }
704  }
705 
706  }
707 
708  if (ret == 1) {
710  }
711 
712 
713  // handle survey-in status update
714  if (_survey_in_start != 0) {
715  const gps_abstime now = gps_absolute_time();
716  uint32_t survey_in_duration = (now - _survey_in_start) / 1000000;
717 
718  if (survey_in_duration != _base_settings.settings.survey_in.min_dur) {
719  _base_settings.settings.survey_in.min_dur = survey_in_duration;
720  sendSurveyInStatusUpdate(true, false);
721  }
722  }
723 
724  return ret;
725 }
726 
728 {
729  char buffer[40];
730  const char *rtcm_options[] = {
731  "$PASHS,NME,POS,%c,ON,0.2\r\n", // reduce position updates to 5 Hz
732 
733  "$PASHS,RT3,1074,%c,ON,1\r\n", // GPS observations
734  "$PASHS,RT3,1084,%c,ON,1\r\n", // GLONASS observations
735  "$PASHS,RT3,1094,%c,ON,1\r\n", // Galileo observations
736 
737  "$PASHS,RT3,1114,%c,ON,1\r\n", // QZSS observations
738  "$PASHS,RT3,1124,%c,ON,1\r\n", // BDS observations
739  "$PASHS,RT3,1006,%c,ON,1\r\n", // Static position
740  "$PASHS,RT3,1033,%c,ON,31\r\n", // Antenna and receiver name
741  "$PASHS,RT3,1013,%c,ON,1\r\n", // System parameters
742  "$PASHS,RT3,1029,%c,ON,1\r\n", // ASCII message
743  "$PASHS,RT3,1230,%c,ON\r\n", // GLONASS code phase bias
744 
745  // TODO: are these required (these are the ones from u-blox)?
746  "$PASHS,RT3,1005,%c,ON,1\r\n",
747  "$PASHS,RT3,1077,%c,ON,1\r\n",
748  "$PASHS,RT3,1087,%c,ON,1\r\n",
749  };
750 
751  for (unsigned int conf_i = 0; conf_i < sizeof(rtcm_options) / sizeof(rtcm_options[0]); conf_i++) {
752  int str_len = snprintf(buffer, sizeof(buffer), rtcm_options[conf_i], _port);
753 
754  if (writeAckedCommand(buffer, str_len, ASH_RESPONSE_TIMEOUT) != 0) {
755  ASH_DEBUG("command %s failed", buffer);
756  }
757  }
758 }
759 
760 void GPSDriverAshtech::receiveWait(unsigned timeout_min)
761 {
762  gps_abstime time_started = gps_absolute_time();
763 
764  while (gps_absolute_time() < time_started + timeout_min * 1000) {
765  receive(timeout_min);
766  }
767 }
768 
769 int GPSDriverAshtech::receive(unsigned timeout)
770 {
771  {
772 
773  uint8_t buf[GPS_READ_BUFFER_SIZE];
774 
775  /* timeout additional to poll */
776  uint64_t time_started = gps_absolute_time();
777 
778  int j = 0;
779  int bytes_count = 0;
780 
781  while (true) {
782 
783  /* pass received bytes to the packet decoder */
784  while (j < bytes_count) {
785  int l = 0;
786 
787  if ((l = parseChar(buf[j])) > 0) {
788  /* return to configure during configuration or to the gps driver during normal work
789  * if a packet has arrived */
790  int ret = handleMessage(l);
791 
792  if (ret > 0) {
793  return ret;
794  }
795  }
796 
797  j++;
798  }
799 
800  /* everything is read */
801  j = bytes_count = 0;
802 
803  /* then poll or read for new data */
804  int ret = read(buf, sizeof(buf), timeout * 2);
805 
806  if (ret < 0) {
807  /* something went wrong when polling */
808  return -1;
809 
810  } else if (ret == 0) {
811  /* Timeout while polling or just nothing read if reading, let's
812  * stay here, and use timeout below. */
813 
814  } else if (ret > 0) {
815  /* if we have new data from GPS, go handle it */
816  bytes_count = ret;
817  }
818 
819  /* in case we get crap from GPS or time out */
820  if (time_started + timeout * 1000 * 2 < gps_absolute_time()) {
821  return -1;
822  }
823  }
824  }
825 
826 }
827 #define HEXDIGIT_CHAR(d) ((char)((d) + (((d) < 0xA) ? '0' : 'A'-0xA)))
828 
830 {
831  int iRet = 0;
832 
833  switch (_decode_state) {
834  /* First, look for sync1 */
836  if (b == '$') {
838  _rx_buffer_bytes = 0;
840 
841  } else if (b == RTCM3_PREAMBLE && _rtcm_parsing) {
844 
845  }
846 
847  break;
848 
850  if (b == '$') {
852  _rx_buffer_bytes = 0;
853 
854  } else if (b == '*') {
856  }
857 
858  if (_rx_buffer_bytes >= (sizeof(_rx_buffer) - 5)) {
859  ASH_DEBUG("buffer overflow");
861  _rx_buffer_bytes = 0;
862 
863  } else {
865  }
866 
867  break;
868 
872  break;
873 
876  uint8_t checksum = 0;
877  uint8_t *buffer = _rx_buffer + 1;
878  uint8_t *bufend = _rx_buffer + _rx_buffer_bytes - 3;
879 
880  for (; buffer < bufend; buffer++) { checksum ^= *buffer; }
881 
882  if ((HEXDIGIT_CHAR(checksum >> 4) == *(_rx_buffer + _rx_buffer_bytes - 2)) &&
883  (HEXDIGIT_CHAR(checksum & 0x0F) == *(_rx_buffer + _rx_buffer_bytes - 1))) {
884  iRet = _rx_buffer_bytes;
885  }
886 
887  decodeInit();
888  }
889  break;
890 
892  if (_rtcm_parsing->addByte(b)) {
893  ASH_DEBUG("got RTCM message with length %i", (int)_rtcm_parsing->messageLength());
895  decodeInit();
896  }
897 
898  break;
899  }
900 
901  return iRet;
902 }
903 
905 {
906  _rx_buffer_bytes = 0;
908 
910  if (!_rtcm_parsing) {
911  _rtcm_parsing = new RTCMParsing();
912  }
913 
914  _rtcm_parsing->reset();
915  }
916 }
917 
918 int GPSDriverAshtech::writeAckedCommand(const void *buf, int buf_length, unsigned timeout)
919 {
920  if (write(buf, buf_length) != buf_length) {
921  return -1;
922  }
923 
924  return waitForReply(NMEACommand::Acked, timeout);
925 }
926 
928 {
929  gps_abstime time_started = gps_absolute_time();
930 
931  ASH_DEBUG("waiting for reply for command %i", (int)command);
932 
935 
936  while (_command_state == NMEACommandState::waiting && gps_absolute_time() < time_started + timeout * 1000) {
937  receive(timeout);
938  }
939 
940  return _command_state == NMEACommandState::received ? 0 : -1;
941 }
942 
944 {
945  _output_mode = output_mode;
947  _configure_done = false;
948 
949  /* Try different baudrates (115200 is the default for Trimble) and request the baudrate that we want.
950  *
951  * These are Ashtech proprietary commands, we can use them for auto-detection:
952  * $PASHS for setting
953  * $PASHQ for querying
954  * $PASHR for a response
955  */
956  const unsigned baudrates_to_try[] = {9600, 38400, 19200, 57600, 115200};
957  bool success = false;
958 
959  unsigned test_baudrate;
960 
961  for (unsigned int baud_i = 0; !success && baud_i < sizeof(baudrates_to_try) / sizeof(baudrates_to_try[0]); baud_i++) {
962  test_baudrate = baudrates_to_try[baud_i];
963 
964  if (baudrate > 0 && baudrate != test_baudrate) {
965  continue; // skip to next baudrate
966  }
967 
968  setBaudrate(test_baudrate);
969 
970  ASH_DEBUG("baudrate set to %i", test_baudrate);
971 
972  const char port_config[] = "$PASHQ,PRT\r\n"; // ask for the current port configuration
973 
974  for (int run = 0; run < 2; ++run) { // try several times
975  write(port_config, sizeof(port_config) - 1);
976 
978  ASH_DEBUG("got port for baudrate %i", test_baudrate);
979  success = true;
980  break;
981  }
982  }
983  }
984 
985  if (!success) {
986  return -1;
987  }
988 
989  // We successfully got a response and know to which port we are connected. Now set the desired baudrate
990  // if it's different from the current one.
991  const unsigned desired_baudrate = 115200; // changing this requires also changing the SPD command
992 
993  baudrate = test_baudrate;
994 
995  if (baudrate != desired_baudrate) {
996  baudrate = desired_baudrate;
997  const char baud_config[] = "$PASHS,SPD,%c,9\r\n"; // configure baudrate to 115200
998  char baud_config_str[sizeof(baud_config)];
999  int len = snprintf(baud_config_str, sizeof(baud_config_str), baud_config, _port);
1000  write(baud_config_str, len);
1001  decodeInit();
1002  receiveWait(200);
1003  decodeInit();
1004  setBaudrate(baudrate);
1005 
1006  success = false;
1007 
1008  for (int run = 0; run < 10; ++run) {
1009  // We ask for the port config again. If we get a reply, we know that the changed settings work.
1010  const char port_config[] = "$PASHQ,PRT\r\n";
1011  write(port_config, sizeof(port_config) - 1);
1012 
1014  success = true;
1015  break;
1016  }
1017  }
1018 
1019  if (!success) {
1020  return -1;
1021  }
1022 
1023  ASH_DEBUG("Successfully configured the baudrate");
1024  }
1025 
1026 
1027 // Additional commands that might be useful:
1028 // Reading firmware version:
1029 // $PASHQ,VER
1030 // Reading installed firmware options:
1031 // $PASHQ,OPTION
1032 // The output for the Trimble MB-two is:
1033 // $PASHR,OPTION,0,SERIAL NUMBER,5730C00370*3E
1034 // $PASHR,OPTION,@1,GEOFENCING_WW,034017C7114ED*36
1035 // $PASHR,OPTION,N,GPS,0340173F8924D*66
1036 // $PASHR,OPTION,G,GLONASS,0340178A9E138*69
1037 // $PASHR,OPTION,B,BEIDOU,03401434EC35A*4D
1038 // $PASHR,OPTION,X,L1TRACKING,0340119C547B8*40
1039 // $PASHR,OPTION,Y,L2TRACKING,034012CD03607*42
1040 // $PASHR,OPTION,W,20HZ,034016B5A5225*2A
1041 // $PASHR,OPTION,J,RTKROVER,034010C800693*41
1042 // $PASHR,OPTION,K,RTKBASE,03401065AB099*7E
1043 // $PASHR,OPTION,D,DUO,0340138851415*70
1044 // $PASHR,OPTION,S,L3TRACKING,034011C7AB73D*48
1045 // Reset the full configuration (however it will lead to a reboot and requires about 15s waiting time)
1046 // $PASHS,RST
1047 
1048  // get the board identification
1049  const char board_identification[] = "$PASHQ,RID\r\n";
1050 
1051  if (write(board_identification, sizeof(board_identification) - 1) == sizeof(board_identification) - 1) {
1053  ASH_DEBUG("command %s failed", board_identification);
1054  return -1;
1055  }
1056  }
1057 
1058  // Now configure the messages we want
1059 
1060  const char update_rate[] = "$PASHS,POP,20\r\n"; // set internal update rate to 20 Hz
1061 
1062  if (writeAckedCommand(update_rate, sizeof(update_rate) - 1, ASH_RESPONSE_TIMEOUT) != 0) {
1063  ASH_DEBUG("command %s failed", update_rate);
1064  // for some reason we don't get a response here
1065  }
1066 
1067  // Enable dual antenna mode (2: both antennas are L1/L2 GNSS capable, flex mode, avoids the need to determine
1068  // the baseline length through a prior calibration stage)
1069  // Needs to be set before other commands
1070  const bool use_dual_mode = output_mode == OutputMode::GPS && _board == AshtechBoard::trimble_mb_two;
1071 
1072  if (use_dual_mode) {
1073  ASH_DEBUG("Enabling DUO mode");
1074  const char duo_mode[] = "$PASHS,SNS,DUO,2\r\n";
1075 
1076  if (writeAckedCommand(duo_mode, sizeof(duo_mode) - 1, ASH_RESPONSE_TIMEOUT) != 0) {
1077  ASH_DEBUG("command %s failed", duo_mode);
1078  }
1079 
1080  } else {
1081  const char solo_mode[] = "$PASHS,SNS,SOL\r\n";
1082 
1083  if (writeAckedCommand(solo_mode, sizeof(solo_mode) - 1, ASH_RESPONSE_TIMEOUT) != 0) {
1084  ASH_DEBUG("command %s failed", solo_mode);
1085  }
1086  }
1087 
1088  char buffer[40];
1089  const char *config_options[] = {
1090  "$PASHS,NME,ALL,%c,OFF\r\n", // disable all NMEA and NMEA-Like Messages
1091  "$PASHS,ATM,ALL,%c,OFF\r\n", // disable all ATM (ATOM) Messages
1092  "$PASHS,OUT,%c,ON\r\n", // enable periodic output
1093  "$PASHS,NME,ZDA,%c,ON,3\r\n", // enable ZDA (date & time) output every 3s
1094  "$PASHS,NME,GST,%c,ON,3\r\n", // position accuracy messages
1095  "$PASHS,NME,POS,%c,ON,0.05\r\n",// position & velocity (we can go up to 20Hz if FW option [W] is given and to 50Hz if [8] is given)
1096  "$PASHS,NME,GSV,%c,ON,1\r\n" // satellite status
1097  };
1098 
1099  for (unsigned int conf_i = 0; conf_i < sizeof(config_options) / sizeof(config_options[0]); conf_i++) {
1100  int len = snprintf(buffer, sizeof(buffer), config_options[conf_i], _port);
1101 
1102  if (writeAckedCommand(buffer, len, ASH_RESPONSE_TIMEOUT) != 0) {
1103  ASH_DEBUG("command %s failed", buffer);
1104  // some commands are not acked (e.g. GSV), so don't make this fatal
1105  }
1106  }
1107 
1108  if (use_dual_mode) {
1109  // enable heading output
1110  const char heading_output[] = "$PASHS,NME,HDT,%c,ON,0.05\r\n";
1111  int len = snprintf(buffer, sizeof(buffer), heading_output, _port);
1112 
1113  if (writeAckedCommand(buffer, len, ASH_RESPONSE_TIMEOUT) != 0) {
1114  ASH_DEBUG("command %s failed", buffer);
1115  }
1116  }
1117 
1118 
1119  if (output_mode == OutputMode::RTCM && _board == AshtechBoard::trimble_mb_two) {
1121  status.latitude = status.longitude = (double)NAN;
1122  status.altitude = NAN;
1123  status.duration = 0;
1124  status.mean_accuracy = 0;
1125  const bool valid = false;
1126  const bool active = true;
1127  status.flags = (int)valid | ((int)active << 1);
1129  }
1130 
1131  _configure_done = true;
1132  return 0;
1133 }
1134 
1136 {
1138  return;
1139  }
1140 
1142  char buffer[100];
1143 
1145  ASH_DEBUG("enabling survey-in");
1146 
1147  // setup the base reference: average the position over N seconds
1148  const char avg_pos[] = "$PASHS,POS,AVG,%i\r\n";
1149  // alternatively use the current position as reference: "$PASHS,POS,CUR\r\n"
1150  int len = snprintf(buffer, sizeof(buffer), avg_pos, (int)_base_settings.settings.survey_in.min_dur);
1151 
1152  write(buffer, len);
1153 
1155  ASH_DEBUG("command %s failed", buffer);
1156  }
1157 
1158 
1159  const char *config_options[] = {
1160  "$PASHS,ANP,OWN,TRM55971.00\r\n", // set antenna name (arbitrary)
1161  "$PASHS,STI,0001\r\n" // enter a base ID
1162  };
1163 
1164 
1165  for (unsigned int conf_i = 0; conf_i < sizeof(config_options) / sizeof(config_options[0]); conf_i++) {
1166  if (writeAckedCommand(config_options[conf_i], strlen(config_options[conf_i]), ASH_RESPONSE_TIMEOUT) != 0) {
1167  ASH_DEBUG("command %s failed", config_options[conf_i]);
1168  }
1169  }
1170 
1171  _base_settings.settings.survey_in.min_dur = 0; // use it as counter how long survey-in has been active
1173  sendSurveyInStatusUpdate(true, false);
1174 
1175  } else {
1176  ASH_DEBUG("setting base station position");
1177 
1179  char ns, ew;
1180  double latitude = settings.latitude;
1181 
1182  if (latitude < 0.) {
1183  latitude = -latitude;
1184  ns = 'S';
1185 
1186  } else {
1187  ns = 'N';
1188  }
1189 
1190  // convert to ddmm.mmmmmm format
1191  latitude = ((int)latitude) * 100. + (latitude - ((int)latitude)) * 60.;
1192 
1193  double longitude = settings.longitude;
1194 
1195  if (longitude < 0.) {
1196  longitude = -longitude;
1197  ew = 'W';
1198 
1199  } else {
1200  ew = 'E';
1201  }
1202 
1203  // convert to ddmm.mmmmmm format
1204  longitude = ((int)longitude) * 100. + (longitude - ((int)longitude)) * 60.;
1205 
1206  int len = snprintf(buffer, sizeof(buffer), "$PASHS,POS,%.8f,%c,%.8f,%c,%.5f,PC1",
1207  latitude, ns, longitude, ew, (double)settings.altitude);
1208 
1209  if (len >= 0 && len < (int)sizeof(buffer)) {
1210  if (writeAckedCommand(buffer, len, ASH_RESPONSE_TIMEOUT) != 0) {
1211  ASH_DEBUG("command %s failed", buffer);
1212  }
1213 
1214  } else {
1215  ASH_DEBUG("snprintf failed (buffer too short)");
1216  }
1217 
1219  sendSurveyInStatusUpdate(false, true, settings.latitude, settings.longitude, settings.altitude);
1220  }
1221 }
1222 
1223 void
1224 GPSDriverAshtech::sendSurveyInStatusUpdate(bool active, bool valid, double latitude, double longitude, float altitude)
1225 {
1227  status.latitude = latitude;
1228  status.longitude = longitude;
1229  status.altitude = altitude;
1231  status.mean_accuracy = 0; // unknown
1232  status.flags = (int)valid | ((int)active << 1);
1234 }
void surveyInStatus(SurveyInStatus &status)
Definition: gps_helper.h:233
union GPSBaseStationSupport::BaseSettings::@4 settings
static struct vehicle_status_s status
Definition: Commander.cpp:138
RTCMParsing * _rtcm_parsing
Definition: ashtech.h:133
int handleMessage(int len)
Definition: ashtech.cpp:76
int writeAckedCommand(const void *buf, int buf_length, unsigned timeout)
Write a command and wait for a (N)Ack.
Definition: ashtech.cpp:918
int receive(unsigned timeout) override
receive & handle new data from the device
Definition: ashtech.cpp:769
uint8_t used[20]
int setBaudrate(int baudrate)
set the Baudrate
Definition: gps_helper.h:228
uint8_t azimuth[20]
NMEACommandState _command_state
Definition: ashtech.h:129
#define ASH_UNUSED(x)
Definition: ashtech.cpp:48
#define MIN(X, Y)
Definition: ashtech.cpp:47
void reset()
reset the parsing state
Definition: rtcm.cpp:49
bool _configure_done
Definition: ashtech.h:139
#define RTCM3_PREAMBLE
Definition: rtcm.h:39
int read(uint8_t *buf, int buf_length, int timeout)
read from device
Definition: gps_helper.h:206
struct satellite_info_s * _satellite_info
Definition: ashtech.h:119
uint8_t snr[20]
#define ASH_RESPONSE_TIMEOUT
Definition: ashtech.h:49
hrt_abstime gps_abstime
Definition: definitions.h:58
bool _correction_output_activated
Definition: ashtech.h:138
FixedPositionSettings fixed_position
Definition: base_station.h:107
struct vehicle_gps_position_s * _gps_position
Definition: ashtech.h:120
int write(const void *buf, int buf_length)
write to the device
Definition: gps_helper.h:218
void activateCorrectionOutput()
enable output of correction output
Definition: ashtech.cpp:1135
uint8_t _rx_buffer[ASHTECH_RECV_BUFFER_SIZE]
Definition: ashtech.h:124
bool _got_pashr_pos_message
If we got a PASHR,POS message, we will ignore GGA messages.
Definition: ashtech.h:126
void gotRTCMMessage(uint8_t *buf, int buf_length)
got an RTCM message from the device
Definition: gps_helper.h:239
NMEACommand _waiting_for_command
Definition: ashtech.h:128
uint8_t _rate_count_vel
Definition: gps_helper.h:265
Vector< float, 6 > f(float t, const Matrix< float, 6, 1 > &, const Matrix< float, 3, 1 > &)
Definition: integration.cpp:8
int configure(unsigned &baudrate, OutputMode output_mode) override
configure the device
Definition: ashtech.cpp:943
int(* GPSCallbackPtr)(GPSCallbackType type, void *data1, int data2, void *user)
Callback function for platform-specific stuff.
Definition: gps_helper.h:132
#define GPS_EPOCH_SECS
Definition: gps_helper.h:145
void receiveWait(unsigned timeout_min)
receive data for at least the specified amount of time
Definition: ashtech.cpp:760
#define ASH_DEBUG(...)
Definition: ashtech.cpp:51
GPSDriverAshtech(GPSCallbackPtr callback, void *callback_user, struct vehicle_gps_position_s *gps_position, struct satellite_info_s *satellite_info, float heading_offset=0.f)
Definition: ashtech.cpp:53
#define GPS_READ_BUFFER_SIZE
buffer size for the read() call. Messages can be longer than that.
Definition: gps_helper.h:46
float _heading_offset
Definition: ashtech.h:141
uint64_t _last_timestamp_time
Definition: ashtech.h:121
request RTCM output. This is used for (fixed position) base stations
void decodeInit(void)
Definition: ashtech.cpp:904
int parseChar(uint8_t b)
Definition: ashtech.cpp:829
void sendSurveyInStatusUpdate(bool active, bool valid, double latitude=(double) NAN, double longitude=(double) NAN, float altitude=NAN)
Definition: ashtech.cpp:1224
uint16_t messageLength() const
Definition: rtcm.h:62
uint8_t * message() const
Definition: rtcm.h:61
GPS driver base class with Base Station Support.
Definition: base_station.h:49
virtual ~GPSDriverAshtech()
Definition: ashtech.cpp:64
uint8_t _rate_count_lat_lon
Definition: gps_helper.h:264
gps_abstime _survey_in_start
Definition: ashtech.h:135
BaseSettings _base_settings
Definition: base_station.h:110
uint16_t _rx_buffer_bytes
Definition: ashtech.h:125
int waitForReply(NMEACommand command, const unsigned timeout)
Definition: ashtech.cpp:927
#define HEXDIGIT_CHAR(d)
Definition: ashtech.cpp:827
OutputMode _output_mode
Definition: ashtech.h:137
#define M_PI_F
Definition: ashtech.cpp:44
#define gps_absolute_time
Get the current time in us.
Definition: definitions.h:57
normal GPS output
AshtechBoard _board
board we are connected to
Definition: ashtech.h:131
uint8_t elevation[20]
uint8_t svid[20]
NMEADecodeState _decode_state
Definition: ashtech.h:123
void setClock(timespec &t)
Definition: gps_helper.h:244
bool addByte(uint8_t b)
add a byte to the message
Definition: rtcm.cpp:60
char _port
port we are connected to (e.g.
Definition: ashtech.h:130
void activateRTCMOutput()
Definition: ashtech.cpp:727