Skip to content

Commit 67db04c

Browse files
committed
feat(drivers/uavcan): use GNSS PVT timestamp for EKF time alignment
Previously the DroneCAN GNSS driver left sensor_gps.timestamp_sample at 0, which caused VehicleGPSPosition to fall back to timestamp - SENS_GPS_DELAY (a fixed 110 ms guess). The actual PVT solution timestamp from Fix2.gnss_timestamp was only consumed for system clock setting and the PPS path. Map the UTC PVT timestamp to HRT directly using an asymmetric filter on (UTC_PVT - HRT_recv): snap up to a new max immediately (tracks low-latency observations and growing UTC-HRT delta), and decay slowly between observations at 100 us/s (~100 ppm) so the offset can also track a shrinking delta when HRT runs faster than UTC. Without the decay, long-flight clock drift in the HRT-faster-than-UTC direction would accumulate unbounded bias. Clamp the resulting timestamp_sample to strictly less than timestamp so that VehicleGPSPosition does not mistake a snap-up event for an unset value. PpsTimeSync still overrides this when PPS hardware is wired. Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
1 parent 9ca5cf3 commit 67db04c

2 files changed

Lines changed: 48 additions & 0 deletions

File tree

src/drivers/uavcan/sensors/gnss.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,46 @@ void UavcanGnssBridge::process_fixx(const uavcan::ReceivedDataStructure<FixType>
505505
break;
506506
}
507507

508+
// Map the GNSS PVT UTC timestamp to HRT so the EKF can align GPS with IMU. cand_offset =
509+
// UTC_PVT - HRT_recv = delta - latency. Snap up immediately to track lower latency or
510+
// growing delta (HRT slower than UTC); decay slowly so the offset can also follow a
511+
// shrinking delta (HRT faster than UTC) without accumulating bias over long flights.
512+
// PpsTimeSync in VehicleGPSPosition overrides this when PPS hardware is wired.
513+
if (sensor_gps.time_utc_usec != 0) {
514+
const int64_t cand_offset = (int64_t)sensor_gps.time_utc_usec - (int64_t)sensor_gps.timestamp;
515+
516+
if (!_utc_minus_hrt_offset_valid) {
517+
_utc_minus_hrt_offset_us = cand_offset;
518+
_utc_minus_hrt_offset_valid = true;
519+
520+
} else {
521+
const hrt_abstime dt_us = (sensor_gps.timestamp > _utc_minus_hrt_offset_last_update)
522+
? (sensor_gps.timestamp - _utc_minus_hrt_offset_last_update) : 0;
523+
const int64_t decay_us = static_cast<int64_t>(dt_us) * kMaxClockDriftPpm / 1'000'000;
524+
_utc_minus_hrt_offset_us -= decay_us;
525+
526+
if (cand_offset > _utc_minus_hrt_offset_us) {
527+
_utc_minus_hrt_offset_us = cand_offset;
528+
}
529+
}
530+
531+
_utc_minus_hrt_offset_last_update = sensor_gps.timestamp;
532+
533+
int64_t pvt_hrt_us = (int64_t)sensor_gps.time_utc_usec - _utc_minus_hrt_offset_us;
534+
535+
// On snap-up events pvt_hrt_us == timestamp, which VehicleGPSPosition mistakes for
536+
// "driver didn't set timestamp_sample" and replaces with SENS_GPS_DELAY (110 ms back).
537+
// That intermittent backward jump trips the EKF monotonicity gate and drops samples.
538+
// Bias by 1 us so the value is always strictly less than timestamp.
539+
if (pvt_hrt_us >= (int64_t)sensor_gps.timestamp) {
540+
pvt_hrt_us = (int64_t)sensor_gps.timestamp - 1;
541+
}
542+
543+
if (pvt_hrt_us > 0) {
544+
sensor_gps.timestamp_sample = (uint64_t)pvt_hrt_us;
545+
}
546+
}
547+
508548
// If we haven't already done so, set the system clock using GPS data
509549
if (sensor_gps.time_utc_usec != 0 && (fix_type >= sensor_gps_s::FIX_TYPE_2D) && !_system_clock_set) {
510550
timespec ts{};

src/drivers/uavcan/sensors/gnss.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,14 @@ class UavcanGnssBridge : public UavcanSensorBridgeBase
151151

152152
bool _system_clock_set{false}; ///< Have we set the system clock at least once from GNSS data?
153153

154+
// Asymmetric filter on (UTC_PVT - HRT_recv) used to map GNSS PVT UTC to HRT for EKF
155+
// alignment. Snap up to a new max (low-latency or UTC>HRT drift), decay slowly between
156+
// observations at the worst-case clock-drift rate so the offset can also track UTC<HRT.
157+
static constexpr int64_t kMaxClockDriftPpm{100}; // ~100 us/s; covers cheap-crystal drift
158+
int64_t _utc_minus_hrt_offset_us{0};
159+
hrt_abstime _utc_minus_hrt_offset_last_update{0};
160+
bool _utc_minus_hrt_offset_valid{false};
161+
154162
bool *_channel_using_fix2; ///< Flag for whether each channel is using Fix2 or Fix msg
155163

156164
bool _publish_rtcm_stream{false};

0 commit comments

Comments
 (0)