PX4 Firmware
PX4 Autopilot Software http://px4.io
gnss.cpp
Go to the documentation of this file.
1 /****************************************************************************
2  *
3  * Copyright (c) 2014, 2015 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 /**
35  * @file gnss.cpp
36  *
37  * @author Pavel Kirienko <pavel.kirienko@gmail.com>
38  * @author Andrew Chambers <achamber@gmail.com>
39  *
40  */
41 
42 #include "gnss.hpp"
43 
44 #include <cstdint>
45 
46 #include <drivers/drv_hrt.h>
47 #include <systemlib/err.h>
48 #include <mathlib/mathlib.h>
49 
50 using namespace time_literals;
51 
52 const char *const UavcanGnssBridge::NAME = "gnss";
53 
54 UavcanGnssBridge::UavcanGnssBridge(uavcan::INode &node) :
55  _node(node),
56  _sub_auxiliary(node),
57  _sub_fix(node),
58  _sub_fix2(node),
59  _pub_fix2(node),
60  _orb_to_uavcan_pub_timer(node, TimerCbBinder(this, &UavcanGnssBridge::broadcast_from_orb))
61 {
62 }
63 
64 int
66 {
67  int res = _pub_fix2.init(uavcan::TransferPriority::MiddleLower);
68 
69  if (res < 0) {
70  PX4_WARN("GNSS fix2 pub failed %i", res);
71  return res;
72  }
73 
75 
76  if (res < 0) {
77  PX4_WARN("GNSS auxiliary sub failed %i", res);
78  return res;
79  }
80 
82 
83  if (res < 0) {
84  PX4_WARN("GNSS fix sub failed %i", res);
85  return res;
86  }
87 
89 
90  if (res < 0) {
91  PX4_WARN("GNSS fix2 sub failed %i", res);
92  return res;
93  }
94 
95  _orb_to_uavcan_pub_timer.startPeriodic(uavcan::MonotonicDuration::fromUSec(1000000U / ORB_TO_UAVCAN_FREQUENCY_HZ));
96 
97  return res;
98 }
99 
100 unsigned
102 {
103  return (_receiver_node_id < 0) ? 0 : 1;
104 }
105 
106 void
108 {
109  printf("RX errors: %d, using old Fix: %d, receiver node id: ",
110  _sub_fix.getFailureCount(), int(_old_fix_subscriber_active));
111 
112  if (_receiver_node_id < 0) {
113  printf("N/A\n");
114 
115  } else {
116  printf("%d\n", _receiver_node_id);
117  }
118 }
119 
120 void
121 UavcanGnssBridge::gnss_auxiliary_sub_cb(const uavcan::ReceivedDataStructure<uavcan::equipment::gnss::Auxiliary> &msg)
122 {
123  // store latest hdop and vdop for use in process_fixx();
125  _last_gnss_auxiliary_hdop = msg.hdop;
126  _last_gnss_auxiliary_vdop = msg.vdop;
127 }
128 
129 void
130 UavcanGnssBridge::gnss_fix_sub_cb(const uavcan::ReceivedDataStructure<uavcan::equipment::gnss::Fix> &msg)
131 {
132  const bool valid_pos_cov = !msg.position_covariance.empty();
133  const bool valid_vel_cov = !msg.velocity_covariance.empty();
134 
135  float pos_cov[9];
136  msg.position_covariance.unpackSquareMatrix(pos_cov);
137 
138  float vel_cov[9];
139  msg.velocity_covariance.unpackSquareMatrix(vel_cov);
140 
141  process_fixx(msg, pos_cov, vel_cov, valid_pos_cov, valid_vel_cov);
142 }
143 
144 void
145 UavcanGnssBridge::gnss_fix2_sub_cb(const uavcan::ReceivedDataStructure<uavcan::equipment::gnss::Fix2> &msg)
146 {
148  PX4_INFO("GNSS Fix2 message detected, disabling support for the old Fix message");
149  _sub_fix.stop();
151  _receiver_node_id = -1;
152  }
153 
154  float pos_cov[9] {};
155  float vel_cov[9] {};
156  bool valid_covariances = true;
157 
158  switch (msg.covariance.size()) {
159  case 1: {
160  // Scalar matrix
161  const auto x = msg.covariance[0];
162 
163  pos_cov[0] = x;
164  pos_cov[4] = x;
165  pos_cov[8] = x;
166 
167  vel_cov[0] = x;
168  vel_cov[4] = x;
169  vel_cov[8] = x;
170  }
171  break;
172 
173  case 6: {
174  // Diagonal matrix (the most common case)
175  pos_cov[0] = msg.covariance[0];
176  pos_cov[4] = msg.covariance[1];
177  pos_cov[8] = msg.covariance[2];
178 
179  vel_cov[0] = msg.covariance[3];
180  vel_cov[4] = msg.covariance[4];
181  vel_cov[8] = msg.covariance[5];
182 
183  }
184  break;
185 
186 
187  case 21: {
188  // Upper triangular matrix.
189  // This code has been carefully optimized by hand. We could use unpackSquareMatrix(), but it's slow.
190  // Sub-matrix indexes (empty squares contain velocity-position covariance data):
191  // 0 1 2
192  // 1 6 7
193  // 2 7 11
194  // 15 16 17
195  // 16 18 19
196  // 17 19 20
197  pos_cov[0] = msg.covariance[0];
198  pos_cov[1] = msg.covariance[1];
199  pos_cov[2] = msg.covariance[2];
200  pos_cov[3] = msg.covariance[1];
201  pos_cov[4] = msg.covariance[6];
202  pos_cov[5] = msg.covariance[7];
203  pos_cov[6] = msg.covariance[2];
204  pos_cov[7] = msg.covariance[7];
205  pos_cov[8] = msg.covariance[11];
206 
207  vel_cov[0] = msg.covariance[15];
208  vel_cov[1] = msg.covariance[16];
209  vel_cov[2] = msg.covariance[17];
210  vel_cov[3] = msg.covariance[16];
211  vel_cov[4] = msg.covariance[18];
212  vel_cov[5] = msg.covariance[19];
213  vel_cov[6] = msg.covariance[17];
214  vel_cov[7] = msg.covariance[19];
215  vel_cov[8] = msg.covariance[20];
216  }
217 
218  /* FALLTHROUGH */
219  case 36: {
220  // Full matrix 6x6.
221  // This code has been carefully optimized by hand. We could use unpackSquareMatrix(), but it's slow.
222  // Sub-matrix indexes (empty squares contain velocity-position covariance data):
223  // 0 1 2
224  // 6 7 8
225  // 12 13 14
226  // 21 22 23
227  // 27 28 29
228  // 33 34 35
229  pos_cov[0] = msg.covariance[0];
230  pos_cov[1] = msg.covariance[1];
231  pos_cov[2] = msg.covariance[2];
232  pos_cov[3] = msg.covariance[6];
233  pos_cov[4] = msg.covariance[7];
234  pos_cov[5] = msg.covariance[8];
235  pos_cov[6] = msg.covariance[12];
236  pos_cov[7] = msg.covariance[13];
237  pos_cov[8] = msg.covariance[14];
238 
239  vel_cov[0] = msg.covariance[21];
240  vel_cov[1] = msg.covariance[22];
241  vel_cov[2] = msg.covariance[23];
242  vel_cov[3] = msg.covariance[27];
243  vel_cov[4] = msg.covariance[28];
244  vel_cov[5] = msg.covariance[29];
245  vel_cov[6] = msg.covariance[33];
246  vel_cov[7] = msg.covariance[34];
247  vel_cov[8] = msg.covariance[35];
248  }
249 
250  /* FALLTHROUGH */
251  default: {
252  // Either empty or invalid sized, interpret as zero matrix
253  valid_covariances = false;
254  break; // Nothing to do
255  }
256  }
257 
258  process_fixx(msg, pos_cov, vel_cov, valid_covariances, valid_covariances);
259 }
260 
261 template <typename FixType>
262 void UavcanGnssBridge::process_fixx(const uavcan::ReceivedDataStructure<FixType> &msg,
263  const float (&pos_cov)[9], const float (&vel_cov)[9],
264  const bool valid_pos_cov, const bool valid_vel_cov)
265 {
266  // This bridge does not support redundant GNSS receivers yet.
267  if (_receiver_node_id < 0) {
268  _receiver_node_id = msg.getSrcNodeID().get();
269  PX4_INFO("GNSS receiver node ID: %d", _receiver_node_id);
270 
271  } else {
272  if (_receiver_node_id != msg.getSrcNodeID().get()) {
273  return; // This GNSS receiver is the redundant one, ignore it.
274  }
275  }
276 
277  vehicle_gps_position_s report{};
278 
279  /*
280  * FIXME HACK
281  * There used to be the following line of code:
282  * report.timestamp_position = msg.getMonotonicTimestamp().toUSec();
283  * It stopped working when the time sync feature has been introduced, because it caused libuavcan
284  * to use an independent time source (based on hardware TIM5) instead of HRT.
285  * The proper solution is to be developed.
286  */
287  report.timestamp = hrt_absolute_time();
288 
289  report.lat = msg.latitude_deg_1e8 / 10;
290  report.lon = msg.longitude_deg_1e8 / 10;
291  report.alt = msg.height_msl_mm;
292  report.alt_ellipsoid = msg.height_ellipsoid_mm;
293 
294  if (valid_pos_cov) {
295  // Horizontal position uncertainty
296  const float horizontal_pos_variance = math::max(pos_cov[0], pos_cov[4]);
297  report.eph = (horizontal_pos_variance > 0) ? sqrtf(horizontal_pos_variance) : -1.0F;
298 
299  // Vertical position uncertainty
300  report.epv = (pos_cov[8] > 0) ? sqrtf(pos_cov[8]) : -1.0F;
301 
302  } else {
303  report.eph = -1.0F;
304  report.epv = -1.0F;
305  }
306 
307  if (valid_vel_cov) {
308  report.s_variance_m_s = math::max(math::max(vel_cov[0], vel_cov[4]), vel_cov[8]);
309 
310  /* There is a nonlinear relationship between the velocity vector and the heading.
311  * Use Jacobian to transform velocity covariance to heading covariance
312  *
313  * Nonlinear equation:
314  * heading = atan2(vel_e_m_s, vel_n_m_s)
315  * For math, see http://en.wikipedia.org/wiki/Atan2#Derivative
316  *
317  * To calculate the variance of heading from the variance of velocity,
318  * cov(heading) = J(velocity)*cov(velocity)*J(velocity)^T
319  */
320  float vel_n = msg.ned_velocity[0];
321  float vel_e = msg.ned_velocity[1];
322  float vel_n_sq = vel_n * vel_n;
323  float vel_e_sq = vel_e * vel_e;
324  report.c_variance_rad =
325  (vel_e_sq * vel_cov[0] +
326  -2 * vel_n * vel_e * vel_cov[1] + // Covariance matrix is symmetric
327  vel_n_sq * vel_cov[4]) / ((vel_n_sq + vel_e_sq) * (vel_n_sq + vel_e_sq));
328 
329  } else {
330  report.s_variance_m_s = -1.0F;
331  report.c_variance_rad = -1.0F;
332  }
333 
334  report.fix_type = msg.status;
335 
336  report.vel_n_m_s = msg.ned_velocity[0];
337  report.vel_e_m_s = msg.ned_velocity[1];
338  report.vel_d_m_s = msg.ned_velocity[2];
339  report.vel_m_s = sqrtf(report.vel_n_m_s * report.vel_n_m_s +
340  report.vel_e_m_s * report.vel_e_m_s +
341  report.vel_d_m_s * report.vel_d_m_s);
342  report.cog_rad = atan2f(report.vel_e_m_s, report.vel_n_m_s);
343  report.vel_ned_valid = true;
344 
345  report.timestamp_time_relative = 0;
346 
347  const uint64_t gnss_ts_usec = uavcan::UtcTime(msg.gnss_timestamp).toUSec();
348 
349  switch (msg.gnss_time_standard) {
350  case FixType::GNSS_TIME_STANDARD_UTC:
351  report.time_utc_usec = gnss_ts_usec;
352  break;
353 
354  case FixType::GNSS_TIME_STANDARD_GPS:
355  if (msg.num_leap_seconds > 0) {
356  report.time_utc_usec = gnss_ts_usec - msg.num_leap_seconds + 9;
357  }
358 
359  break;
360 
361  case FixType::GNSS_TIME_STANDARD_TAI:
362  if (msg.num_leap_seconds > 0) {
363  report.time_utc_usec = gnss_ts_usec - msg.num_leap_seconds - 10;
364  }
365 
366  break;
367 
368  default:
369  break;
370  }
371 
372  // If we haven't already done so, set the system clock using GPS data
373  if (valid_pos_cov && !_system_clock_set) {
374  timespec ts{};
375 
376  // get the whole microseconds
377  ts.tv_sec = report.time_utc_usec / 1000000ULL;
378 
379  // get the remainder microseconds and convert to nanoseconds
380  ts.tv_nsec = (report.time_utc_usec % 1000000ULL) * 1000;
381 
382  px4_clock_settime(CLOCK_REALTIME, &ts);
383 
384  _system_clock_set = true;
385  }
386 
387  report.satellites_used = msg.sats_used;
388 
390  report.hdop = _last_gnss_auxiliary_hdop;
391  report.vdop = _last_gnss_auxiliary_vdop;
392 
393  } else {
394  // Using PDOP for HDOP and VDOP
395  // Relevant discussion: https://github.com/PX4/Firmware/issues/5153
396  report.hdop = msg.pdop;
397  report.vdop = msg.pdop;
398  }
399 
400  report.heading = NAN;
401  report.heading_offset = NAN;
402 
403  // Publish to a multi-topic
404  _gps_pub.publish(report);
405 
406  // Doing less time critical stuff here
407  if (_orb_to_uavcan_pub_timer.isRunning()) {
409  PX4_INFO("GNSS ORB->UAVCAN bridge stopped, because there are other GNSS publishers");
410  }
411 }
412 
413 void
414 UavcanGnssBridge::broadcast_from_orb(const uavcan::TimerEvent &)
415 {
416  vehicle_gps_position_s orb_msg{};
417 
418  if (!_orb_sub_gnss.update(&orb_msg)) {
419  return;
420  }
421 
422  // Convert to UAVCAN
423  using uavcan::equipment::gnss::Fix2;
424  Fix2 msg;
425 
426  msg.gnss_timestamp = uavcan::UtcTime::fromUSec(orb_msg.time_utc_usec);
427  msg.gnss_time_standard = Fix2::GNSS_TIME_STANDARD_UTC;
428 
429  msg.longitude_deg_1e8 = std::int64_t(orb_msg.lon) * 10LL;
430  msg.latitude_deg_1e8 = std::int64_t(orb_msg.lat) * 10LL;
431  msg.height_ellipsoid_mm = orb_msg.alt_ellipsoid;
432  msg.height_msl_mm = orb_msg.alt;
433 
434  msg.ned_velocity[0] = orb_msg.vel_n_m_s;
435  msg.ned_velocity[1] = orb_msg.vel_e_m_s;
436  msg.ned_velocity[2] = orb_msg.vel_d_m_s;
437 
438  msg.sats_used = orb_msg.satellites_used;
439  msg.status = orb_msg.fix_type;
440  // mode skipped
441  // sub mode skipped
442 
443  // diagonal covariance matrix
444  msg.covariance.resize(2, orb_msg.eph * orb_msg.eph);
445  msg.covariance.resize(3, orb_msg.epv * orb_msg.epv);
446  msg.covariance.resize(6, orb_msg.s_variance_m_s * orb_msg.s_variance_m_s);
447 
448  msg.pdop = (orb_msg.hdop > orb_msg.vdop) ? orb_msg.hdop : orb_msg.vdop; // this is a hack :(
449 
450  // Publishing now
451  _pub_fix2.broadcast(msg);
452 }
void gnss_fix2_sub_cb(const uavcan::ReceivedDataStructure< uavcan::equipment::gnss::Fix2 > &msg)
Definition: gnss.cpp:145
int init() override
Starts the bridge.
Definition: gnss.cpp:65
bool _old_fix_subscriber_active
Definition: gnss.hpp:125
uavcan::MethodBinder< UavcanGnssBridge *, void(UavcanGnssBridge::*)(const uavcan::ReceivedDataStructure< uavcan::equipment::gnss::Auxiliary > &) > AuxiliaryCbBinder
Definition: gnss.hpp:93
uavcan::Publisher< uavcan::equipment::gnss::Fix2 > _pub_fix2
Definition: gnss.hpp:113
bool _system_clock_set
Have we set the system clock at least once from GNSS data?
Definition: gnss.hpp:126
static constexpr unsigned ORB_TO_UAVCAN_FREQUENCY_HZ
Definition: gnss.hpp:60
int _receiver_node_id
Definition: gnss.hpp:124
UavcanGnssBridge(uavcan::INode &node)
Definition: gnss.cpp:54
High-resolution timer with callouts and timekeeping.
uORB::Subscription _orb_sub_gnss
Definition: gnss.hpp:122
uavcan::TimerEventForwarder< TimerCbBinder > _orb_to_uavcan_pub_timer
Definition: gnss.hpp:115
void broadcast_from_orb(const uavcan::TimerEvent &)
Definition: gnss.cpp:414
uavcan::MethodBinder< UavcanGnssBridge *, void(UavcanGnssBridge::*)(const uavcan::TimerEvent &)> TimerCbBinder
Definition: gnss.hpp:105
static char msg[NUM_MSG][CONFIG_USART1_TXBUFSIZE]
Definition: px4io.c:89
uORB::PublicationMulti< vehicle_gps_position_s > _gps_pub
Definition: gnss.hpp:121
static hrt_abstime hrt_elapsed_time(const hrt_abstime *then)
Compute the delta between a timestamp taken in the past and now.
Definition: drv_hrt.h:102
Simple error/warning functions, heavily inspired by the BSD functions of the same names...
void gnss_fix_sub_cb(const uavcan::ReceivedDataStructure< uavcan::equipment::gnss::Fix > &msg)
Definition: gnss.cpp:130
uavcan::MethodBinder< UavcanGnssBridge *, void(UavcanGnssBridge::*)(const uavcan::ReceivedDataStructure< uavcan::equipment::gnss::Fix > &) > FixCbBinder
Definition: gnss.hpp:97
uavcan::Subscriber< uavcan::equipment::gnss::Fix, FixCbBinder > _sub_fix
Definition: gnss.hpp:110
UAVCAN <–> ORB bridge for GNSS messages: uavcan.equipment.gnss.Fix (deprecated, but still supported ...
uavcan::MethodBinder< UavcanGnssBridge *, void(UavcanGnssBridge::*)(const uavcan::ReceivedDataStructure< uavcan::equipment::gnss::Fix2 > &) > Fix2CbBinder
Definition: gnss.hpp:101
unsigned get_num_redundant_channels() const override
Returns number of active redundancy channels.
Definition: gnss.cpp:101
uavcan::Subscriber< uavcan::equipment::gnss::Fix2, Fix2CbBinder > _sub_fix2
Definition: gnss.hpp:111
void gnss_auxiliary_sub_cb(const uavcan::ReceivedDataStructure< uavcan::equipment::gnss::Auxiliary > &msg)
GNSS fix message will be reported via this callback.
Definition: gnss.cpp:121
uavcan::Subscriber< uavcan::equipment::gnss::Auxiliary, AuxiliaryCbBinder > _sub_auxiliary
Definition: gnss.hpp:109
constexpr _Tp max(_Tp a, _Tp b)
Definition: Limits.hpp:60
float _last_gnss_auxiliary_hdop
Definition: gnss.hpp:118
static const char *const NAME
Definition: gnss.hpp:63
float _last_gnss_auxiliary_vdop
Definition: gnss.hpp:119
uint64_t _last_gnss_auxiliary_timestamp
Definition: gnss.hpp:117
void print_status() const override
Prints current status in a human readable format to stdout.
Definition: gnss.cpp:107
bool update(void *dst)
Update the struct.
bool publish(const T &data)
Publish the struct.
void process_fixx(const uavcan::ReceivedDataStructure< FixType > &msg, const float(&pos_cov)[9], const float(&vel_cov)[9], const bool valid_pos_cov, const bool valid_vel_cov)
Definition: gnss.cpp:262
__EXPORT hrt_abstime hrt_absolute_time(void)
Get absolute time in [us] (does not wrap).