-
Tuning the Feedback Circuit
08/30/2025 at 17:08 • 0 commentsIn the previous Post, I described that I have sporadic overtones in the Fork measurement which slightly shifts the base frequency.
The main Feedback is this (taken from the Inspiration's Project):
There, R4 and R5 together with C2 and C3 form a Bandpass filter. Mainly I suspected the large C3 (High-Pass) to be too big.
So I used my "trusty" DS203 oscilloscope and attached Probes to ForkOut (Yellow) and Pin 3 of the Tuning Fork Inductor (Blue). It is labelled 47^3 because of the 47 nF Capacitor at C3.
Notice the sharp edges at the low and high spots of the Yellow trace, as soon as the blue trace (the driving coil) either Turns on or the first step-off. (I don't know why it does the first step down. Well, I am not an Electrical Engineer...)
Then I tried different Values for C3. From Top to bottom: 47^3, (10+22)^3, 10^3, and 68^2.![]()
You can notice the timing of the sharp edges being later with a smaller value, and also the drive of the Fork is more smooth with a lower value. Probably the Transistor was overloaded, but I don't know (Not an EE...)
The Final Fork signal, after choosing a single 10nF Capacitor for C3 and moving the Fork until the trace looks optimal, I am much happier with the ForkOut signal, because it looks much more like an actual sine wave:
Well, now off to a new test run for logging :) -
Analysis of final Verification Run before One-Herz-Challenge
08/17/2025 at 18:03 • 0 commentsAs the deadline for the one hertz challenge approaches, I need to conclude the current validation test run. The changes mentioned in the previous post - mainly the hot display - increased precision quite a lot. However, as one can see in the previous log run, the Oscillating Feedback Loop on the Fork seems to sometimes produce noise overtones which seems to slightly change the base frequency. I will address that by tuning the feedback loop capacitors, but sadly this is too late for the Challenge entry. And only the hard facts count!
For the good side: The Drift, before the noise kicks in (Phase A), is around -0.5 seconds per day, which is a great project success for me. And in the last Phase C, when the frequency seems to settle, so does the drift (remember: Measured against a one-megahertz reference GPS "ground truth").
![]()
And to allow everyone to look at it themselves, the raw data of that graph is published here.
The other part of the Project was also the question what kind of Model is needed to estimate this System. This is fortunately answerable :)
The final Process Model consists of 1. a rolling-average Temperature Dampening to estimate the temperature of the Fork based on the Sensor, 2. a two-degree polynomial for the Fork's frequency response, and 3. another two-degree polynomial for an estimation compensation based on the Temperature-Change-Rate to accommodate the warm-up of the fork for fast temperature changes. But, as you can see in the factors, the (3) Temperature-change-rate has a very small influence on the overall system, but increases the fit by a minor percentage.![]()
The Model Parameters that were used for this validation run are based on the previous run's data (see my last post) and was estimated to the following:
Dampen factor b_d: 0.001893494029314581
Factors for damped period estimation:
b_0 = 9.885662144472119398e+05 (Base period per 440 oscillations at zero degrees)
b_1 = 1.213906162853203297e+00 (linear temperature dependency)
b_2 = 8.097113008716144879e-06 (exponential dependency: very small if any)
Factors for period error estimation based on temp gradient:
b_0 = -6.554192502536039244e-01 (constant error offset)
b_1 = -1.555599069694961478e-01 (rate-change dependent error)
b_2 = 2.188748997581093855e-02 (??)So, in conclusion: It is very possible to reach less-than-one-second per day of drift with a cheap tuning fork, a basic self-exciting circuit, and a lot of statistics and math :)
I am sure that this can be done better, and I will also try to find out what in the circuit causes the noisy overtones / frequency nudge.But nevertheless, this project was very fun. I hope to also have sparked some interest, it sure did a lot for me. Bye!
-
A log without Temperature Influence of the Display
08/12/2025 at 11:43 • 0 commentsOne of the things that go with a statistics project is, that you... well need a lot of samples.
So after one week of sampling, I can conclude that the actions mentioned in the previous post (Mostly a less bright display setting) improved the correlation between temperature measurement and fork frequency by a lot:
Also, the floating-average-factor (damp factor) is smaller now, which indicates a tighter temperature vs. frequency coupling (by time).
The Estimation of drift also seems very good with a plateau after a very noisy initial start.
Note That:
1. The Absolute Drift (in red) stays roughly the same after the initial noise period
2. The Per-Sample Estimation Differences are scaled so that they can be even seen
3. I have too many lines in this plot. :DNow, to deliver a final precision figure just in time for the end of the One-Hertz-Challenge, I used this week's data to estimate the final model parameters. The following week will then deliver real-world measured precision data based on the current settings.
-
Tricked by Myself and the Power-Hungry Display
07/27/2025 at 21:11 • 0 commentsWhile the test-run with the sub-microsecond estimation precision is running, I was reviewing the logs from the previous run:
![]()
... which was not too great considering some data loss and an unstable GPS readout.
Small side-note: actual pulse counter was working, but only the register readout had a too-small timeout value. This is a topic for another post, but with the reference clock being switched back- and forth, you also see the drift of the internal clock compared to the GPS.But you also see periodic temperature drops, which made me wonder where they come from. Is some abrupt heating / cooling happening when I am not in the office?
![]()
But after watching the Video of CuriousMarc about the Display and remembering "Taking a lot of power"... It hit me.
Do you see it? I dim the display at nighttimes to reduce Eye-Strain and in that glass-dome the "dayBrightness" (5 of 7) actually heats up the thing! And to make matters worse, the heat rises up to the Temperature Sensor, but most of the Tuning Fork is actually below the display, making it worse to correlate measured temperature with actual tuning fork temperature.
So my first change will be to set it to "medium brightness" all the time to not throw the measurement off, and on the long run probably will drill a vent-hole into the Glass-Dome's top to allow the air to circulate (and / or put the temperature sensor on the bottom half).
Weird Problem to have; but when the PPMs count, this is actually measurable in the precision!
Here in the last image you see that the "Difference per period" (which is a measure of precision) is actually worse (most off-zero) when the system is non-uniformly heated by the display. -
Chasing the PPMs - Fixed / floating precision
07/21/2025 at 21:57 • 0 commentsI had a shower-thought: Most or all of my test runs tend to drift into the direction "too fast".
And I was talking about a microsecond of resolution per period being at the limit of a second per day....
The code is correctly estimating a lot of digits after the comma for the microseconds, but on every accumulation cycle (one second, i.e. around 440 periods) I round off the sub-microseconds...So basically I am throwing accuracy out in the order of ~1 microseconds per second, which results in 86400 * 0.5us = 0.086s / day, which is roughly one second per week.
My plan to tackle this is to keep the fixed-point microseconds for reading off the time, but also keeping the sub-microsecond digits after the comma in a float to correctly round up or down.
Let's start another two week run with the modifications and let's see whether this improves the drift... -
First validation is in - Success!
07/07/2025 at 20:35 • 0 commentsA am very happy to announce that the longest test run so far - three weeks in a hot office - shows a drift of around 45 seconds.
This is far exceeding my goal of "under one minute per week" :)
The calibration data is taken from a one-week GPS-backed trial, so I expect it to behave slightly better if I re-run the analyze script based on these three weeks to output newer model parameters. But, from now on, I consider these changes to be cosmetic only.
I had to re-start the runs two times because of the I2C communication to the temperature sensor. The 1MHz from the external GPS-backed reference oscillator induced some serious noise, so I decreased the I2C bus resistors slightly.
This was enough to make it mostly work, but for a time-piece that should run a long time, this was not enough. I had also to detect a stuck I2C bus and a consecutive I2C bus recovery (see also i2c-lock-up-prevention-and-recovery).
And even that was not enough; sometimes the BME280 seemed to stop the measurement-cycle, which can be inferred by the registers having a certain default value. After detecting that state, and re-starting the continuous measurement mode, it finally ran through, even with people moving about in an office with carpet floor.To finalize the project, I want to crimp a custom cable to contain power, serial connection, and the (now optional!) external reference oscillator in one connection. Additionally, I'd like to make the model parameters run-time configurable (in the NOR flash) as a finishing touch, and perhaps I will improve the info of the watch-faces. Let's see.
But first, after many requests, a video where you can actually hear the tuning fork when I take off the glass hood (and stop the fork by touching it):
Oh, and by the way: Courious Marc just recently released a video about the HDSP-211X Display that I use. I agree both on the beautifulness as well as the fact that it is hard to film: www.youtube.com/watch?v=JXACON1XkPI
-
When it counts!
05/11/2025 at 17:30 • 0 commentsFor the external Clock-Source I opted for a used GPS-Disipled Oscillator: The U-Blox EVK-6T Eval kit.
With the cumbersome Windows configuration program "u-center" (which works on wine ;) ) it can be set to output up to one Megaherz from the "lock" timepulse. Which is coincidentally exactly the resolution that the internal counter of the RP2040 also has, which is nice.
Of course, setting up an interrupt callback for 1MHz input would saturate the CPU considerably (in contrast to the Tuning-Fork measurement, which is a slow 0.000440MHz.
As I wanted to learn about the PIO part of the RP2040 anyway, and did not find a pulsecounter in the examples, I implemented it in a PIO, which probably could count frequencies up to 50Mhz (depending on the implementation and base clock).
To reduce load on the memory bus, I did not use a DMA transfer to read the current count from the PIO FIFO, but instead opted for a "read request", so that the PIO program only sends the current counter if the input FIFO contains a "request".
For an "on average" correct pulse count, it checks for that request on both the high and low pulse (see https://github.com/Cirromulus/tuning-fork-clock/blob/main/lib/pulsecounter.pio).BTW, the rp2040 pio simulator did help me with debugging, especially with the limited jump conditions.
Anyway, it counts :)I needed to add a filtering-capacitor to the fork input, as I noticed the Display heavily distorting the fork input signal if the USB-Hub is weak and has to power both the Tuning Fork Clock as well as the GPS receiver.
Now, on to logging some time with the GPS-Precision of the external clock source :)
-
Calibration and the Model
04/28/2025 at 09:05 • 0 commentsNow that I have a steady oscillation, I can use the internal Quartz oscillator of the RP2040 to test my setup.
Of course, that is not precise and also has a temperature coefficient, but for development I think that this is enough. Once the Process is settled, I can use an external and more precise reference with the same tools.![A resolution problem comes up A resolution problem comes up]()
With a time reference of 1MHz (which I consider already high), the time between a single clock cycle at 440Hz is 2272.727µs. This poses a resolution problem, in where a single tick (µs) already can't resolve the exact (expected) frequency.
So I instead measure the time that 440 oscillations take, which adds a factor of 440 on top of the resolution while still being around one measurement a second. I like to see seconds updating actually every second :D
The process of finding some optimum was the usual data-science way: Fiddling around in python until something makes sense!The whole development is currently too long to write up for me, but basically I apply a simple polynom of the form `f(temp) = x1 + x2*temp + (x3*temp)^2`. This already finds a correlation and improves the estimation by a factor of 50:
![]()
But, as you can slightly make out in Figure 2, the temp vs. period graph is not linear but has some "loops".
This is caused by the tuning fork being of some considerable mass!It takes longer to get to temperature on temp changes than the faster sensor picks up. So I added a dampening model that adds a "low pass" to the temperature. This took a lot longer, because this dampening function is non-linear and can't be optimized by scipy. Well, it perhaps can, but I don't know how and the math is starting to get over my head :F
... So I track minima and maxima, and sample the mean time difference between temperature and frequency response, and linearize between some guessed sample points before optimizing the tempco polynom:
![More or less "final" estimation process More or less "final" estimation process]()
So you know see the "damp factor". Please excuse the damp vs dampening misuse, I am not a native speaker :D
Anyway, you can see the much more linear green dots in Figure 4 (correlation Data). The blue dots are the undampened samples. This actual measurement run went on for 5 Days, standing directly at a south-facing window, which I consider being the worst position, as sometimes the temperature goes from 20 Degrees up to 45 degrees (celsius)! But this is probably good for calibration :)I suspect the sunlight being the last factor that I can't get out, so another run was recorded in a more calm environment, which surprisingly resulted in a much more linear behavior and thus less error at, tested in the application phase, around 10µs/s. Success!
Now on to the display and final touches, and perhaps also an external reference to compensate the internal reference's drift that these runs could not consider.
-
First self-oscillating light
04/28/2025 at 07:55 • 0 commentsI started first in 2020, when the inspirational project still was fresh. I ordered only the base components for the self-oscillating fork circuit (that you can find in the inspiration project) along with a self-supporting holder.
I did not directly design a PCB, because that was the first hurdle and I did not want to waste such a big PCB in testing phase.However, I was not very successful on the breadboard and the Fork did not hold itself reliably, so the project got on hold.
Only much later I re-started the project and "just went for it" on a stripboard and, behold, it worked!
So it turns out (if you want to replicate that!), that my breadboard connections were loose enough that the vibrations from the Clock somehow made it very fragile.Well, now it is stable, and I can start with my original project question: Can I make it more precise?
![]()
![]()
PP
Notice the sharp edges at the low and high spots of the Yellow trace, as soon as the blue trace (the driving coil) either Turns on or the first step-off. (I don't know why it does the first step down. Well, I am not an Electrical Engineer...) 
Well, now off to a new test run for logging :)







