Skip to content

feat(radio): Automatic clock drift compensation - #7763

Open
3djc wants to merge 2 commits into
mainfrom
3djc/rtc-clock
Open

feat(radio): Automatic clock drift compensation#7763
3djc wants to merge 2 commits into
mainfrom
3djc/rtc-clock

Conversation

@3djc

@3djc 3djc commented Sep 6, 2026

Copy link
Copy Markdown
Collaborator

The clock that learns

Your radio now measures how fast its own clock runs and quietly trims itself.
There is nothing to configure and nothing to switch on. This note explains what
it needs from you, and what it will never be able to do.

The short version

  1. Set the clock properly — to the second, from a phone or a computer. Do it
    in one visit to the screen, then leave.
  2. Leave it alone for a month. Don't nudge it, don't check it. The waiting is
    what makes the measurement worth anything.
  3. Set it properly again. That second correction is the first one that
    teaches the radio anything. Repeat a few times.

Correcting the clock weekly, or nudging it for reasons other than accuracy,
actively works against this.

What it does

The quartz crystal that keeps time in your transmitter is trimmed at the factory
to a tolerance, not to perfection. A typical one runs about 20 ppm off — roughly
a minute a month — and whether it gains or loses is different for every unit.

The clock hardware can be told to add or remove a few ticks to compensate, and
that trim survives being switched off. What's new is that the radio works out the
right value by itself: it notes when you last set the clock, and the next time you
correct it, it divides the error by the time that passed. That's the drift rate.
It trims by that much and starts the measurement again.

Your first correction teaches it nothing

This is the part that surprises people. The radio needs two points to measure a
rate, so the very first time you set the clock it has nothing to compare against —
it makes a note of the date and time and moves on. No trim is applied. It looks
like the feature isn't working.

When What happens
Day 0 — first correction Reference noted. Nothing learned.
≈ 1 month — second correction First real trim. Removes most of the error.
≈ 2 months — third correction Refines what's left over.
≈ 4 months Settled. Further corrections change little.

The trim closes in on the true drift rate rather than jumping to it, deliberately:
it under-corrects slightly each time so that a sloppy button press can't throw it
off.

The same thing happens after the clock's backup battery goes flat or is removed.
That memory holds both the trim and the reference date, so losing it puts you back
at day zero — silently.

How long you waited decides how much it can learn

The radio ignores any error smaller than 4 seconds, on the grounds that below
that it can't tell real drift from you pressing the button a beat late. Four
seconds is the same threshold whatever the interval — but what it means changes
completely:

Since last correction Smallest drift it can see Verdict
1 day 4 s a day — about 2 minutes a month Only a broken clock
1 week 4 s a week — about 17 s a month Coarse
1 month 4 s a month The sweet spot
1 year 4 s a year — under 1 s a month Finer than the hardware

Corrections made less than a day apart are discarded outright.

So a correction made the day after the last one is close to worthless — it's blind
to anything under two minutes a month, while the crystal you're trying to fix is
out by about one. Monthly is where this starts working.

Expect seconds a month, not zero

Drift
Typical radio, untrimmed ≈ 60 s/month
After a few corrections ≈ 4 s/month
Hardware limit ≈ 2 s/month

The finest adjustment the clock hardware can make is worth about 2.5 seconds a
month, so that is the floor. No amount of patience gets you below it.

Temperature moves the target

Watch crystals of this type run fastest at around room temperature and slow down
when it's hotter or colder — following a curve, not a straight line. The effect
is not small print; it is the same size as the error being corrected.

Temperature Rate error Equivalent
−10 °C −41.7 ppm −108 s/month
0 °C −21.3 ppm −55 s/month
10 °C −7.7 ppm −20 s/month
20 °C −0.9 ppm −2 s/month
25 °C 0 ppm fastest here
30 °C −0.9 ppm −2 s/month
40 °C −7.7 ppm −20 s/month
50 °C −21.3 ppm −55 s/month

Shape of a 32.768 kHz tuning-fork crystal, roughly −0.034 ppm per °C² about its
turnover point. Individual crystals vary, but the curvature is a property of the
cut.

The trim your radio settles on is a single fixed number, fitted to whatever
temperatures it happened to experience while it was learning. A transmitter that
calibrated itself over a summer in a warm house will drift again on a winter
flying field
, and there is no way around that without a temperature sensor and a
lookup curve, which the radio does not have.

Practically: if your radio lives somewhere with a stable temperature you'll get
the full benefit. If it commutes between a cold car boot and a warm workshop,
expect a few seconds a month of wander no matter how patient you are.

Five ways to undo the learning

The radio assumes every change you make to the clock is you correcting drift.
Mostly that's true. Where it isn't:

What happens
Nudging the clock for some other reason Mislearns. Shift the time by a minute after a month for any reason that isn't accuracy, and the radio concludes your crystal is bad and trims the wrong way. It recovers over following corrections, but that cycle is wasted.
Daylight saving on top of existing drift Mislearns. A clean one-hour change is recognised and ignored, as is any whole timezone shift. One hour plus a couple of minutes of accumulated drift is not always spotted, and can throw the trim off noticeably. Set the exact correct time in one go.
A wildly wrong entry Ignored. If the correction implies your clock is out by more than about nine minutes a month, the radio assumes you moved it deliberately and discards the measurement. Useful protection — though it also means a genuinely dying crystal never gets compensated.
Lingering on the Date & Time screen Mislearns. Edits within about five minutes of each other count as one correction, which is right when you're stepping through hours, minutes and seconds. But change a field, get distracted for a few minutes, then change another before leaving — and the measurement is taken against a stale reading. Set it in one go and leave the screen.
Starting a correction and backing out Restarts the clock. Any change to the clock resets the measurement window. Open the screen, nudge something, back out — and the month of drift you were about to capture is gone. You start counting from now.

