forked from Archive/PX4-Autopilot
mavlink : add advanced timesync algorithm
This commit is contained in:
parent
6b2daef5ec
commit
39bb65ffd7
|
@ -103,7 +103,7 @@ set(msg_files
|
||||||
tecs_status.msg
|
tecs_status.msg
|
||||||
telemetry_status.msg
|
telemetry_status.msg
|
||||||
test_motor.msg
|
test_motor.msg
|
||||||
time_offset.msg
|
timesync_status.msg
|
||||||
transponder_report.msg
|
transponder_report.msg
|
||||||
tune_control.msg
|
tune_control.msg
|
||||||
uavcan_parameter_request.msg
|
uavcan_parameter_request.msg
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
uint64 offset_ns # time offset between companion system and PX4, in nanoseconds
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
uint64 remote_timestamp # remote system timestamp (microseconds)
|
||||||
|
int64 observed_offset # raw time offset directly observed from this timesync packet (microseconds)
|
||||||
|
int64 estimated_offset # smoothed time offset between companion system and PX4 (microseconds)
|
||||||
|
uint32 round_trip_time # round trip time of this timesync packet (microseconds)
|
|
@ -60,6 +60,7 @@ px4_add_module(
|
||||||
mavlink_simple_analyzer.cpp
|
mavlink_simple_analyzer.cpp
|
||||||
mavlink_stream.cpp
|
mavlink_stream.cpp
|
||||||
mavlink_ulog.cpp
|
mavlink_ulog.cpp
|
||||||
|
mavlink_timesync.cpp
|
||||||
DEPENDS
|
DEPENDS
|
||||||
git_mavlink_v2
|
git_mavlink_v2
|
||||||
conversion
|
conversion
|
||||||
|
|
|
@ -96,6 +96,7 @@ MavlinkReceiver::MavlinkReceiver(Mavlink *parent) :
|
||||||
_parameters_manager(parent),
|
_parameters_manager(parent),
|
||||||
_mavlink_ftp(parent),
|
_mavlink_ftp(parent),
|
||||||
_mavlink_log_handler(parent),
|
_mavlink_log_handler(parent),
|
||||||
|
_mavlink_timesync(parent),
|
||||||
_status{},
|
_status{},
|
||||||
_hil_local_pos{},
|
_hil_local_pos{},
|
||||||
_hil_land_detector{},
|
_hil_land_detector{},
|
||||||
|
@ -128,7 +129,6 @@ MavlinkReceiver::MavlinkReceiver(Mavlink *parent) :
|
||||||
_manual_pub(nullptr),
|
_manual_pub(nullptr),
|
||||||
_obstacle_distance_pub(nullptr),
|
_obstacle_distance_pub(nullptr),
|
||||||
_land_detector_pub(nullptr),
|
_land_detector_pub(nullptr),
|
||||||
_time_offset_pub(nullptr),
|
|
||||||
_follow_target_pub(nullptr),
|
_follow_target_pub(nullptr),
|
||||||
_landing_target_pose_pub(nullptr),
|
_landing_target_pose_pub(nullptr),
|
||||||
_transponder_report_pub(nullptr),
|
_transponder_report_pub(nullptr),
|
||||||
|
@ -147,8 +147,6 @@ MavlinkReceiver::MavlinkReceiver(Mavlink *parent) :
|
||||||
_hil_local_alt0(0.0f),
|
_hil_local_alt0(0.0f),
|
||||||
_hil_local_proj_ref{},
|
_hil_local_proj_ref{},
|
||||||
_offboard_control_mode{},
|
_offboard_control_mode{},
|
||||||
_time_offset_avg_alpha(0.8),
|
|
||||||
_time_offset(0),
|
|
||||||
_orb_class_instance(-1),
|
_orb_class_instance(-1),
|
||||||
_mom_switch_pos{},
|
_mom_switch_pos{},
|
||||||
_mom_switch_state(0),
|
_mom_switch_state(0),
|
||||||
|
@ -285,14 +283,6 @@ MavlinkReceiver::handle_message(mavlink_message_t *msg)
|
||||||
handle_message_heartbeat(msg);
|
handle_message_heartbeat(msg);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MAVLINK_MSG_ID_SYSTEM_TIME:
|
|
||||||
handle_message_system_time(msg);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MAVLINK_MSG_ID_TIMESYNC:
|
|
||||||
handle_message_timesync(msg);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MAVLINK_MSG_ID_DISTANCE_SENSOR:
|
case MAVLINK_MSG_ID_DISTANCE_SENSOR:
|
||||||
handle_message_distance_sensor(msg);
|
handle_message_distance_sensor(msg);
|
||||||
break;
|
break;
|
||||||
|
@ -812,7 +802,7 @@ MavlinkReceiver::handle_message_att_pos_mocap(mavlink_message_t *msg)
|
||||||
// Use the component ID to identify the mocap system
|
// Use the component ID to identify the mocap system
|
||||||
att_pos_mocap.id = msg->compid;
|
att_pos_mocap.id = msg->compid;
|
||||||
|
|
||||||
att_pos_mocap.timestamp = sync_stamp(mocap.time_usec);
|
att_pos_mocap.timestamp = _mavlink_timesync.sync_stamp(mocap.time_usec);
|
||||||
att_pos_mocap.timestamp_received = hrt_absolute_time();
|
att_pos_mocap.timestamp_received = hrt_absolute_time();
|
||||||
|
|
||||||
att_pos_mocap.q[0] = mocap.q[0];
|
att_pos_mocap.q[0] = mocap.q[0];
|
||||||
|
@ -1106,7 +1096,7 @@ MavlinkReceiver::handle_message_attitude_quaternion_cov(mavlink_message_t *msg)
|
||||||
|
|
||||||
struct vehicle_attitude_s vision_attitude = {};
|
struct vehicle_attitude_s vision_attitude = {};
|
||||||
|
|
||||||
vision_attitude.timestamp = sync_stamp(att.time_usec);
|
vision_attitude.timestamp = _mavlink_timesync.sync_stamp(att.time_usec);
|
||||||
|
|
||||||
vision_attitude.q[0] = att.q[0];
|
vision_attitude.q[0] = att.q[0];
|
||||||
vision_attitude.q[1] = att.q[1];
|
vision_attitude.q[1] = att.q[1];
|
||||||
|
@ -1132,7 +1122,7 @@ MavlinkReceiver::handle_message_local_position_ned_cov(mavlink_message_t *msg)
|
||||||
|
|
||||||
struct vehicle_local_position_s vision_position = {};
|
struct vehicle_local_position_s vision_position = {};
|
||||||
|
|
||||||
vision_position.timestamp = sync_stamp(pos.time_usec);
|
vision_position.timestamp = _mavlink_timesync.sync_stamp(pos.time_usec);
|
||||||
|
|
||||||
vision_position.xy_valid = true;
|
vision_position.xy_valid = true;
|
||||||
vision_position.z_valid = true;
|
vision_position.z_valid = true;
|
||||||
|
@ -1192,7 +1182,7 @@ MavlinkReceiver::handle_message_vision_position_estimate(mavlink_message_t *msg)
|
||||||
|
|
||||||
struct vehicle_local_position_s vision_position = {};
|
struct vehicle_local_position_s vision_position = {};
|
||||||
|
|
||||||
vision_position.timestamp = sync_stamp(pos.usec);
|
vision_position.timestamp = _mavlink_timesync.sync_stamp(pos.usec);
|
||||||
vision_position.x = pos.x;
|
vision_position.x = pos.x;
|
||||||
vision_position.y = pos.y;
|
vision_position.y = pos.y;
|
||||||
vision_position.z = pos.z;
|
vision_position.z = pos.z;
|
||||||
|
@ -1204,7 +1194,7 @@ MavlinkReceiver::handle_message_vision_position_estimate(mavlink_message_t *msg)
|
||||||
|
|
||||||
struct vehicle_attitude_s vision_attitude = {};
|
struct vehicle_attitude_s vision_attitude = {};
|
||||||
|
|
||||||
vision_attitude.timestamp = sync_stamp(pos.usec);
|
vision_attitude.timestamp = _mavlink_timesync.sync_stamp(pos.usec);
|
||||||
|
|
||||||
matrix::Quatf q(matrix::Eulerf(pos.roll, pos.pitch, pos.yaw));
|
matrix::Quatf q(matrix::Eulerf(pos.roll, pos.pitch, pos.yaw));
|
||||||
q.copyTo(vision_attitude.q);
|
q.copyTo(vision_attitude.q);
|
||||||
|
@ -1793,80 +1783,6 @@ MavlinkReceiver::get_message_interval(int msgId)
|
||||||
mavlink_msg_message_interval_send(_mavlink->get_channel(), msgId, interval);
|
mavlink_msg_message_interval_send(_mavlink->get_channel(), msgId, interval);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
MavlinkReceiver::handle_message_system_time(mavlink_message_t *msg)
|
|
||||||
{
|
|
||||||
mavlink_system_time_t time;
|
|
||||||
mavlink_msg_system_time_decode(msg, &time);
|
|
||||||
|
|
||||||
timespec tv = {};
|
|
||||||
px4_clock_gettime(CLOCK_REALTIME, &tv);
|
|
||||||
|
|
||||||
// date -d @1234567890: Sat Feb 14 02:31:30 MSK 2009
|
|
||||||
bool onb_unix_valid = (unsigned long long)tv.tv_sec > PX4_EPOCH_SECS;
|
|
||||||
bool ofb_unix_valid = time.time_unix_usec > PX4_EPOCH_SECS * 1000ULL;
|
|
||||||
|
|
||||||
if (!onb_unix_valid && ofb_unix_valid) {
|
|
||||||
tv.tv_sec = time.time_unix_usec / 1000000ULL;
|
|
||||||
tv.tv_nsec = (time.time_unix_usec % 1000000ULL) * 1000ULL;
|
|
||||||
|
|
||||||
if (px4_clock_settime(CLOCK_REALTIME, &tv)) {
|
|
||||||
PX4_ERR("failed setting clock");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
MavlinkReceiver::handle_message_timesync(mavlink_message_t *msg)
|
|
||||||
{
|
|
||||||
mavlink_timesync_t tsync = {};
|
|
||||||
mavlink_msg_timesync_decode(msg, &tsync);
|
|
||||||
|
|
||||||
struct time_offset_s tsync_offset = {};
|
|
||||||
|
|
||||||
uint64_t now_ns = hrt_absolute_time() * 1000LL ;
|
|
||||||
|
|
||||||
if (tsync.tc1 == 0) {
|
|
||||||
|
|
||||||
mavlink_timesync_t rsync; // return timestamped sync message
|
|
||||||
|
|
||||||
rsync.tc1 = now_ns;
|
|
||||||
rsync.ts1 = tsync.ts1;
|
|
||||||
|
|
||||||
mavlink_msg_timesync_send_struct(_mavlink->get_channel(), &rsync);
|
|
||||||
|
|
||||||
return;
|
|
||||||
|
|
||||||
} else if (tsync.tc1 > 0) {
|
|
||||||
|
|
||||||
int64_t offset_ns = (int64_t)(tsync.ts1 + now_ns - tsync.tc1 * 2) / 2 ;
|
|
||||||
int64_t dt = _time_offset - offset_ns;
|
|
||||||
|
|
||||||
if (dt > 10000000LL || dt < -10000000LL) { // 10 millisecond skew
|
|
||||||
_time_offset = offset_ns;
|
|
||||||
|
|
||||||
// Provide a warning only if not syncing initially
|
|
||||||
if (_time_offset != 0) {
|
|
||||||
PX4_ERR("[timesync] Hard setting offset.");
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
smooth_time_offset(offset_ns);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tsync_offset.offset_ns = _time_offset ;
|
|
||||||
|
|
||||||
if (_time_offset_pub == nullptr) {
|
|
||||||
_time_offset_pub = orb_advertise(ORB_ID(time_offset), &tsync_offset);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
orb_publish(ORB_ID(time_offset), _time_offset_pub, &tsync_offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
MavlinkReceiver::handle_message_hil_sensor(mavlink_message_t *msg)
|
MavlinkReceiver::handle_message_hil_sensor(mavlink_message_t *msg)
|
||||||
{
|
{
|
||||||
|
@ -2074,7 +1990,7 @@ void MavlinkReceiver::handle_message_landing_target(mavlink_message_t *msg)
|
||||||
|
|
||||||
if (landing_target.position_valid && landing_target.frame == MAV_FRAME_LOCAL_NED) {
|
if (landing_target.position_valid && landing_target.frame == MAV_FRAME_LOCAL_NED) {
|
||||||
landing_target_pose_s landing_target_pose = {};
|
landing_target_pose_s landing_target_pose = {};
|
||||||
landing_target_pose.timestamp = sync_stamp(landing_target.time_usec);
|
landing_target_pose.timestamp = _mavlink_timesync.sync_stamp(landing_target.time_usec);
|
||||||
landing_target_pose.abs_pos_valid = true;
|
landing_target_pose.abs_pos_valid = true;
|
||||||
landing_target_pose.x_abs = landing_target.x;
|
landing_target_pose.x_abs = landing_target.x;
|
||||||
landing_target_pose.y_abs = landing_target.y;
|
landing_target_pose.y_abs = landing_target.y;
|
||||||
|
@ -2547,6 +2463,9 @@ MavlinkReceiver::receive_thread(void *arg)
|
||||||
/* handle packet with log component */
|
/* handle packet with log component */
|
||||||
_mavlink_log_handler.handle_message(&msg);
|
_mavlink_log_handler.handle_message(&msg);
|
||||||
|
|
||||||
|
/* handle packet with timesync component */
|
||||||
|
_mavlink_timesync.handle_message(&msg);
|
||||||
|
|
||||||
/* handle packet with parent object */
|
/* handle packet with parent object */
|
||||||
_mavlink->handle_message(&msg);
|
_mavlink->handle_message(&msg);
|
||||||
}
|
}
|
||||||
|
@ -2587,28 +2506,6 @@ void MavlinkReceiver::print_status()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t MavlinkReceiver::sync_stamp(uint64_t usec)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (_time_offset != 0) {
|
|
||||||
return usec + (_time_offset / 1000) ;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
return hrt_absolute_time();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void MavlinkReceiver::smooth_time_offset(int64_t offset_ns)
|
|
||||||
{
|
|
||||||
/* The closer alpha is to 1.0, the faster the moving
|
|
||||||
* average updates in response to new offset samples.
|
|
||||||
*/
|
|
||||||
|
|
||||||
_time_offset = (_time_offset_avg_alpha * offset_ns) + (1.0 - _time_offset_avg_alpha) * _time_offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void *MavlinkReceiver::start_helper(void *context)
|
void *MavlinkReceiver::start_helper(void *context)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
|
@ -71,7 +71,6 @@
|
||||||
#include <uORB/topics/debug_vect.h>
|
#include <uORB/topics/debug_vect.h>
|
||||||
#include <uORB/topics/airspeed.h>
|
#include <uORB/topics/airspeed.h>
|
||||||
#include <uORB/topics/battery_status.h>
|
#include <uORB/topics/battery_status.h>
|
||||||
#include <uORB/topics/time_offset.h>
|
|
||||||
#include <uORB/topics/distance_sensor.h>
|
#include <uORB/topics/distance_sensor.h>
|
||||||
#include <uORB/topics/follow_target.h>
|
#include <uORB/topics/follow_target.h>
|
||||||
#include <uORB/topics/landing_target_pose.h>
|
#include <uORB/topics/landing_target_pose.h>
|
||||||
|
@ -84,8 +83,7 @@
|
||||||
#include "mavlink_parameters.h"
|
#include "mavlink_parameters.h"
|
||||||
#include "mavlink_ftp.h"
|
#include "mavlink_ftp.h"
|
||||||
#include "mavlink_log_handler.h"
|
#include "mavlink_log_handler.h"
|
||||||
|
#include "mavlink_timesync.h"
|
||||||
#define PX4_EPOCH_SECS 1234567890ULL
|
|
||||||
|
|
||||||
class Mavlink;
|
class Mavlink;
|
||||||
|
|
||||||
|
@ -143,8 +141,6 @@ private:
|
||||||
void handle_message_rc_channels_override(mavlink_message_t *msg);
|
void handle_message_rc_channels_override(mavlink_message_t *msg);
|
||||||
void handle_message_heartbeat(mavlink_message_t *msg);
|
void handle_message_heartbeat(mavlink_message_t *msg);
|
||||||
void handle_message_ping(mavlink_message_t *msg);
|
void handle_message_ping(mavlink_message_t *msg);
|
||||||
void handle_message_system_time(mavlink_message_t *msg);
|
|
||||||
void handle_message_timesync(mavlink_message_t *msg);
|
|
||||||
void handle_message_hil_sensor(mavlink_message_t *msg);
|
void handle_message_hil_sensor(mavlink_message_t *msg);
|
||||||
void handle_message_hil_gps(mavlink_message_t *msg);
|
void handle_message_hil_gps(mavlink_message_t *msg);
|
||||||
void handle_message_hil_state_quaternion(mavlink_message_t *msg);
|
void handle_message_hil_state_quaternion(mavlink_message_t *msg);
|
||||||
|
@ -178,17 +174,6 @@ private:
|
||||||
int set_message_interval(int msgId, float interval, int data_rate = -1);
|
int set_message_interval(int msgId, float interval, int data_rate = -1);
|
||||||
void get_message_interval(int msgId);
|
void get_message_interval(int msgId);
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert remote timestamp to local hrt time (usec)
|
|
||||||
* Use timesync if available, monotonic boot time otherwise
|
|
||||||
*/
|
|
||||||
uint64_t sync_stamp(uint64_t usec);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exponential moving average filter to smooth time offset
|
|
||||||
*/
|
|
||||||
void smooth_time_offset(int64_t offset_ns);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decode a switch position from a bitfield
|
* Decode a switch position from a bitfield
|
||||||
*/
|
*/
|
||||||
|
@ -209,6 +194,7 @@ private:
|
||||||
MavlinkParametersManager _parameters_manager;
|
MavlinkParametersManager _parameters_manager;
|
||||||
MavlinkFTP _mavlink_ftp;
|
MavlinkFTP _mavlink_ftp;
|
||||||
MavlinkLogHandler _mavlink_log_handler;
|
MavlinkLogHandler _mavlink_log_handler;
|
||||||
|
MavlinkTimesync _mavlink_timesync;
|
||||||
|
|
||||||
mavlink_status_t _status; ///< receiver status, used for mavlink_parse_char()
|
mavlink_status_t _status; ///< receiver status, used for mavlink_parse_char()
|
||||||
struct vehicle_local_position_s _hil_local_pos;
|
struct vehicle_local_position_s _hil_local_pos;
|
||||||
|
@ -242,7 +228,6 @@ private:
|
||||||
orb_advert_t _manual_pub;
|
orb_advert_t _manual_pub;
|
||||||
orb_advert_t _obstacle_distance_pub;
|
orb_advert_t _obstacle_distance_pub;
|
||||||
orb_advert_t _land_detector_pub;
|
orb_advert_t _land_detector_pub;
|
||||||
orb_advert_t _time_offset_pub;
|
|
||||||
orb_advert_t _follow_target_pub;
|
orb_advert_t _follow_target_pub;
|
||||||
orb_advert_t _landing_target_pose_pub;
|
orb_advert_t _landing_target_pose_pub;
|
||||||
orb_advert_t _transponder_report_pub;
|
orb_advert_t _transponder_report_pub;
|
||||||
|
@ -262,8 +247,6 @@ private:
|
||||||
float _hil_local_alt0;
|
float _hil_local_alt0;
|
||||||
struct map_projection_reference_s _hil_local_proj_ref;
|
struct map_projection_reference_s _hil_local_proj_ref;
|
||||||
struct offboard_control_mode_s _offboard_control_mode;
|
struct offboard_control_mode_s _offboard_control_mode;
|
||||||
double _time_offset_avg_alpha;
|
|
||||||
int64_t _time_offset;
|
|
||||||
int _orb_class_instance;
|
int _orb_class_instance;
|
||||||
|
|
||||||
static constexpr unsigned MOM_SWITCH_COUNT = 8;
|
static constexpr unsigned MOM_SWITCH_COUNT = 8;
|
||||||
|
|
|
@ -0,0 +1,256 @@
|
||||||
|
/****************************************************************************
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 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 mavlink_timesync.cpp
|
||||||
|
* Mavlink timesync implementation.
|
||||||
|
*
|
||||||
|
* @author Mohammed Kabir <mhkabir98@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "mavlink_timesync.h"
|
||||||
|
#include "mavlink_main.h"
|
||||||
|
|
||||||
|
MavlinkTimesync::MavlinkTimesync(Mavlink *mavlink) :
|
||||||
|
_timesync_status_pub(nullptr),
|
||||||
|
_sequence(0),
|
||||||
|
_time_offset(0.0),
|
||||||
|
_time_skew(0.0),
|
||||||
|
_filter_alpha(ALPHA_GAIN_INITIAL),
|
||||||
|
_filter_beta(BETA_GAIN_INITIAL),
|
||||||
|
_high_deviation_count(0),
|
||||||
|
_high_rtt_count(0),
|
||||||
|
_mavlink(mavlink)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
MavlinkTimesync::~MavlinkTimesync()
|
||||||
|
{
|
||||||
|
if (_timesync_status_pub) {
|
||||||
|
orb_unadvertise(_timesync_status_pub);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MavlinkTimesync::handle_message(const mavlink_message_t *msg)
|
||||||
|
{
|
||||||
|
switch (msg->msgid) {
|
||||||
|
case MAVLINK_MSG_ID_TIMESYNC: {
|
||||||
|
|
||||||
|
mavlink_timesync_t tsync = {};
|
||||||
|
mavlink_msg_timesync_decode(msg, &tsync);
|
||||||
|
|
||||||
|
const uint64_t now = hrt_absolute_time();
|
||||||
|
|
||||||
|
if (tsync.tc1 == 0) { // Message originating from remote system, timestamp and return it
|
||||||
|
|
||||||
|
mavlink_timesync_t rsync;
|
||||||
|
|
||||||
|
rsync.tc1 = now * 1000ULL;
|
||||||
|
rsync.ts1 = tsync.ts1;
|
||||||
|
|
||||||
|
mavlink_msg_timesync_send_struct(_mavlink->get_channel(), &rsync);
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
} else if (tsync.tc1 > 0) { // Message originating from this system, compute time offset from it
|
||||||
|
|
||||||
|
// Calculate time offset between this system and the remote system, assuming RTT for
|
||||||
|
// the timesync packet is roughly equal both ways.
|
||||||
|
int64_t offset_us = (int64_t)((tsync.ts1 / 1000ULL) + now - (tsync.tc1 / 1000ULL) * 2) / 2 ;
|
||||||
|
|
||||||
|
// Calculate the round trip time (RTT) it took the timesync packet to bounce back to us from remote system
|
||||||
|
uint64_t rtt_us = now - (tsync.ts1 / 1000ULL);
|
||||||
|
|
||||||
|
// Calculate the difference of this sample from the current estimate
|
||||||
|
uint64_t deviation = llabs((int64_t)_time_offset - offset_us);
|
||||||
|
|
||||||
|
if (rtt_us < MAX_RTT_SAMPLE) { // Only use samples with low RTT
|
||||||
|
|
||||||
|
if (sync_converged() && (deviation > MAX_DEVIATION_SAMPLE)) {
|
||||||
|
|
||||||
|
// Increment the counter if we have a good estimate and are getting samples far from the estimate
|
||||||
|
_high_deviation_count++;
|
||||||
|
|
||||||
|
// We reset the filter if we received 5 consecutive samples which violate our present estimate.
|
||||||
|
// This is most likely due to a time jump on the offboard system.
|
||||||
|
if (_high_deviation_count > MAX_CONSECUTIVE_HIGH_DEVIATION) {
|
||||||
|
PX4_ERR("[timesync] Time jump detected. Resetting time synchroniser.");
|
||||||
|
// Reset the filter
|
||||||
|
reset_filter();
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// Filter gain scheduling
|
||||||
|
if (!sync_converged()) {
|
||||||
|
// Interpolate with a sigmoid function
|
||||||
|
float progress = ((float)_sequence) / CONVERGENCE_WINDOW;
|
||||||
|
float p = 1.0f - expf(0.5f * (1.0f - 1.0f / (1.0f - progress)));
|
||||||
|
_filter_alpha = p * (float)ALPHA_GAIN_FINAL + (1.0f - p) * (float)ALPHA_GAIN_INITIAL;
|
||||||
|
_filter_beta = p * (float)BETA_GAIN_FINAL + (1.0f - p) * (float)BETA_GAIN_INITIAL;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
_filter_alpha = ALPHA_GAIN_FINAL;
|
||||||
|
_filter_beta = BETA_GAIN_FINAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform filter update
|
||||||
|
add_sample(offset_us);
|
||||||
|
|
||||||
|
// Increment sequence counter after filter update
|
||||||
|
_sequence++;
|
||||||
|
|
||||||
|
// Reset high deviation count after filter update
|
||||||
|
_high_deviation_count = 0;
|
||||||
|
|
||||||
|
// Reset high RTT count after filter update
|
||||||
|
_high_rtt_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Increment counter if round trip time is too high for accurate timesync
|
||||||
|
_high_rtt_count++;
|
||||||
|
|
||||||
|
if (_high_rtt_count > MAX_CONSECUTIVE_HIGH_RTT) {
|
||||||
|
PX4_WARN("[timesync] RTT too high for timesync: %llu ms", rtt_us / 1000ULL);
|
||||||
|
// Reset counter to rate-limit warnings
|
||||||
|
_high_rtt_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Publish status message
|
||||||
|
struct timesync_status_s tsync_status = {};
|
||||||
|
|
||||||
|
tsync_status.timestamp = hrt_absolute_time();
|
||||||
|
tsync_status.remote_timestamp = tsync.tc1 / 1000ULL;
|
||||||
|
tsync_status.observed_offset = offset_us;
|
||||||
|
tsync_status.estimated_offset = (int64_t)_time_offset;
|
||||||
|
tsync_status.round_trip_time = rtt_us;
|
||||||
|
|
||||||
|
if (_timesync_status_pub == nullptr) {
|
||||||
|
int instance;
|
||||||
|
_timesync_status_pub = orb_advertise_multi(ORB_ID(timesync_status), &tsync_status, &instance, ORB_PRIO_DEFAULT);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
orb_publish(ORB_ID(timesync_status), _timesync_status_pub, &tsync_status);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case MAVLINK_MSG_ID_SYSTEM_TIME: {
|
||||||
|
|
||||||
|
mavlink_system_time_t time;
|
||||||
|
mavlink_msg_system_time_decode(msg, &time);
|
||||||
|
|
||||||
|
timespec tv = {};
|
||||||
|
px4_clock_gettime(CLOCK_REALTIME, &tv);
|
||||||
|
|
||||||
|
// date -d @1234567890: Sat Feb 14 02:31:30 MSK 2009
|
||||||
|
bool onb_unix_valid = (unsigned long long)tv.tv_sec > PX4_EPOCH_SECS;
|
||||||
|
bool ofb_unix_valid = time.time_unix_usec > PX4_EPOCH_SECS * 1000ULL;
|
||||||
|
|
||||||
|
if (!onb_unix_valid && ofb_unix_valid) {
|
||||||
|
tv.tv_sec = time.time_unix_usec / 1000000ULL;
|
||||||
|
tv.tv_nsec = (time.time_unix_usec % 1000000ULL) * 1000ULL;
|
||||||
|
|
||||||
|
if (px4_clock_settime(CLOCK_REALTIME, &tv)) {
|
||||||
|
PX4_ERR("[timesync] Failed setting realtime clock");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t
|
||||||
|
MavlinkTimesync::sync_stamp(uint64_t usec)
|
||||||
|
{
|
||||||
|
// Only return synchronised stamp if we have converged to a good value
|
||||||
|
if (sync_converged()) {
|
||||||
|
return usec + (int64_t)_time_offset;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return hrt_absolute_time();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
MavlinkTimesync::sync_converged()
|
||||||
|
{
|
||||||
|
return _sequence >= CONVERGENCE_WINDOW;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MavlinkTimesync::add_sample(int64_t offset_us)
|
||||||
|
{
|
||||||
|
/* Online exponential smoothing filter. The derivative of the estimate is also
|
||||||
|
* estimated in order to produce an estimate without steady state lag:
|
||||||
|
* https://en.wikipedia.org/wiki/Exponential_smoothing#Double_exponential_smoothing
|
||||||
|
*/
|
||||||
|
|
||||||
|
double time_offset_prev = _time_offset;
|
||||||
|
|
||||||
|
if (_sequence == 0) { // First offset sample
|
||||||
|
_time_offset = offset_us;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Update the clock offset estimate
|
||||||
|
_time_offset = _filter_alpha * offset_us + (1.0 - _filter_alpha) * (_time_offset + _time_skew);
|
||||||
|
|
||||||
|
// Update the clock skew estimate
|
||||||
|
_time_skew = _filter_beta * (_time_offset - time_offset_prev) + (1.0 - _filter_beta) * _time_skew;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MavlinkTimesync::reset_filter()
|
||||||
|
{
|
||||||
|
// Do a full reset of all statistics and parameters
|
||||||
|
_sequence = 0;
|
||||||
|
_time_offset = 0.0;
|
||||||
|
_time_skew = 0.0;
|
||||||
|
_filter_alpha = ALPHA_GAIN_INITIAL;
|
||||||
|
_filter_beta = BETA_GAIN_INITIAL;
|
||||||
|
_high_deviation_count = 0;
|
||||||
|
_high_rtt_count = 0;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,150 @@
|
||||||
|
/****************************************************************************
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 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 mavlink_timesync.h
|
||||||
|
* Mavlink time synchroniser definition.
|
||||||
|
*
|
||||||
|
* @author Mohammed Kabir <mhkabir98@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "mavlink_bridge_header.h"
|
||||||
|
|
||||||
|
#include <uORB/uORB.h>
|
||||||
|
#include <uORB/topics/timesync_status.h>
|
||||||
|
|
||||||
|
#include <drivers/drv_hrt.h>
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include <float.h>
|
||||||
|
|
||||||
|
#define PX4_EPOCH_SECS 1234567890ULL
|
||||||
|
|
||||||
|
// Filter gains
|
||||||
|
//
|
||||||
|
// Alpha : Used to smooth the overall clock offset estimate. Smaller values will lead
|
||||||
|
// to a smoother estimate, but track time drift more slowly, introducing a bias
|
||||||
|
// in the estimate. Larger values will cause low-amplitude oscillations.
|
||||||
|
//
|
||||||
|
// Beta : Used to smooth the clock skew estimate. Smaller values will lead to a
|
||||||
|
// tighter estimation of the skew (derivative), but will negatively affect how fast the
|
||||||
|
// filter reacts to clock skewing (e.g cause by temperature changes to the oscillator).
|
||||||
|
// Larger values will cause large-amplitude oscillations.
|
||||||
|
#define ALPHA_GAIN_INITIAL 0.05
|
||||||
|
#define BETA_GAIN_INITIAL 0.05
|
||||||
|
#define ALPHA_GAIN_FINAL 0.003
|
||||||
|
#define BETA_GAIN_FINAL 0.003
|
||||||
|
|
||||||
|
// Filter gain scheduling
|
||||||
|
//
|
||||||
|
// The filter interpolates between the INITIAL and FINAL gains while the number of
|
||||||
|
// exhanged timesync packets is less than CONVERGENCE_WINDOW. A lower value will
|
||||||
|
// allow the timesync to converge faster, but with potentially less accurate initial
|
||||||
|
// offset and skew estimates.
|
||||||
|
#define CONVERGENCE_WINDOW 500
|
||||||
|
|
||||||
|
// Outlier rejection and filter reset
|
||||||
|
//
|
||||||
|
// Samples with round-trip time higher than MAX_RTT_SAMPLE are not used to update the filter.
|
||||||
|
// More than MAX_CONSECUTIVE_HIGH_RTT number of such events in a row will throw a warning
|
||||||
|
// but not reset the filter.
|
||||||
|
// Samples whose calculated clock offset is more than MAX_DEVIATION_SAMPLE off from the current
|
||||||
|
// estimate are not used to update the filter. More than MAX_CONSECUTIVE_HIGH_DEVIATION number
|
||||||
|
// of such events in a row will reset the filter. This usually happens only due to a time jump
|
||||||
|
// on the remote system.
|
||||||
|
// TODO : automatically determine these using ping statistics?
|
||||||
|
#define MAX_RTT_SAMPLE 10000ULL // 10ms
|
||||||
|
#define MAX_DEVIATION_SAMPLE 100000ULL // 100ms
|
||||||
|
#define MAX_CONSECUTIVE_HIGH_RTT 5
|
||||||
|
#define MAX_CONSECUTIVE_HIGH_DEVIATION 5
|
||||||
|
|
||||||
|
class Mavlink;
|
||||||
|
|
||||||
|
class MavlinkTimesync
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit MavlinkTimesync(Mavlink *mavlink);
|
||||||
|
~MavlinkTimesync();
|
||||||
|
|
||||||
|
void handle_message(const mavlink_message_t *msg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert remote timestamp to local hrt time (usec)
|
||||||
|
* Use synchronised time if available, monotonic boot time otherwise
|
||||||
|
*/
|
||||||
|
uint64_t sync_stamp(uint64_t usec);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
/* do not allow top copying this class */
|
||||||
|
MavlinkTimesync(MavlinkTimesync &);
|
||||||
|
MavlinkTimesync &operator = (const MavlinkTimesync &);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Online exponential filter to smooth time offset
|
||||||
|
*/
|
||||||
|
void add_sample(int64_t offset_us);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if the timesync algorithm converged to a good estimate,
|
||||||
|
* return false otherwise
|
||||||
|
*/
|
||||||
|
bool sync_converged();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the exponential filter and its states
|
||||||
|
*/
|
||||||
|
void reset_filter();
|
||||||
|
|
||||||
|
orb_advert_t _timesync_status_pub;
|
||||||
|
|
||||||
|
uint32_t _sequence;
|
||||||
|
|
||||||
|
// Timesync statistics
|
||||||
|
double _time_offset;
|
||||||
|
double _time_skew;
|
||||||
|
|
||||||
|
// Filter parameters
|
||||||
|
double _filter_alpha;
|
||||||
|
double _filter_beta;
|
||||||
|
|
||||||
|
// Outlier rejection and filter reset
|
||||||
|
uint32_t _high_deviation_count;
|
||||||
|
uint32_t _high_rtt_count;
|
||||||
|
|
||||||
|
Mavlink *_mavlink;
|
||||||
|
};
|
Loading…
Reference in New Issue