I want to precisely synchronize UTC times of some UAVCAN nodes with a time master which has the GPS PPS signal connected to one IO.
Pavel wrote there is a TODO comment for that in the STM32 driver I believe, but this feature is not implemented. Yes, it is perfectly feasible and can be done relatively easily.
But I didn’t find this comment. The idea is to directly sync timer with external input ?
The comment happened to be in the Zubax GNSS firmware:
I recommend you either write your own clock driver for your application, or you add support for an optional external signal synchronization to the upstream driver (that would be amazing). If you chose to go with your own driver, you could either reimplement the entire STM32 platform driver from scratch, or you could provide your own implementation of the clock driver only (in that case just leave UAVCAN_STM32_TIMER_NUMBER undefined; see https://github.com/UAVCAN/libuavcan_stm32/blob/8c32fad90025d8466fd8ff0d856bcf9ebb68eab8/driver/src/uc_stm32_clock.cpp#L9).
You could implement the synchronization feature through a simple software phase-locked loop; something similar to what we have in the driver currently, except that the reference signal will be provided in the form of a hardware pulse train rather than periodic time adjustments.
would you use a timer capture interrupt or a simple external trigger interrupt handler ?
my idea was to just add an external input trigger into the existing stm32 driver and then call the also existing adjustutc method in the interrupt handler. you think it is not a good idea ? the only thing you’ll to set is the correct gpio with a define.
In order to take full advantage of the PPS signal you should account for the IRQ jitter. In order to do that, you should likely use a hardware timer capture input (that is unless you can guarantee a very low jitter, e.g., by using the highest priority IRQ with extremely short to nonexistent critical sections – this is rarely feasible).
Thanks. I’ll give a try with capture input. What do you of directly use the existing adjustUtc ? You’ve already implemented the sync loop. This function can be called from an interrupt handler ?
I don’t think this function can be invoked from an interrupt context, but you should be able to fix that easily. You will need a different phase locked loop because the PPS signal contains only rate information but no phase information; i.e., with PPS, you can determine the rate error of your local clock, but not the absolute time.
If I configure PPS from the GPS to 1Hz synchronized with the UTC time and set the UTC time accordingly with gps topic so I can know to which absolute time the pulse refers.
I didn’t have the bandwith to work on that and will try now.
I saw your discussion on PX4 https://github.com/PX4/Firmware/issues/2915 about using hrt timer monotonic instead of tim5. I wonder if using rtc clock with smooth calibration HW of the stm32 is not a good idea for the Utc (or system or realtime) time base ? So like on the linux implementation the used clocs will be the monotonic (in this one the rate can be changed by the way) and realtime.
It’s quite sad that we never got around to fix that problem you linked.
The current libuavcan implementation does just that: it maintains two time bases, monotonic and realtime, using one hardware timer. The existing implementation does not require hardware support for clock stretching, since this feature is implemented in software, but you can easily change that.
I am having the exact same problem with clock synchronisation from external GNSS NMEA stream and PPS signal. I see that hardware timer capture input is the way to go for best precision, but I lack skill to implement that and my already produced hardware would be pin incompatible. So I went with a simple solution and modified adjustUtc function so it could be called from ISR: https://github.com/chemicstry/libuavcan_stm32/commit/c2431c263f4bfac7655e9ac05e5f6850d8396e77
The procedure is as follows:
Ceil the received NMEA timestamp up to the nearest second (PPS signal will indicate the precise moment of this time) and store it in next_pps_timestamp_usec
Attach interrupt to PPS pin and when it fires do (ChibiOS code):
palSetLineCallback(LINE_PPS, [](void *arg){
if (next_pps_timestamp_usec) {
uavcan_stm32::clock::adjustUtcAbsoluteFromISR(next_pps_timestamp_usec);
}
}, NULL);
I know this is not ideal due to long ISR routine and IRQ jitter, but I think someone could find this useful.
Did not notice much of a difference from using external interrupts, although I did not have much interrupt load.
If this code seems bearable I could make pull request with requested changes. One thing that I don’t like is the heavy updateRatePID function called from ISR, but clock driver does not have any periodic callback or thread to do the update from.
Having looked at the code, I think it’s best to keep it outside of the upstream because it might be too domain-specific. I think we should probably link your repo from the STM32-specific docs as a generic time sync implementation example. I should probably ping @scottdixon so that he’s aware of this.
The problem with accepting things like this into UAVCAN is we don’t have the hardware test apparatuses to verify it and maintain it. This is something I hope to address in the future but wouldn’t happen until after the libuavcan v1 rewrite is complete.