mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-10-22 03:33:33 +08:00
463 lines
13 KiB
C++
463 lines
13 KiB
C++
/****************************************************************************
|
|
*
|
|
* Copyright (c) 2014, 2015 PX4 Development Team. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in
|
|
* the documentation and/or other materials provided with the
|
|
* distribution.
|
|
* 3. Neither the name PX4 nor the names of its contributors may be
|
|
* used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
|
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
|
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/**
|
|
* @file gnss.cpp
|
|
*
|
|
* @author Pavel Kirienko <pavel.kirienko@gmail.com>
|
|
* @author Andrew Chambers <achamber@gmail.com>
|
|
*
|
|
*/
|
|
|
|
#include "gnss.hpp"
|
|
#include <drivers/drv_hrt.h>
|
|
#include <systemlib/err.h>
|
|
#include <mathlib/mathlib.h>
|
|
|
|
const char *const UavcanGnssBridge::NAME = "gnss";
|
|
|
|
UavcanGnssBridge::UavcanGnssBridge(uavcan::INode &node) :
|
|
_node(node),
|
|
_sub_fix(node),
|
|
_sub_fix2(node),
|
|
_pub_fix2(node),
|
|
_orb_to_uavcan_pub_timer(node, TimerCbBinder(this, &UavcanGnssBridge::broadcast_from_orb)),
|
|
_report_pub(nullptr)
|
|
{
|
|
}
|
|
|
|
UavcanGnssBridge::~UavcanGnssBridge()
|
|
{
|
|
(void) orb_unsubscribe(_orb_sub_gnss);
|
|
}
|
|
|
|
int UavcanGnssBridge::init()
|
|
{
|
|
int res = _pub_fix2.init(uavcan::TransferPriority::MiddleLower);
|
|
|
|
if (res < 0) {
|
|
PX4_WARN("GNSS fix2 pub failed %i", res);
|
|
return res;
|
|
}
|
|
|
|
res = _sub_fix.start(FixCbBinder(this, &UavcanGnssBridge::gnss_fix_sub_cb));
|
|
|
|
if (res < 0) {
|
|
PX4_WARN("GNSS fix sub failed %i", res);
|
|
return res;
|
|
}
|
|
|
|
res = _sub_fix2.start(Fix2CbBinder(this, &UavcanGnssBridge::gnss_fix2_sub_cb));
|
|
|
|
if (res < 0) {
|
|
PX4_WARN("GNSS fix2 sub failed %i", res);
|
|
return res;
|
|
}
|
|
|
|
_orb_to_uavcan_pub_timer.startPeriodic(
|
|
uavcan::MonotonicDuration::fromUSec(1000000U / ORB_TO_UAVCAN_FREQUENCY_HZ));
|
|
|
|
return res;
|
|
}
|
|
|
|
unsigned UavcanGnssBridge::get_num_redundant_channels() const
|
|
{
|
|
return (_receiver_node_id < 0) ? 0 : 1;
|
|
}
|
|
|
|
void UavcanGnssBridge::print_status() const
|
|
{
|
|
printf("RX errors: %d, using old Fix: %d, receiver node id: ",
|
|
_sub_fix.getFailureCount(), int(_old_fix_subscriber_active));
|
|
|
|
if (_receiver_node_id < 0) {
|
|
printf("N/A\n");
|
|
|
|
} else {
|
|
printf("%d\n", _receiver_node_id);
|
|
}
|
|
}
|
|
|
|
void UavcanGnssBridge::gnss_fix_sub_cb(const uavcan::ReceivedDataStructure<uavcan::equipment::gnss::Fix> &msg)
|
|
{
|
|
const bool valid_pos_cov = !msg.position_covariance.empty();
|
|
const bool valid_vel_cov = !msg.velocity_covariance.empty();
|
|
|
|
float pos_cov[9];
|
|
msg.position_covariance.unpackSquareMatrix(pos_cov);
|
|
|
|
float vel_cov[9];
|
|
msg.velocity_covariance.unpackSquareMatrix(vel_cov);
|
|
|
|
process_fixx(msg, pos_cov, vel_cov, valid_pos_cov, valid_vel_cov);
|
|
}
|
|
|
|
void UavcanGnssBridge::gnss_fix2_sub_cb(const uavcan::ReceivedDataStructure<uavcan::equipment::gnss::Fix2> &msg)
|
|
{
|
|
if (_old_fix_subscriber_active) {
|
|
PX4_WARN("GNSS Fix2 message detected, disabling support for the old Fix message");
|
|
_sub_fix.stop();
|
|
_old_fix_subscriber_active = false;
|
|
_receiver_node_id = -1;
|
|
}
|
|
|
|
float pos_cov[9] {};
|
|
float vel_cov[9] {};
|
|
bool valid_covariances = true;
|
|
|
|
switch (msg.covariance.size()) {
|
|
// Scalar matrix
|
|
case 1: {
|
|
const auto x = msg.covariance[0];
|
|
|
|
pos_cov[0] = x;
|
|
pos_cov[4] = x;
|
|
pos_cov[8] = x;
|
|
|
|
vel_cov[0] = x;
|
|
vel_cov[4] = x;
|
|
vel_cov[8] = x;
|
|
break;
|
|
}
|
|
|
|
// Diagonal matrix (the most common case)
|
|
case 6: {
|
|
pos_cov[0] = msg.covariance[0];
|
|
pos_cov[4] = msg.covariance[1];
|
|
pos_cov[8] = msg.covariance[2];
|
|
|
|
vel_cov[0] = msg.covariance[3];
|
|
vel_cov[4] = msg.covariance[4];
|
|
vel_cov[8] = msg.covariance[5];
|
|
break;
|
|
}
|
|
|
|
// Upper triangular matrix.
|
|
// This code has been carefully optimized by hand. We could use unpackSquareMatrix(), but it's slow.
|
|
// Sub-matrix indexes (empty squares contain velocity-position covariance data):
|
|
// 0 1 2
|
|
// 1 6 7
|
|
// 2 7 11
|
|
// 15 16 17
|
|
// 16 18 19
|
|
// 17 19 20
|
|
case 21: {
|
|
pos_cov[0] = msg.covariance[0];
|
|
pos_cov[1] = msg.covariance[1];
|
|
pos_cov[2] = msg.covariance[2];
|
|
pos_cov[3] = msg.covariance[1];
|
|
pos_cov[4] = msg.covariance[6];
|
|
pos_cov[5] = msg.covariance[7];
|
|
pos_cov[6] = msg.covariance[2];
|
|
pos_cov[7] = msg.covariance[7];
|
|
pos_cov[8] = msg.covariance[11];
|
|
|
|
vel_cov[0] = msg.covariance[15];
|
|
vel_cov[1] = msg.covariance[16];
|
|
vel_cov[2] = msg.covariance[17];
|
|
vel_cov[3] = msg.covariance[16];
|
|
vel_cov[4] = msg.covariance[18];
|
|
vel_cov[5] = msg.covariance[19];
|
|
vel_cov[6] = msg.covariance[17];
|
|
vel_cov[7] = msg.covariance[19];
|
|
vel_cov[8] = msg.covariance[20];
|
|
}
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
// Full matrix 6x6.
|
|
// This code has been carefully optimized by hand. We could use unpackSquareMatrix(), but it's slow.
|
|
// Sub-matrix indexes (empty squares contain velocity-position covariance data):
|
|
// 0 1 2
|
|
// 6 7 8
|
|
// 12 13 14
|
|
// 21 22 23
|
|
// 27 28 29
|
|
// 33 34 35
|
|
case 36: {
|
|
pos_cov[0] = msg.covariance[0];
|
|
pos_cov[1] = msg.covariance[1];
|
|
pos_cov[2] = msg.covariance[2];
|
|
pos_cov[3] = msg.covariance[6];
|
|
pos_cov[4] = msg.covariance[7];
|
|
pos_cov[5] = msg.covariance[8];
|
|
pos_cov[6] = msg.covariance[12];
|
|
pos_cov[7] = msg.covariance[13];
|
|
pos_cov[8] = msg.covariance[14];
|
|
|
|
vel_cov[0] = msg.covariance[21];
|
|
vel_cov[1] = msg.covariance[22];
|
|
vel_cov[2] = msg.covariance[23];
|
|
vel_cov[3] = msg.covariance[27];
|
|
vel_cov[4] = msg.covariance[28];
|
|
vel_cov[5] = msg.covariance[29];
|
|
vel_cov[6] = msg.covariance[33];
|
|
vel_cov[7] = msg.covariance[34];
|
|
vel_cov[8] = msg.covariance[35];
|
|
}
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
// Either empty or invalid sized, interpret as zero matrix
|
|
default: {
|
|
valid_covariances = false;
|
|
break; // Nothing to do
|
|
}
|
|
}
|
|
|
|
process_fixx(msg, pos_cov, vel_cov, valid_covariances, valid_covariances);
|
|
}
|
|
|
|
template <typename FixType>
|
|
void UavcanGnssBridge::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)
|
|
{
|
|
// This bridge does not support redundant GNSS receivers yet.
|
|
if (_receiver_node_id < 0) {
|
|
_receiver_node_id = msg.getSrcNodeID().get();
|
|
PX4_WARN("GNSS receiver node ID: %d", _receiver_node_id);
|
|
|
|
} else {
|
|
if (_receiver_node_id != msg.getSrcNodeID().get()) {
|
|
return; // This GNSS receiver is the redundant one, ignore it.
|
|
}
|
|
}
|
|
|
|
auto report = ::vehicle_gps_position_s();
|
|
|
|
/*
|
|
* FIXME HACK
|
|
* There used to be the following line of code:
|
|
* report.timestamp_position = msg.getMonotonicTimestamp().toUSec();
|
|
* It stopped working when the time sync feature has been introduced, because it caused libuavcan
|
|
* to use an independent time source (based on hardware TIM5) instead of HRT.
|
|
* The proper solution is to be developed.
|
|
*/
|
|
report.timestamp = hrt_absolute_time();
|
|
|
|
report.lat = msg.latitude_deg_1e8 / 10;
|
|
report.lon = msg.longitude_deg_1e8 / 10;
|
|
report.alt = msg.height_msl_mm;
|
|
report.alt_ellipsoid = msg.height_ellipsoid_mm;
|
|
|
|
if (valid_pos_cov) {
|
|
// Horizontal position uncertainty
|
|
const float horizontal_pos_variance = math::max(pos_cov[0], pos_cov[4]);
|
|
report.eph = (horizontal_pos_variance > 0) ? sqrtf(horizontal_pos_variance) : -1.0F;
|
|
|
|
// Vertical position uncertainty
|
|
report.epv = (pos_cov[8] > 0) ? sqrtf(pos_cov[8]) : -1.0F;
|
|
|
|
} else {
|
|
report.eph = -1.0F;
|
|
report.epv = -1.0F;
|
|
}
|
|
|
|
if (valid_vel_cov) {
|
|
report.s_variance_m_s = math::max(math::max(vel_cov[0], vel_cov[4]), vel_cov[8]);
|
|
|
|
/* There is a nonlinear relationship between the velocity vector and the heading.
|
|
* Use Jacobian to transform velocity covariance to heading covariance
|
|
*
|
|
* Nonlinear equation:
|
|
* heading = atan2(vel_e_m_s, vel_n_m_s)
|
|
* For math, see http://en.wikipedia.org/wiki/Atan2#Derivative
|
|
*
|
|
* To calculate the variance of heading from the variance of velocity,
|
|
* cov(heading) = J(velocity)*cov(velocity)*J(velocity)^T
|
|
*/
|
|
float vel_n = msg.ned_velocity[0];
|
|
float vel_e = msg.ned_velocity[1];
|
|
float vel_n_sq = vel_n * vel_n;
|
|
float vel_e_sq = vel_e * vel_e;
|
|
report.c_variance_rad =
|
|
(vel_e_sq * vel_cov[0] +
|
|
-2 * vel_n * vel_e * vel_cov[1] + // Covariance matrix is symmetric
|
|
vel_n_sq * vel_cov[4]) / ((vel_n_sq + vel_e_sq) * (vel_n_sq + vel_e_sq));
|
|
|
|
} else {
|
|
report.s_variance_m_s = -1.0F;
|
|
report.c_variance_rad = -1.0F;
|
|
}
|
|
|
|
report.fix_type = msg.status;
|
|
|
|
report.vel_n_m_s = msg.ned_velocity[0];
|
|
report.vel_e_m_s = msg.ned_velocity[1];
|
|
report.vel_d_m_s = msg.ned_velocity[2];
|
|
report.vel_m_s = sqrtf(report.vel_n_m_s * report.vel_n_m_s +
|
|
report.vel_e_m_s * report.vel_e_m_s +
|
|
report.vel_d_m_s * report.vel_d_m_s);
|
|
report.cog_rad = atan2f(report.vel_e_m_s, report.vel_n_m_s);
|
|
report.vel_ned_valid = true;
|
|
|
|
report.timestamp_time_relative = 0;
|
|
|
|
const std::uint64_t gnss_ts_usec = uavcan::UtcTime(msg.gnss_timestamp).toUSec();
|
|
|
|
switch (msg.gnss_time_standard) {
|
|
case FixType::GNSS_TIME_STANDARD_UTC: {
|
|
report.time_utc_usec = gnss_ts_usec;
|
|
break;
|
|
}
|
|
|
|
case FixType::GNSS_TIME_STANDARD_GPS: {
|
|
if (msg.num_leap_seconds > 0) {
|
|
report.time_utc_usec = gnss_ts_usec - msg.num_leap_seconds + 9;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case FixType::GNSS_TIME_STANDARD_TAI: {
|
|
if (msg.num_leap_seconds > 0) {
|
|
report.time_utc_usec = gnss_ts_usec - msg.num_leap_seconds - 10;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default: {
|
|
break;
|
|
}
|
|
}
|
|
|
|
//TODO px4_clock_settime does nothing on the Snapdragon platform
|
|
#ifndef __PX4_QURT
|
|
|
|
// If we haven't already done so, set the system clock using GPS data
|
|
if (valid_pos_cov && !_system_clock_set) {
|
|
timespec ts;
|
|
memset(&ts, 0, sizeof(ts));
|
|
// get the whole microseconds
|
|
ts.tv_sec = report.time_utc_usec / 1000000ULL;
|
|
// get the remainder microseconds and convert to nanoseconds
|
|
ts.tv_nsec = (report.time_utc_usec % 1000000ULL) * 1000;
|
|
px4_clock_settime(CLOCK_REALTIME, &ts);
|
|
_system_clock_set = true;
|
|
}
|
|
|
|
#endif
|
|
|
|
report.satellites_used = msg.sats_used;
|
|
|
|
// Using PDOP for HDOP and VDOP
|
|
// Relevant discussion: https://github.com/PX4/Firmware/issues/5153
|
|
report.hdop = msg.pdop;
|
|
report.vdop = msg.pdop;
|
|
|
|
report.heading = NAN;
|
|
|
|
// Publish to a multi-topic
|
|
int32_t gps_orb_instance;
|
|
orb_publish_auto(ORB_ID(vehicle_gps_position), &_report_pub, &report, &gps_orb_instance,
|
|
ORB_PRIO_HIGH);
|
|
|
|
// Doing less time critical stuff here
|
|
if (_orb_to_uavcan_pub_timer.isRunning()) {
|
|
_orb_to_uavcan_pub_timer.stop();
|
|
PX4_WARN("GNSS ORB->UAVCAN bridge stopped, because there are other GNSS publishers");
|
|
}
|
|
}
|
|
|
|
void UavcanGnssBridge::broadcast_from_orb(const uavcan::TimerEvent &)
|
|
{
|
|
if (_orb_sub_gnss < 0) {
|
|
// ORB subscription stops working if this is relocated into init()
|
|
_orb_sub_gnss = orb_subscribe(ORB_ID(vehicle_gps_position));
|
|
|
|
if (_orb_sub_gnss < 0) {
|
|
PX4_WARN("GNSS ORB sub errno %d", errno);
|
|
return;
|
|
}
|
|
|
|
PX4_WARN("GNSS ORB fd %d", _orb_sub_gnss);
|
|
}
|
|
|
|
{
|
|
bool updated = false;
|
|
const int res = orb_check(_orb_sub_gnss, &updated);
|
|
|
|
if (res < 0) {
|
|
PX4_WARN("GNSS ORB check err %d %d", res, errno);
|
|
return;
|
|
}
|
|
|
|
if (!updated) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
auto orb_msg = ::vehicle_gps_position_s();
|
|
const int res = orb_copy(ORB_ID(vehicle_gps_position), _orb_sub_gnss, &orb_msg);
|
|
|
|
if (res < 0) {
|
|
PX4_WARN("GNSS ORB read errno %d", errno);
|
|
return;
|
|
}
|
|
|
|
// Convert to UAVCAN
|
|
using uavcan::equipment::gnss::Fix2;
|
|
Fix2 msg;
|
|
|
|
msg.gnss_timestamp = uavcan::UtcTime::fromUSec(orb_msg.time_utc_usec);
|
|
msg.gnss_time_standard = Fix2::GNSS_TIME_STANDARD_UTC;
|
|
|
|
msg.longitude_deg_1e8 = std::int64_t(orb_msg.lon) * 10LL;
|
|
msg.latitude_deg_1e8 = std::int64_t(orb_msg.lat) * 10LL;
|
|
msg.height_ellipsoid_mm = orb_msg.alt_ellipsoid;
|
|
msg.height_msl_mm = orb_msg.alt;
|
|
|
|
msg.ned_velocity[0] = orb_msg.vel_n_m_s;
|
|
msg.ned_velocity[1] = orb_msg.vel_e_m_s;
|
|
msg.ned_velocity[2] = orb_msg.vel_d_m_s;
|
|
|
|
msg.sats_used = orb_msg.satellites_used;
|
|
msg.status = orb_msg.fix_type;
|
|
// mode skipped
|
|
// sub mode skipped
|
|
|
|
// diagonal covariance matrix
|
|
msg.covariance.resize(2, orb_msg.eph * orb_msg.eph);
|
|
msg.covariance.resize(3, orb_msg.epv * orb_msg.epv);
|
|
msg.covariance.resize(6, orb_msg.s_variance_m_s * orb_msg.s_variance_m_s);
|
|
|
|
msg.pdop = (orb_msg.hdop > orb_msg.vdop) ? orb_msg.hdop : orb_msg.vdop; // this is a hack :(
|
|
|
|
// Publishing now
|
|
(void) _pub_fix2.broadcast(msg);
|
|
}
|