If you fly with GPS, it does all of this for you

When GPS telemetry is present the radio already resets its clock automatically
once the error passes about 20 seconds. Those automatic syncs feed the same
learning process, so you get the benefit without ever opening the Date & Time
screen.

It also converges rather elegantly: as the trim improves, the clock takes longer
to drift 20 seconds, so the syncs spread further apart — and every measurement
over a longer gap is a more accurate one.

There is nothing to look at

From your side the feature is entirely invisible — the clock simply gets better,
or it doesn't, and the reasons it might not are all above.


Figures are typical values for a 32.768 kHz tuning-fork crystal; any individual
radio will differ.

This closes #7759, just in a more user friendly way

@3djc 3djc changed the title feat(radio): the clock that learns feat(radio): Automatic clock drift compensation Sep 6, 2026
@3djc 3djc added enhancement ✨ New feature or request backport/2.12 To be backported to a 2.12 release also. labels Sep 6, 2026
@Taxom

Taxom commented Sep 8, 2026

Copy link
Copy Markdown

Thanks for implementing this — this looks like a very nice solution to #7759.

One concern: the current code uses RTC_CALIB_MAX_PPM = 200.

My TX16S MK3, which prompted #7759, has a measured powered-off RTC drift of approximately +25 s/day, i.e. about +289 ppm. I repeated this against the earlier observation of roughly +8 minutes over ~20 days, and the figures are consistent.

As I understand the current logic, my radio would therefore be rejected by:

if (absError * 1000000 > (int64_t)elapsed * RTC_CALIB_MAX_PPM) return;

even though the STM32 smooth-calibration range appears large enough to compensate this amount of drift.

Would it make sense to increase the sanity limit, perhaps closer to the actual hardware calibration range, or otherwise distinguish a genuinely large but stable crystal error from a deliberate clock change?

@3djc

3djc commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator Author

The limits as they are should allow normal variation of the crystal, your values seem off limits (while technically stm32 could compensate larger ones, it should not have to).

I have engaged a discussion with Radiomaster to get a better understanding at what is happening there

@Taxom

Taxom commented Sep 8, 2026

Copy link
Copy Markdown

Thanks — that makes sense. I also agree that ~300 ppm is far beyond what I would expect from a normally operating 32.768 kHz crystal oscillator, even though the STM32 hardware is technically capable of compensating it.

I have also replied to RadioMaster support and asked them for the exact 32.768 kHz crystal part number/datasheet and the expected RTC tolerance of the TX16S MK3. I also pointed out that the RTC circuitry is located on the removable CORE_H750 module, so replacing that module should be sufficient for hardware testing.

I'll update this thread when I get their response.

@3djc

3djc commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator Author

@Taxom are you confortable using a serial terminal to connect to the radio cli ? if yes, I can make a version when you can setup your correction manually, then switch back to normal one

@Taxom

Taxom commented Sep 8, 2026

Copy link
Copy Markdown

Yes, absolutely. I already have the TX16S MK3 USB-VCP CLI working and have successfully used set rtc both from a serial terminal and from a PowerShell script.

I would be happy to test a debug build with manual RTC calibration.

Based on my measured +25 s/day drift (~+289 ppm), I assume the initial test would be around:

set rtccal -289

I can then leave the radio powered off for 24–48 hours, measure the remaining drift, and report the result.

If the calibration survives switching back to the normal firmware as you suggested, that would also be an excellent workaround while the hardware issue is being investigated.

Thank you!

@3djc

3djc commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator Author

Use this:

firmware.uf2.zip

Connect USB, set VCP-CLI

Using cli:

set rtc 2026 09 08 14 30 00 (adjust time accordingly)
set rtccal -289

Done, flash 2.12.4 back, you should see a much better (lower) drift

@3djc

3djc commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator Author

I have also created an lvgl analog clock for easier handling

https://github.com/3djc/EdgeTX-widgets-and-scripts-

@Taxom

Taxom commented Sep 8, 2026

Copy link
Copy Markdown

I put the PowerShell RTC test/sync tool here:

https://github.com/Taxom/tx16smk3-rtc-tools

It automatically finds the TX16S MK3 VCP, reads print rtc when available, measures the pre-sync offset, records the actual post-sync baseline, calculates elapsed drift and estimated ppm on later runs, and appends everything to a CSV log.

On normal release builds where print rtc is not available, it does not fail — it simply logs that RTC reading is unsupported and still performs the normal set rtc synchronization.

I’m using it now for the 24-hour test of the -288.9 ppm calibration.

Licensed GPL-2.0-or-later, so feel free to reuse or adapt anything useful from it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport/2.12 To be backported to a 2.12 release also. enhancement ✨ New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add user-adjustable RTC drift calibration / ppm correction

2 participants