dRonin  adbada4
dRonin firmware
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
UBX.c
Go to the documentation of this file.
1 
16 /*
17  * This program is free software; you can redistribute it and/or modify
18  * it under the terms of the GNU General Public License as published by
19  * the Free Software Foundation; either version 3 of the License, or
20  * (at your option) any later version.
21  *
22  * This program is distributed in the hope that it will be useful, but
23  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
24  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
25  * for more details.
26  *
27  * You should have received a copy of the GNU General Public License along
28  * with this program; if not, see <http://www.gnu.org/licenses/>
29  */
30 
31 #include "openpilot.h"
32 #include "pios.h"
33 
34 #if defined(PIOS_INCLUDE_GPS_UBX_PARSER)
35 
36 #include "UBX.h"
37 #include "GPS.h"
38 
39 static uint32_t parse_errors;
40 
41 static bool checksum_ubx_message(const struct UBXPacket *);
42 static uint32_t parse_ubx_message(const struct UBXPacket *, GPSPositionData *);
43 
44 // parse incoming character stream for messages in UBX binary format
45 
46 int parse_ubx_stream (uint8_t c, char *gps_rx_buffer, GPSPositionData *GpsData, struct GPS_RX_STATS *gpsRxStats)
47 {
48  enum proto_states {
49  START,
50  UBX_SY2,
51  UBX_CLASS,
52  UBX_ID,
53  UBX_LEN1,
54  UBX_LEN2,
55  UBX_PAYLOAD,
56  UBX_CHK1,
57  UBX_CHK2,
58  FINISHED
59  };
60 
61  static enum proto_states proto_state = START;
62  static uint16_t rx_count = 0;
63  struct UBXPacket *ubx = (struct UBXPacket *)gps_rx_buffer;
64 
65  switch (proto_state) {
66  case START: // detect protocol
67  if (c == UBX_SYNC1) // first UBX sync char found
68  proto_state = UBX_SY2;
69  break;
70  case UBX_SY2:
71  if (c == UBX_SYNC2) // second UBX sync char found
72  proto_state = UBX_CLASS;
73  else
74  proto_state = START; // reset state
75  break;
76  case UBX_CLASS:
77  ubx->header.class = c;
78  proto_state = UBX_ID;
79  break;
80  case UBX_ID:
81  ubx->header.id = c;
82  proto_state = UBX_LEN1;
83  break;
84  case UBX_LEN1:
85  ubx->header.len = c;
86  proto_state = UBX_LEN2;
87  break;
88  case UBX_LEN2:
89  ubx->header.len += (c << 8);
90  if (ubx->header.len > sizeof(UBXPayload)) {
91  gpsRxStats->gpsRxOverflow++;
92  proto_state = START;
93  } else {
94  rx_count = 0;
95  proto_state = UBX_PAYLOAD;
96  }
97  break;
98  case UBX_PAYLOAD:
99  if (rx_count < ubx->header.len) {
100  ubx->payload.payload[rx_count] = c;
101  if (++rx_count == ubx->header.len)
102  proto_state = UBX_CHK1;
103  } else {
104  gpsRxStats->gpsRxOverflow++;
105  proto_state = START;
106  }
107  break;
108  case UBX_CHK1:
109  ubx->header.ck_a = c;
110  proto_state = UBX_CHK2;
111  break;
112  case UBX_CHK2:
113  ubx->header.ck_b = c;
114  if (checksum_ubx_message(ubx)) { // message complete and valid
115  parse_ubx_message(ubx, GpsData);
116  proto_state = FINISHED;
117  } else {
118  gpsRxStats->gpsRxChkSumError++;
119  proto_state = START;
120  }
121  break;
122  default: break;
123  }
124 
125  if (proto_state == START)
126  return PARSER_ERROR; // parser couldn't use this byte
127  else if (proto_state == FINISHED) {
128  gpsRxStats->gpsRxReceived++;
129  proto_state = START;
130  return PARSER_COMPLETE; // message complete & processed
131  }
132 
133  return PARSER_INCOMPLETE; // message not (yet) complete
134 }
135 
136 
137 // Keep track of various GPS messages needed to make up a single UAVO update
138 // time-of-week timestamp is used to correlate matching messages
139 
140 #define POSLLH_RECEIVED (1 << 0)
141 #define STATUS_RECEIVED (1 << 1)
142 #define DOP_RECEIVED (1 << 2)
143 #define VELNED_RECEIVED (1 << 3)
144 #define SOL_RECEIVED (1 << 4)
145 #define ALL_RECEIVED (SOL_RECEIVED | VELNED_RECEIVED | DOP_RECEIVED | POSLLH_RECEIVED)
146 #define NONE_RECEIVED 0
147 
148 static struct msgtracker{
149  uint32_t currentTOW; // TOW of the message set currently in progress
150  uint8_t msg_received; // keep track of received message types
151  } msgtracker;
152 
153 // Check if a message belongs to the current data set and register it as 'received'
154 bool check_msgtracker (uint32_t tow, uint8_t msg_flag)
155 {
156 
157  if (tow > msgtracker.currentTOW ? true // start of a new message set
158  : (msgtracker.currentTOW - tow > 6*24*3600*1000)) { // 6 days, TOW wrap around occured
159  msgtracker.currentTOW = tow;
160  msgtracker.msg_received = NONE_RECEIVED;
161  } else if (tow < msgtracker.currentTOW) // message outdated (don't process)
162  return false;
163 
164  msgtracker.msg_received |= msg_flag; // register reception of this msg type
165  return true;
166 }
167 
168 static bool checksum_ubx_message (const struct UBXPacket *ubx)
169 {
170  int i;
171  uint8_t ck_a, ck_b;
172 
173  ck_a = ubx->header.class;
174  ck_b = ck_a;
175 
176  ck_a += ubx->header.id;
177  ck_b += ck_a;
178 
179  ck_a += ubx->header.len & 0xff;
180  ck_b += ck_a;
181 
182  ck_a += ubx->header.len >> 8;
183  ck_b += ck_a;
184 
185  for (i = 0; i < ubx->header.len; i++) {
186  ck_a += ubx->payload.payload[i];
187  ck_b += ck_a;
188  }
189 
190  if (ubx->header.ck_a == ck_a &&
191  ubx->header.ck_b == ck_b)
192  return true;
193  else {
194  parse_errors++;
195  UBloxInfoParseErrorsSet(&parse_errors);
196 
197  return false;
198  }
199 
200 }
201 
202 static void parse_ubx_nav_posllh (const struct UBX_NAV_POSLLH *posllh, GPSPositionData *GpsPosition)
203 {
204  if (check_msgtracker(posllh->iTOW, POSLLH_RECEIVED)) {
205  if (GpsPosition->Status != GPSPOSITION_STATUS_NOFIX) {
206  GpsPosition->Altitude = (float)posllh->hMSL*0.001f;
207  GpsPosition->GeoidSeparation = (float)(posllh->height - posllh->hMSL)*0.001f;
208  GpsPosition->Latitude = posllh->lat;
209  GpsPosition->Longitude = posllh->lon;
210  }
211  }
212 }
213 
214 static void parse_ubx_nav_sol (const struct UBX_NAV_SOL *sol, GPSPositionData *GpsPosition)
215 {
216  if (check_msgtracker(sol->iTOW, SOL_RECEIVED)) {
217  GpsPosition->Satellites = sol->numSV;
218  GpsPosition->Accuracy = sol->pAcc / 100.0f;
219 
220  if (sol->flags & STATUS_FLAGS_GPSFIX_OK) {
221  switch (sol->gpsFix) {
222  case STATUS_GPSFIX_2DFIX:
223  GpsPosition->Status = GPSPOSITION_STATUS_FIX2D;
224  break;
225  case STATUS_GPSFIX_3DFIX:
226  GpsPosition->Status = (sol->flags & STATUS_FLAGS_DIFFSOLN) ?
227  GPSPOSITION_STATUS_DIFF3D : GPSPOSITION_STATUS_FIX3D;
228  break;
229  default: GpsPosition->Status = GPSPOSITION_STATUS_NOFIX;
230  }
231  }
232  else // fix is not valid so we make sure to treat is as NOFIX
233  GpsPosition->Status = GPSPOSITION_STATUS_NOFIX;
234  }
235 }
236 
237 static void parse_ubx_nav_dop (const struct UBX_NAV_DOP *dop, GPSPositionData *GpsPosition)
238 {
239  if (check_msgtracker(dop->iTOW, DOP_RECEIVED)) {
240  GpsPosition->HDOP = (float)dop->hDOP * 0.01f;
241  GpsPosition->VDOP = (float)dop->vDOP * 0.01f;
242  GpsPosition->PDOP = (float)dop->pDOP * 0.01f;
243  }
244 }
245 
246 static void parse_ubx_nav_velned (const struct UBX_NAV_VELNED *velned, GPSPositionData *GpsPosition)
247 {
248  GPSVelocityData GpsVelocity;
249 
250  if (check_msgtracker(velned->iTOW, VELNED_RECEIVED)) {
251  if (GpsPosition->Status != GPSPOSITION_STATUS_NOFIX) {
252  GpsVelocity.North = (float)velned->velN/100.0f;
253  GpsVelocity.East = (float)velned->velE/100.0f;
254  GpsVelocity.Down = (float)velned->velD/100.0f;
255  GpsVelocity.Accuracy = (float)velned->sAcc/100.0f;
256  GPSVelocitySet(&GpsVelocity);
257  GpsPosition->Groundspeed = (float)velned->gSpeed * 0.01f;
258  GpsPosition->Heading = (float)velned->heading * 1.0e-5f;
259  }
260  }
261 }
262 
263 static void parse_ubx_nav_timeutc (const struct UBX_NAV_TIMEUTC *timeutc)
264 {
265  if (!(timeutc->valid & TIMEUTC_VALIDWKN))
266  return;
267 
268  GPSTimeData GpsTime;
269 
270  GpsTime.Year = timeutc->year;
271  GpsTime.Month = timeutc->month;
272  GpsTime.Day = timeutc->day;
273  GpsTime.Hour = timeutc->hour;
274  GpsTime.Minute = timeutc->min;
275  GpsTime.Second = timeutc->sec;
276 
277  GPSTimeSet(&GpsTime);
278 }
279 
280 static void parse_ubx_nav_svinfo (const struct UBX_NAV_SVINFO *svinfo)
281 {
282  uint8_t chan = 0;
283  GPSSatellitesData svdata;
284 
285  bool skipped=false;
286 
287  svdata.SatsInView = svinfo->numCh;
288 
289  // Invalid.. too many channels to fit in message.
290  if (svinfo->numCh > MAX_SVS) return;
291 
292  for (int i = 0;
293  (i < svinfo->numCh) && (chan < GPSSATELLITES_PRN_NUMELEM);
294  i++) {
295  // Prioritize putting info on satellites we're actually
296  // receiving first
297  if (!svinfo->sv[i].cno) {
298  skipped=true;
299  continue;
300  }
301 
302  svdata.Azimuth[chan] = svinfo->sv[i].azim;
303  svdata.Elevation[chan] = svinfo->sv[i].elev;
304  svdata.PRN[chan] = svinfo->sv[i].svid;
305 
306  /* Scale to the range used by NMEA GPSes.. a cno >50 is
307  * extremely improbable. This makes the display in GCS more
308  * consistent between NMEA and UBX GPSes.
309  */
310  if (svinfo->sv[chan].cno < 66) {
311  svdata.SNR[chan] = svinfo->sv[i].cno * 3 / 2;
312  } else {
313  svdata.SNR[chan] = 99;
314  }
315 
316  chan++;
317  }
318 
319  // Now fill any available slots with satellites we're not receiving.
320  if (skipped) {
321  for (int i = 0;
322  (i < svinfo->numCh) && (chan < GPSSATELLITES_PRN_NUMELEM);
323  i++) {
324  if (!svinfo->sv[i].cno) {
325  svdata.Azimuth[chan] = svinfo->sv[i].azim;
326  svdata.Elevation[chan] = svinfo->sv[i].elev;
327  svdata.PRN[chan] = svinfo->sv[i].svid;
328  svdata.SNR[chan] = svinfo->sv[i].cno;
329  chan++;
330  }
331  }
332  }
333 
334  // fill remaining slots (if any)
335  for (; chan < GPSSATELLITES_PRN_NUMELEM; chan++) {
336  svdata.Azimuth[chan] = 0;
337  svdata.Elevation[chan] = 0;
338  svdata.PRN[chan] = 0;
339  svdata.SNR[chan] = 0;
340  }
341 
342  GPSSatellitesSet(&svdata);
343 }
344 
345 static void parse_ubx_mon_ver (const struct UBX_MON_VER *version_info)
346 {
347  UBloxInfoData ublox;
348  UBloxInfoGet(&ublox);
349  // sw version is in the format X.YY
350  ublox.swVersion = (version_info->swVersion[0] - '0') +
351  (version_info->swVersion[2] - '0') * 0.1f +
352  (version_info->swVersion[3] - '0') * 0.01f;
353  for (uint32_t i = 0; i < 4; i++) {
354  ublox.hwVersion = ublox.hwVersion * 10 +
355  version_info->hwVersion[i] - '0';
356  }
357  ublox.ParseErrors = parse_errors;
358  UBloxInfoSet(&ublox);
359 }
360 
361 // UBX message parser
362 // returns UAVObjectID if a UAVObject structure is ready for further processing
363 
364 static uint32_t parse_ubx_message (const struct UBXPacket *ubx, GPSPositionData *GpsPosition)
365 {
366  uint32_t id = 0;
367 
368  switch (ubx->header.class) {
369  case UBX_CLASS_NAV:
370  switch (ubx->header.id) {
371  case UBX_ID_POSLLH:
372  parse_ubx_nav_posllh (&ubx->payload.nav_posllh, GpsPosition);
373  break;
374  case UBX_ID_DOP:
375  parse_ubx_nav_dop (&ubx->payload.nav_dop, GpsPosition);
376  break;
377  case UBX_ID_SOL:
378  parse_ubx_nav_sol (&ubx->payload.nav_sol, GpsPosition);
379  break;
380  case UBX_ID_VELNED:
381  parse_ubx_nav_velned (&ubx->payload.nav_velned, GpsPosition);
382  break;
383  case UBX_ID_TIMEUTC:
384  parse_ubx_nav_timeutc (&ubx->payload.nav_timeutc);
385  break;
386  case UBX_ID_SVINFO:
387  parse_ubx_nav_svinfo (&ubx->payload.nav_svinfo);
388  break;
389  }
390  break;
391  case UBX_CLASS_MON:
392  switch (ubx->header.id) {
393  case UBX_ID_MONVER:
394  parse_ubx_mon_ver (&ubx->payload.mon_ver);
395  break;
396  }
397  break;
398  }
399  if (msgtracker.msg_received == ALL_RECEIVED) {
400  GPSPositionSet(GpsPosition);
401  msgtracker.msg_received = NONE_RECEIVED;
402  id = GPSPOSITION_OBJID;
403  }
404  return id;
405 }
406 
407 #endif // PIOS_INCLUDE_GPS_UBX_PARSER
408 
uint16_t hDOP
Definition: UBX.h:101
uint16_t vDOP
Definition: UBX.h:100
struct UBXHeader header
Definition: UBX.h:223
uint8_t class
Definition: UBX.h:215
static char * gps_rx_buffer
Definition: GPS.c:84
struct UBX_NAV_DOP nav_dop
Definition: UBX.h:206
int32_t lon
Definition: UBX.h:62
uint8_t svid
Definition: UBX.h:176
uint8_t ck_b
Definition: UBX.h:219
uint16_t gpsRxReceived
Definition: GPS.h:45
Main PiOS header to include all the compiled in PiOS options.
#define UBX_CLASS_NAV
Definition: UBX.h:43
struct UBX_NAV_VELNED nav_velned
Definition: UBX.h:208
uint16_t pDOP
Definition: UBX.h:98
uint8_t month
Definition: UBX.h:153
uint8_t sec
Definition: UBX.h:157
uint8_t numCh
Definition: UBX.h:190
uint32_t pAcc
Definition: UBX.h:117
uint8_t cno
Definition: UBX.h:179
#define UBX_SYNC2
Definition: UBX.h:38
uint8_t hwVersion[10]
Definition: UBX.h:198
int32_t heading
Definition: UBX.h:137
uint32_t iTOW
Definition: UBX.h:109
uint8_t id
struct UBX_NAV_TIMEUTC nav_timeutc
Definition: UBX.h:209
#define UBX_ID_TIMEUTC
Definition: UBX.h:51
struct UBX_NAV_SOL nav_sol
Definition: UBX.h:207
int32_t hMSL
Definition: UBX.h:65
struct UBX_NAV_POSLLH nav_posllh
Definition: UBX.h:204
#define UBX_ID_DOP
Definition: UBX.h:48
#define PARSER_ERROR
Definition: GPS.h:40
#define STATUS_FLAGS_GPSFIX_OK
Definition: UBX.h:79
uint8_t day
Definition: UBX.h:154
uint8_t gpsFix
Definition: UBX.h:112
#define MAX_SVS
Definition: UBX.h:186
uint32_t iTOW
Definition: UBX.h:96
#define UBX_SYNC1
Definition: UBX.h:37
int8_t elev
Definition: UBX.h:180
int16_t azim
Definition: UBX.h:181
#define STATUS_GPSFIX_3DFIX
Definition: UBX.h:75
int32_t velE
Definition: UBX.h:133
uint8_t flags
Definition: UBX.h:113
#define UBX_ID_SOL
Definition: UBX.h:49
uint16_t gpsRxChkSumError
Definition: GPS.h:46
uint16_t year
Definition: UBX.h:152
int32_t velD
Definition: UBX.h:134
Include file of the GPS module.
uint16_t len
Definition: UBX.h:217
#define PARSER_COMPLETE
Definition: GPS.h:42
int32_t velN
Definition: UBX.h:132
uint8_t i
Definition: msp_messages.h:97
int parse_ubx_stream(uint8_t, char *, GPSPositionData *, struct GPS_RX_STATS *)
uint8_t ck_a
Definition: UBX.h:218
#define UBX_ID_SVINFO
Definition: UBX.h:52
#define TIMEUTC_VALIDWKN
Definition: UBX.h:145
#define STATUS_FLAGS_DIFFSOLN
Definition: UBX.h:80
uint8_t min
Definition: UBX.h:156
uint32_t iTOW
Definition: UBX.h:131
tuple f
Definition: px_mkfw.py:81
int32_t height
Definition: UBX.h:64
struct UBX_MON_VER mon_ver
Definition: UBX.h:211
int32_t lat
Definition: UBX.h:63
Includes PiOS and core architecture components.
uint8_t id
Definition: UBX.h:216
uint8_t numSV
Definition: UBX.h:124
struct UBX_NAV_SVINFO nav_svinfo
Definition: UBX.h:210
Include file for UBX processing.
static struct GPS_RX_STATS gpsRxStats
Definition: GPS.c:86
#define UBX_ID_POSLLH
Definition: UBX.h:46
uint32_t sAcc
Definition: UBX.h:138
#define STATUS_GPSFIX_2DFIX
Definition: UBX.h:74
#define UBX_ID_VELNED
Definition: UBX.h:50
uint16_t gpsRxOverflow
Definition: GPS.h:47
uint32_t gSpeed
Definition: UBX.h:136
#define UBX_ID_MONVER
Definition: UBX.h:56
uint8_t swVersion[30]
Definition: UBX.h:197
uint8_t payload[0]
Definition: UBX.h:203
uint32_t iTOW
Definition: UBX.h:61
struct UBX_NAV_SVINFO_SV sv[MAX_SVS]
Definition: UBX.h:193
#define UBX_CLASS_MON
Definition: UBX.h:54
uint8_t hour
Definition: UBX.h:155
Definition: UBX.h:222
#define PARSER_INCOMPLETE
Definition: GPS.h:41
UBXPayload payload
Definition: UBX.h:224
uint8_t valid
Definition: UBX.h:158