time-nuts@lists.febo.com

Discussion of precise time and frequency measurement

View all threads

syncronization algorithms question

CA
Can Altineller
Thu, May 29, 2025 4:41 PM

Hello,

I make embedded sensor boards that are connected to a PC, and the sensor
board uses USB-CDC to communicate with the PC. USB-CDC means HID virtual
serial, that works at 921600 bauds, data is transmitted at 64 byte chunks,
and it does not have any of the problems of classical serial ports nor
needs to go over FTDI chip.

I have implemented a NTP like synchronization system where I calculate:

rtt = (t4_point - t1_point).count() - (t3_point - t2_point).count();
offset = ((t2_point - t1_point).count() + (t3_point - t4_point).count()) /
2;

Basically the computer sends a sync request that has t1, and the sensor
receives this at t2, and replies at t3, and the computer receives it at t4.

I transmit sync requests each 4 seconds, calculate the rtt and offset from
the computer side, and the calculated offset to the sensor, so RTC can be
trimmed.

My RTC frequency does not change, however each 64 seconds you can add or
subtract from the RTC counter at the end of the second. Effectively giving
you RTCTrimSet(32768 + OFFSET) - It is good as a frequency changing
oscillator but does the trick.

On the left column Offset, RTT, and RTC trim values of PC to sensor device.
On the right column we have values from RPI5 to another sensor device.
Units are nanoseconds. For the PC: I have them synced +-0.6ms and for the
RPI5 we have +-0.1ms. The horizontal axis, each point is measured each 4
seconds, so this is about 10 hours of data.

[image: Screenshot from 2025-05-29 19-19-50.png]

Here are my questions: Given that I calculate the offset with this method,
and I adjust the RTC by adding or removing subseconds each 64 seconds, what
kind of algorithm should I use to filter the RTC offset? or should I
directly adjust with the latest measured offset? Or even should I put a PI
algorithm? I have experimented with using a moving average filter vs direct
last measured RTC_OFFSET - and as expected even RTC_OFFSET method responds
faster avg(RTC_OFFSET) is more stable. Above graphs are both
avg(RTC_OFFSET). I am thinking that since RTC_OFFSET points to a difference
in time, theoretically it should be best to instruct the RTC to lead/lag by
that difference. But doing so causes noise. What other algorithms can be
used for these purposes? Maybe something that will calculate the trim not
based on past data, but also prediction of the next cycle of 64 secs?

What are your thoughts on this? What are your thoughts on the graph.

Any ideas / help / recommendations greatly appreciated.

Best Regards,
Can

Hello, I make embedded sensor boards that are connected to a PC, and the sensor board uses USB-CDC to communicate with the PC. USB-CDC means HID virtual serial, that works at 921600 bauds, data is transmitted at 64 byte chunks, and it does not have any of the problems of classical serial ports nor needs to go over FTDI chip. I have implemented a NTP like synchronization system where I calculate: rtt = (t4_point - t1_point).count() - (t3_point - t2_point).count(); offset = ((t2_point - t1_point).count() + (t3_point - t4_point).count()) / 2; Basically the computer sends a sync request that has t1, and the sensor receives this at t2, and replies at t3, and the computer receives it at t4. I transmit sync requests each 4 seconds, calculate the rtt and offset from the computer side, and the calculated offset to the sensor, so RTC can be trimmed. My RTC frequency does not change, however each 64 seconds you can add or subtract from the RTC counter at the end of the second. Effectively giving you RTCTrimSet(32768 + OFFSET) - It is good as a frequency changing oscillator but does the trick. On the left column Offset, RTT, and RTC trim values of PC to sensor device. On the right column we have values from RPI5 to another sensor device. Units are nanoseconds. For the PC: I have them synced +-0.6ms and for the RPI5 we have +-0.1ms. The horizontal axis, each point is measured each 4 seconds, so this is about 10 hours of data. [image: Screenshot from 2025-05-29 19-19-50.png] Here are my questions: Given that I calculate the offset with this method, and I adjust the RTC by adding or removing subseconds each 64 seconds, what kind of algorithm should I use to filter the RTC offset? or should I directly adjust with the latest measured offset? Or even should I put a PI algorithm? I have experimented with using a moving average filter vs direct last measured RTC_OFFSET - and as expected even RTC_OFFSET method responds faster avg(RTC_OFFSET) is more stable. Above graphs are both avg(RTC_OFFSET). I am thinking that since RTC_OFFSET points to a difference in time, theoretically it should be best to instruct the RTC to lead/lag by that difference. But doing so causes noise. What other algorithms can be used for these purposes? Maybe something that will calculate the trim not based on past data, but also prediction of the next cycle of 64 secs? What are your thoughts on this? What are your thoughts on the graph. Any ideas / help / recommendations greatly appreciated. Best Regards, Can
AR
Andrew Rodland
Fri, May 30, 2025 6:34 PM

It depends on how the stability of your oscillator compares to the accuracy
of the offset measurement. If the expected variance of your clock over
those 64 seconds is of the same order of magnitude as the noise in the
offset measurement, or worse, then you might as well just use the "hard
sync" approach of applying the measured offset immediately. If the
oscillator is stable over longer time periods then a PLL makes sense. A
second-order PLL is equivalent to a PI loop; the "I" term corrects for the
average frequency offset while the "P" time drives the phase offset to
zero. The more stable your oscillator, the smaller you can make the P and I
constants (over here we talk about using "longer time constants"); the
disciplined system will be dominated by the performance of the oscillator
over time periods shorter than the PLL time constants, and dominated by the
noise in your offset measurements over longer periods of time — which is
why it's not worth it to do anything fancy if your clock isn't stable
enough over the minimum adjustment interval.

I would guess that you can characterize the noise in the offset measurement
(if you haven't already) by taking batches of offset measurements much
faster than usual so that the clock doesn't have time to wander anywhere;
then if you take a long series of offset measurements with the correction
turned off
you can get a TDEV plot of the oscillator performance plus
measurement noise; and by risking a few assumptions (independent variances,
and that the measurement error is white phase noise) you can figure
TDEV^2[clock] = TDEV^2[total] - TDEV^2[meas.err.] to separate the plots and
figure out where the crossover should be.

On Fri, May 30, 2025 at 8:43 AM Can Altineller via time-nuts <
time-nuts@lists.febo.com> wrote:

Hello,

I make embedded sensor boards that are connected to a PC, and the sensor
board uses USB-CDC to communicate with the PC. USB-CDC means HID virtual
serial, that works at 921600 bauds, data is transmitted at 64 byte chunks,
and it does not have any of the problems of classical serial ports nor
needs to go over FTDI chip.

I have implemented a NTP like synchronization system where I calculate:

rtt = (t4_point - t1_point).count() - (t3_point - t2_point).count();
offset = ((t2_point - t1_point).count() + (t3_point - t4_point).count()) /
2;

Basically the computer sends a sync request that has t1, and the sensor
receives this at t2, and replies at t3, and the computer receives it at t4.

I transmit sync requests each 4 seconds, calculate the rtt and offset from
the computer side, and the calculated offset to the sensor, so RTC can be
trimmed.

My RTC frequency does not change, however each 64 seconds you can add or
subtract from the RTC counter at the end of the second. Effectively giving
you RTCTrimSet(32768 + OFFSET) - It is good as a frequency changing
oscillator but does the trick.

On the left column Offset, RTT, and RTC trim values of PC to sensor device.
On the right column we have values from RPI5 to another sensor device.
Units are nanoseconds. For the PC: I have them synced +-0.6ms and for the
RPI5 we have +-0.1ms. The horizontal axis, each point is measured each 4
seconds, so this is about 10 hours of data.

[image: Screenshot from 2025-05-29 19-19-50.png]

Here are my questions: Given that I calculate the offset with this method,
and I adjust the RTC by adding or removing subseconds each 64 seconds, what
kind of algorithm should I use to filter the RTC offset? or should I
directly adjust with the latest measured offset? Or even should I put a PI
algorithm? I have experimented with using a moving average filter vs direct
last measured RTC_OFFSET - and as expected even RTC_OFFSET method responds
faster avg(RTC_OFFSET) is more stable. Above graphs are both
avg(RTC_OFFSET). I am thinking that since RTC_OFFSET points to a difference
in time, theoretically it should be best to instruct the RTC to lead/lag by
that difference. But doing so causes noise. What other algorithms can be
used for these purposes? Maybe something that will calculate the trim not
based on past data, but also prediction of the next cycle of 64 secs?

What are your thoughts on this? What are your thoughts on the graph.

Any ideas / help / recommendations greatly appreciated.

Best Regards,
Can


time-nuts mailing list -- time-nuts@lists.febo.com
To unsubscribe send an email to time-nuts-leave@lists.febo.com

It depends on how the stability of your oscillator compares to the accuracy of the offset measurement. If the expected variance of your clock over those 64 seconds is of the same order of magnitude as the noise in the offset measurement, or worse, then you might as well just use the "hard sync" approach of applying the measured offset immediately. If the oscillator is stable over longer time periods then a PLL makes sense. A second-order PLL is equivalent to a PI loop; the "I" term corrects for the average frequency offset while the "P" time drives the phase offset to zero. The more stable your oscillator, the smaller you can make the P and I constants (over here we talk about using "longer time constants"); the disciplined system will be dominated by the performance of the oscillator over time periods shorter than the PLL time constants, and dominated by the noise in your offset measurements over longer periods of time — which is why it's not worth it to do anything fancy if your clock isn't stable enough over the minimum adjustment interval. I would guess that you can characterize the noise in the offset measurement (if you haven't already) by taking batches of offset measurements much faster than usual so that the clock doesn't have time to wander anywhere; then if you take a long series of offset measurements *with the correction turned off* you can get a TDEV plot of the oscillator performance plus measurement noise; and by risking a few assumptions (independent variances, and that the measurement error is white phase noise) you can figure TDEV^2[clock] = TDEV^2[total] - TDEV^2[meas.err.] to separate the plots and figure out where the crossover should be. On Fri, May 30, 2025 at 8:43 AM Can Altineller via time-nuts < time-nuts@lists.febo.com> wrote: > Hello, > > I make embedded sensor boards that are connected to a PC, and the sensor > board uses USB-CDC to communicate with the PC. USB-CDC means HID virtual > serial, that works at 921600 bauds, data is transmitted at 64 byte chunks, > and it does not have any of the problems of classical serial ports nor > needs to go over FTDI chip. > > I have implemented a NTP like synchronization system where I calculate: > > rtt = (t4_point - t1_point).count() - (t3_point - t2_point).count(); > offset = ((t2_point - t1_point).count() + (t3_point - t4_point).count()) / > 2; > > Basically the computer sends a sync request that has t1, and the sensor > receives this at t2, and replies at t3, and the computer receives it at t4. > > I transmit sync requests each 4 seconds, calculate the rtt and offset from > the computer side, and the calculated offset to the sensor, so RTC can be > trimmed. > > My RTC frequency does not change, however each 64 seconds you can add or > subtract from the RTC counter at the end of the second. Effectively giving > you RTCTrimSet(32768 + OFFSET) - It is good as a frequency changing > oscillator but does the trick. > > On the left column Offset, RTT, and RTC trim values of PC to sensor device. > On the right column we have values from RPI5 to another sensor device. > Units are nanoseconds. For the PC: I have them synced +-0.6ms and for the > RPI5 we have +-0.1ms. The horizontal axis, each point is measured each 4 > seconds, so this is about 10 hours of data. > > [image: Screenshot from 2025-05-29 19-19-50.png] > > Here are my questions: Given that I calculate the offset with this method, > and I adjust the RTC by adding or removing subseconds each 64 seconds, what > kind of algorithm should I use to filter the RTC offset? or should I > directly adjust with the latest measured offset? Or even should I put a PI > algorithm? I have experimented with using a moving average filter vs direct > last measured RTC_OFFSET - and as expected even RTC_OFFSET method responds > faster avg(RTC_OFFSET) is more stable. Above graphs are both > avg(RTC_OFFSET). I am thinking that since RTC_OFFSET points to a difference > in time, theoretically it should be best to instruct the RTC to lead/lag by > that difference. But doing so causes noise. What other algorithms can be > used for these purposes? Maybe something that will calculate the trim not > based on past data, but also prediction of the next cycle of 64 secs? > > What are your thoughts on this? What are your thoughts on the graph. > > Any ideas / help / recommendations greatly appreciated. > > Best Regards, > Can > _______________________________________________ > time-nuts mailing list -- time-nuts@lists.febo.com > To unsubscribe send an email to time-nuts-leave@lists.febo.com
CA
Can Altineller
Sun, Jun 8, 2025 1:49 PM

Hello,

Thank you for your explanatory answer. I made a PI, and also custom
plotting software that processes the logs, makes graphs with gnuplot, and a
web page, and a small python -m http.server to host the pages.

Well I have synchronization with in +- 3 * 1/32768 most of the time. I am
not sure if it can be made any better. The cheap crystal that I am using
skips like couple of steps each 64 seconds depending on temperature, etc.

One problem is that the system is not resistant to external changes, for
example then the ntp chrony daemon adjusts the hosts clock.

I am sending two giant png's one is the plot of my system.

This image is plot of offset, rtt, t1,t2,t3,t4 and t4-t1 vs t3-t2 - and
applied RTC trim each 64 seconds.
[image: ntp-local.png]

This graph is the chrony ntp logs, (open source software from github)
[image: ntp-echo.png]
You can see for some reason ntp was cut between 6 and 7 hrs, and my system
logs a great offset that it fails to compensate for.

I am thinking of replacing the crystal with a mems oscillator that has
0.1ppm.

Best Regards,
Can

On Sat, May 31, 2025 at 9:29 AM Andrew Rodland via time-nuts <
time-nuts@lists.febo.com> wrote:

It depends on how the stability of your oscillator compares to the accuracy
of the offset measurement. If the expected variance of your clock over
those 64 seconds is of the same order of magnitude as the noise in the
offset measurement, or worse, then you might as well just use the "hard
sync" approach of applying the measured offset immediately. If the
oscillator is stable over longer time periods then a PLL makes sense. A
second-order PLL is equivalent to a PI loop; the "I" term corrects for the
average frequency offset while the "P" time drives the phase offset to
zero. The more stable your oscillator, the smaller you can make the P and I
constants (over here we talk about using "longer time constants"); the
disciplined system will be dominated by the performance of the oscillator
over time periods shorter than the PLL time constants, and dominated by the
noise in your offset measurements over longer periods of time — which is
why it's not worth it to do anything fancy if your clock isn't stable
enough over the minimum adjustment interval.

I would guess that you can characterize the noise in the offset measurement
(if you haven't already) by taking batches of offset measurements much
faster than usual so that the clock doesn't have time to wander anywhere;
then if you take a long series of offset measurements with the correction
turned off
you can get a TDEV plot of the oscillator performance plus
measurement noise; and by risking a few assumptions (independent variances,
and that the measurement error is white phase noise) you can figure
TDEV^2[clock] = TDEV^2[total] - TDEV^2[meas.err.] to separate the plots and
figure out where the crossover should be.

On Fri, May 30, 2025 at 8:43 AM Can Altineller via time-nuts <
time-nuts@lists.febo.com> wrote:

Hello,

I make embedded sensor boards that are connected to a PC, and the sensor
board uses USB-CDC to communicate with the PC. USB-CDC means HID virtual
serial, that works at 921600 bauds, data is transmitted at 64 byte

chunks,

and it does not have any of the problems of classical serial ports nor
needs to go over FTDI chip.

I have implemented a NTP like synchronization system where I calculate:

rtt = (t4_point - t1_point).count() - (t3_point - t2_point).count();
offset = ((t2_point - t1_point).count() + (t3_point - t4_point).count())

/

2;

Basically the computer sends a sync request that has t1, and the sensor
receives this at t2, and replies at t3, and the computer receives it at

t4.

I transmit sync requests each 4 seconds, calculate the rtt and offset

from

the computer side, and the calculated offset to the sensor, so RTC can be
trimmed.

My RTC frequency does not change, however each 64 seconds you can add or
subtract from the RTC counter at the end of the second. Effectively

giving

you RTCTrimSet(32768 + OFFSET) - It is good as a frequency changing
oscillator but does the trick.

On the left column Offset, RTT, and RTC trim values of PC to sensor

device.

On the right column we have values from RPI5 to another sensor device.
Units are nanoseconds. For the PC: I have them synced +-0.6ms and for the
RPI5 we have +-0.1ms. The horizontal axis, each point is measured each 4
seconds, so this is about 10 hours of data.

[image: Screenshot from 2025-05-29 19-19-50.png]

Here are my questions: Given that I calculate the offset with this

method,

and I adjust the RTC by adding or removing subseconds each 64 seconds,

what

kind of algorithm should I use to filter the RTC offset? or should I
directly adjust with the latest measured offset? Or even should I put a

PI

algorithm? I have experimented with using a moving average filter vs

direct

last measured RTC_OFFSET - and as expected even RTC_OFFSET method

responds

faster avg(RTC_OFFSET) is more stable. Above graphs are both
avg(RTC_OFFSET). I am thinking that since RTC_OFFSET points to a

difference

in time, theoretically it should be best to instruct the RTC to lead/lag

by

that difference. But doing so causes noise. What other algorithms can be
used for these purposes? Maybe something that will calculate the trim not
based on past data, but also prediction of the next cycle of 64 secs?

What are your thoughts on this? What are your thoughts on the graph.

Any ideas / help / recommendations greatly appreciated.

Best Regards,
Can


time-nuts mailing list -- time-nuts@lists.febo.com
To unsubscribe send an email to time-nuts-leave@lists.febo.com


time-nuts mailing list -- time-nuts@lists.febo.com
To unsubscribe send an email to time-nuts-leave@lists.febo.com

Hello, Thank you for your explanatory answer. I made a PI, and also custom plotting software that processes the logs, makes graphs with gnuplot, and a web page, and a small python -m http.server to host the pages. Well I have synchronization with in +- 3 * 1/32768 most of the time. I am not sure if it can be made any better. The cheap crystal that I am using skips like couple of steps each 64 seconds depending on temperature, etc. One problem is that the system is not resistant to external changes, for example then the ntp chrony daemon adjusts the hosts clock. I am sending two giant png's one is the plot of my system. This image is plot of offset, rtt, t1,t2,t3,t4 and t4-t1 vs t3-t2 - and applied RTC trim each 64 seconds. [image: ntp-local.png] This graph is the chrony ntp logs, (open source software from github) [image: ntp-echo.png] You can see for some reason ntp was cut between 6 and 7 hrs, and my system logs a great offset that it fails to compensate for. I am thinking of replacing the crystal with a mems oscillator that has 0.1ppm. Best Regards, Can On Sat, May 31, 2025 at 9:29 AM Andrew Rodland via time-nuts < time-nuts@lists.febo.com> wrote: > It depends on how the stability of your oscillator compares to the accuracy > of the offset measurement. If the expected variance of your clock over > those 64 seconds is of the same order of magnitude as the noise in the > offset measurement, or worse, then you might as well just use the "hard > sync" approach of applying the measured offset immediately. If the > oscillator is stable over longer time periods then a PLL makes sense. A > second-order PLL is equivalent to a PI loop; the "I" term corrects for the > average frequency offset while the "P" time drives the phase offset to > zero. The more stable your oscillator, the smaller you can make the P and I > constants (over here we talk about using "longer time constants"); the > disciplined system will be dominated by the performance of the oscillator > over time periods shorter than the PLL time constants, and dominated by the > noise in your offset measurements over longer periods of time — which is > why it's not worth it to do anything fancy if your clock isn't stable > enough over the minimum adjustment interval. > > I would guess that you can characterize the noise in the offset measurement > (if you haven't already) by taking batches of offset measurements much > faster than usual so that the clock doesn't have time to wander anywhere; > then if you take a long series of offset measurements *with the correction > turned off* you can get a TDEV plot of the oscillator performance plus > measurement noise; and by risking a few assumptions (independent variances, > and that the measurement error is white phase noise) you can figure > TDEV^2[clock] = TDEV^2[total] - TDEV^2[meas.err.] to separate the plots and > figure out where the crossover should be. > > On Fri, May 30, 2025 at 8:43 AM Can Altineller via time-nuts < > time-nuts@lists.febo.com> wrote: > > > Hello, > > > > I make embedded sensor boards that are connected to a PC, and the sensor > > board uses USB-CDC to communicate with the PC. USB-CDC means HID virtual > > serial, that works at 921600 bauds, data is transmitted at 64 byte > chunks, > > and it does not have any of the problems of classical serial ports nor > > needs to go over FTDI chip. > > > > I have implemented a NTP like synchronization system where I calculate: > > > > rtt = (t4_point - t1_point).count() - (t3_point - t2_point).count(); > > offset = ((t2_point - t1_point).count() + (t3_point - t4_point).count()) > / > > 2; > > > > Basically the computer sends a sync request that has t1, and the sensor > > receives this at t2, and replies at t3, and the computer receives it at > t4. > > > > I transmit sync requests each 4 seconds, calculate the rtt and offset > from > > the computer side, and the calculated offset to the sensor, so RTC can be > > trimmed. > > > > My RTC frequency does not change, however each 64 seconds you can add or > > subtract from the RTC counter at the end of the second. Effectively > giving > > you RTCTrimSet(32768 + OFFSET) - It is good as a frequency changing > > oscillator but does the trick. > > > > On the left column Offset, RTT, and RTC trim values of PC to sensor > device. > > On the right column we have values from RPI5 to another sensor device. > > Units are nanoseconds. For the PC: I have them synced +-0.6ms and for the > > RPI5 we have +-0.1ms. The horizontal axis, each point is measured each 4 > > seconds, so this is about 10 hours of data. > > > > [image: Screenshot from 2025-05-29 19-19-50.png] > > > > Here are my questions: Given that I calculate the offset with this > method, > > and I adjust the RTC by adding or removing subseconds each 64 seconds, > what > > kind of algorithm should I use to filter the RTC offset? or should I > > directly adjust with the latest measured offset? Or even should I put a > PI > > algorithm? I have experimented with using a moving average filter vs > direct > > last measured RTC_OFFSET - and as expected even RTC_OFFSET method > responds > > faster avg(RTC_OFFSET) is more stable. Above graphs are both > > avg(RTC_OFFSET). I am thinking that since RTC_OFFSET points to a > difference > > in time, theoretically it should be best to instruct the RTC to lead/lag > by > > that difference. But doing so causes noise. What other algorithms can be > > used for these purposes? Maybe something that will calculate the trim not > > based on past data, but also prediction of the next cycle of 64 secs? > > > > What are your thoughts on this? What are your thoughts on the graph. > > > > Any ideas / help / recommendations greatly appreciated. > > > > Best Regards, > > Can > > _______________________________________________ > > time-nuts mailing list -- time-nuts@lists.febo.com > > To unsubscribe send an email to time-nuts-leave@lists.febo.com > _______________________________________________ > time-nuts mailing list -- time-nuts@lists.febo.com > To unsubscribe send an email to time-nuts-leave@lists.febo.com