Frequency counter precision check - Page 1
Forum section: electronics / general

author: jdev99
author_rank: Regular Contributor
timestamp: June 27, 2025, 08:44:47 am
content: Can somebody setup these 2 counters and test against a proper GPSDO, just to see how precise they are and if it is worth using as a frequency counter.The 1st one is using a small piece of code on a Arduino Nano (as the input point) and then use a RUST application on the PC and NTP time to calculate the test frequency, up to about 6MHz.When I compiled the RUST app. I had an "error: failed to run custom build command for libudev-sys v0.1.4 " and had to install this: " sudo apt-get install -y libudev-dev " to fix it.1st one:========From here:https://jpralves.net/post/2021/11/18/arduino-nano-frequency-counter-with-atomic-clock-accuracy.htmlCode here:https://github.com/FrankBuss/frequencycounter2nd one:========From here:https://forums.raspberrypi.com/viewtopic.php?t=306250The 2nd one use a RP2040 and some ASM code to calculate the test frequency.I loaded it onto a little RP2040-zero from Waveshare (https://www.waveshare.com/wiki/RP2040-Zero)I made both and it works, just don't know how good.Micropython code:============================================================================Code:# reciprocal pulse counter from 'horuable'# https://www.raspberrypi.org/forums/viewtopic.php?f=146&t=306250&p=1832034#p1832034# signal on the GP15 input pinfrom micropython import constimport rp2from rp2 import PIO, asm_pio# machine CPU frequency defaults to 125 MHz, but can be set up to 250 MHz (or more)# fractional calibration changes as well#fcal = 1.0000150 # (at 125 MHz)fcal = 1.0000073 # (at 250 MHz)#MFREQ = 125000000MFREQ = 250000000MCAL = int(MFREQ * fcal) # calibrated value for this board at given CPU freq@asm_pio(sideset_init=PIO.OUT_HIGH)def gate():"""PIO to generate gate signal."""mov(x, osr) # load gate time (in clock pulses) from osrwait(0, pin, 0) # wait for input to go lowwait(1, pin, 0) # wait for input to go high - effectively giving us rising edge detectionlabel("loopstart")jmp(x_dec, "loopstart") .side(0) # keep gate low for time programmed by setting x regwait(0, pin, 0) # wait for input to go lowwait(1, pin, 0) .side(1) # set gate to high on rising edgeirq(block, 0) # set interrupt 0 flag and wait for system handler to service interruptwait(1, irq, 4) # wait for irq from clock counting state machinewait(1, irq, 5) # wait for irq from pulse counting state machine@asm_pio()def clock_count():"""PIO for counting clock pulses during gate low."""mov(x, osr) # load x scratch with max value (2^32-1)wait(1, pin, 0) # detect falling edgewait(0, pin, 0) # of gate signallabel("counter")jmp(pin, "output") # as long as gate is low //jmp(x_dec, "counter") # decrement x reg (counting every other clock cycle - have to multiply output value by 2)label("output")mov(isr, x) # move clock count value to isrpush() # send data to FIFOirq(block, 4) # set irq and wait for gate PIO to acknowledge@asm_pio(sideset_init=PIO.OUT_HIGH)def pulse_count():"""PIO for counting incoming pulses during gate low."""mov(x, osr) # load x scratch with max value (2^32-1)wait(1, pin, 0)wait(0, pin, 0) .side(0) # detect falling edge of gatelabel("counter")wait(0, pin, 1) # wait for risingwait(1, pin, 1) # edge of input signaljmp(pin, "output") # as long as gate is low //jmp(x_dec, "counter") # decrement x req counting incoming pulses (probably will count one pulse less than it should - to be checked later)label("output")mov(isr, x) .side(1) # move pulse count value to isr and set pin to high to tell clock counting sm to stop countingpush() # send data to FIFOirq(block, 5) # set irq and wait for gate PIO to acknowledgedef init_sm(freq, input_pin, gate_pin, pulse_fin_pin):"""Starts state machines."""gate_pin.value(1)pulse_fin_pin.value(1)max_count = const((1 << 32) - 1)sm0 = rp2.StateMachine(0, gate, freq=freq, in_base=input_pin, sideset_base=gate_pin)sm0.put(freq)sm0.exec("pull()")sm1 = rp2.StateMachine(1, clock_count, freq=freq, in_base=gate_pin, jmp_pin=pulse_fin_pin)sm1.put(max_count)sm1.exec("pull()")sm2 = rp2.StateMachine(2, pulse_count, freq=freq, in_base=gate_pin, sideset_base = pulse_fin_pin, jmp_pin=gate_pin)sm2.put(max_count-1)sm2.exec("pull()")sm1.active(1)sm2.active(1)sm0.active(1)return sm0, sm1, sm2if __name__ == "__main__":from machine import Pin, freqimport uarray as arrayfreq(MFREQ) # set CPU frequency; not necessarily the default 125 MHzupdate_flag = Falsedata = array.array("I", [0, 0])def counter_handler(sm):#print("IRQ")global update_flagif not update_flag:gateVal = int(MFREQ/1000)# sm0.put(gateVal)# sm0.exec("pull()")data = sm1.get() # clock countdata = sm2.get() # pulse countupdate_flag = Truesm0, sm1, sm2 = init_sm(MFREQ, Pin(15, Pin.IN, Pin.PULL_UP), Pin(14, Pin.OUT), Pin(13, Pin.OUT))sm0.irq(counter_handler)print("Starting test")i = 0rCtr = 0while True:if update_flag:rCtr += 1update_flag = False#if (rCtr % 100) == 0:if True:clock_count = 2*(max_count - data+1)pulse_count = max_count - datafreq = pulse_count * ( MCAL / clock_count) # calibration constantprint("{0:d},{1:d},{2:d},{3:0.2f} Hz".format(i,clock_count,pulse_count,freq))i += 1# END============================================================================

author: EPAIII
author_rank: Super Contributor
timestamp: June 29, 2025, 05:29:58 am
content: The first thing you need for an accurate frequency counter is an accurate time base.I fear that the Arduino Nano is going to fall woefully short in that department. There's a reason why good frequency counters cost many thousands of dollars.On the other hand, if you are just comparing two frequencies, one from a satellite, then the accuracy will depend on the accuracy of the reference one. One such way is to feed both to the X and Y inputs of a scope and if you have a diagonal line they are equal. Other patterns for different relationships.I fear the difficulty with using a Nano for the comparison will be missed counts when the Nano can not see two coincident pulses.

author: Andy Chee
author_rank: Super Contributor
timestamp: June 29, 2025, 05:39:27 am
content: Quote from: EPAIII on June 29, 2025, 05:29:58 amIn this project, the accurate time base is an NTP time sync, which the Arduino Nano fetches from a network connected PC.

author: radiolistener
author_rank: Super Contributor
timestamp: June 29, 2025, 08:39:25 am
content: Quote from: Andy Chee on June 29, 2025, 05:39:27 amyou can't fix oscillator frequency drift and jitter with NTPit's important to understand that NTP does not compensate for oscillator frequency drift or jitter. NTP can help correct the system clock over time, but it does not directly control or stabilize the local oscillator in your Arduino. For precise frequency measurements or stable timing, you'd typically need a hardware-based time reference, such as a TCXO, OCXO, or a GPSDO.If you use a VCXO oscillator with slow drift and low jitter, it is theoretically possible to compute a correction based on NTP and apply it as a control voltage to adjust the oscillator's frequency. While this approach is feasible, it has practical limitations due to the nature of NTP.

author: nctnico
author_rank: Super Contributor
timestamp: June 29, 2025, 09:39:18 am
content: Depending on the OS, NTP update frequency can be as low as once a day or longer. So pretty much useless to add any kind of extra accuracy. For doing a cursory frequency measurement, the crystal on the Arduino itself is good enough.If you want to get more accuracy, buy a GPSDO and a (second hand) frequency counter from HP / Agilent. Without using a GPSDO, you can synchronise a digital function generator from the 10MHz output of the frequency counter to generate all kinds frequencies. This way you create a locally synchronised system to make relative measurement. But make sure to measure the generated frequency before using it as a input for a DUT because the resolution of digital function generators is finite. The cheaper ones typically have trouble to generate a precise 1Hz signal and/or may have phase accumulation errors when doing modulation.

author: indeterminate
author_rank: Regular Contributor
timestamp: June 29, 2025, 10:08:34 am
content: The PI on my bench has an xtel with a Tolerance & Temperature stability of +- 20 PPMso a counter based on it will only ever be worse that that.if your DMM has a counter function then it would be of similar accuracy.witch is ok for many jobs but is rubbish compared to a real counter.

author: tggzzz
author_rank: Super Contributor
timestamp: June 29, 2025, 10:29:45 am
content: Quote from: jdev99 on June 27, 2025, 08:44:47 amOh, good; it is nice to have an easy question to answer...Yes, it will be sufficiently precise for some purposes. To repeat that, no, it will be insufficiently precise for some purposes.

author: jdev99
author_rank: Regular Contributor
timestamp: June 30, 2025, 11:43:23 am
content: The idea is to see HOW GOOD it is, not if it can replace a GPSDO.

author: nctnico
author_rank: Super Contributor
timestamp: June 30, 2025, 04:19:41 pm
content: Quote from: jdev99 on June 30, 2025, 11:43:23 amThat is easy to answer: as good as the crystal being used for the processors on the boards you are using. There is not much use in testing because results depend on so many factors. If you want to improve, replace the crystals with better oscillators.

author: Kleinstein
author_rank: Super Contributor
timestamp: June 30, 2025, 04:29:41 pm
content: How good the frequency measurement on a DMM can vary a lot. With cheap meters an can be rather bad and limited by the display resolution.One can get a reasonable frequency reference from the radio signals. Which ones are good depends on the region. Decades ago the horizontal frequency of analog TV signals was really good in Germany at least.

author: radiolistener
author_rank: Super Contributor
timestamp: July 05, 2025, 10:15:32 pm
content: Branded short wave broadcast AM stations usually have pretty precise frequency, often about ±1 or ±2 Hz which is pretty good, so you can use their carrier for coarse oscillator frequency tuning. But some Arabic stations may have pretty large frequency offset.if you want better precision, there areRWM frequency standard stationsavailable on Eurasia and Europe continents:- 4996 kHz- 9996 kHz- 14996 kHzThey are synchronized with very precise frequency standard and transmitting different codes and carrier in a loop, so you can use it for fine frequency tune. It is very useful for radio receiver frequency calibration.Calibrating the oscillator is relatively straightforward. You'll need an SDR receiver to precisely observe the transmitter frequency on the spectrum, ideally with Hertz-level resolution. Tune in to an RWM time signal station - the higher the frequency, the more accurate the calibration, although high-frequency RWM signals are less frequently available. Then, activate your oscillator connected to a frequency synthesizer (such as an Si5351 or an FPGA-based PLL), configured to generate the same frequency as the RWM station. Adjust the oscillator frequency until synthesizer output aligns exactly with the RWM station's carrier. That’s all there is to it.Instead of an SDR receiver, you can use a conventional analog receiver in SSB or CW mode. Connect its audio output to a spectrum analyzer or a sound card running a real-time FFT display. Absolute frequency accuracy is not critical in this case, what matters is high frequency resolution and precisely aligning the oscillator’s peak (synthesizer output peak) with the peak of the RWM station.You can also perform the calibration without a frequency synthesizer by first calibrating your receiver against the RWM signal and then measuring the oscillator frequency by retuning the receiver's local oscillator. However, this approach requires confidence that your receiver's synthesizer has sufficient frequency resolution, which is not always the case - some receivers may exhibit slight frequency offset due to synthesizer limitations.After such calibration, you can use the oscillator to clock a frequency counter and perform accurate frequency measurements. However, keep in mind that the oscillator frequency drift over time and with temperature changes. Therefore, it's important to calibrate the oscillator immediately before taking measurements to ensure minimal frequency deviation during the measurement process.

author: Wallace Gasiewicz
author_rank: Super Contributor
timestamp: July 11, 2025, 12:00:21 am
content: Radiolisteners approach has been used by many people before GPSDOs were easily available. I use it with an HP 3746 selective level receiver....just to check the output of my homebrew GPSDO..etc. You can get pretty accurate using this approach depending on the resolution and stability of your radio receiver.Your limitation will be your Xtals. They will drift up and down depending on temp and other things. But his approach should give you an idea of how good your results are. Which is what you wanted, I think.

author: EPAIII
author_rank: Super Contributor
timestamp: July 11, 2025, 08:20:35 am
content: Thank you.Not to mention the variability of any information transmitted over the internet. I can easily see the differences between network transmitted time signals and those sent via WWV simply by observing the clocks here in my office. A full two seconds is not unlikely. The time obtained from internet sources is always behind that of WWV.Even satellite time can have more latency. It takes time for the signal to travel down from those orbits. If the receiver should change satellites, for whatever reason, then the distance and latency will be different. To quote one source that explains NTP time, "...ensuring they are accurate to within a few milliseconds of Coordinated Universal Time (UTC)". In today's world a few milliseconds (1/1000 second) is hardly a very accurate specification.I will say it again, "The first thing you need for an accurate frequency counter is an accurate time base." And you do not have it.Perhaps there is some other satellite or internet based reference signal that is more accurate. But I am not aware of any. If you want +/- 0.01% then use almost anything. But if you want parts per million or better, then I suspect you are going to need to spend some dollars or other currency.Edit: I don't know the details, but perhaps GPS may have something. It does depend on timing, but that would be relative timing, not absolute. As I said, I don't know the details.Quote from: radiolistener on June 29, 2025, 08:39:25 am

author: Benta
author_rank: Super Contributor
timestamp: July 11, 2025, 05:05:01 pm
content: As you're in England, the DCF77 carrier is a possibility. Buy a cheap DCF-controlled alarm clock, scavenge the ferrite antenna and build an AM receiver.

author: Doctorandus_P
author_rank: Super Contributor
timestamp: July 20, 2025, 12:40:00 am
content: Quote from: EPAIII on July 11, 2025, 08:20:35 amIndeed. NTP can keep the clock of your PC fairly accurate over long times (weeks, months), but your PC clock is still running on it's own (probably pretty cheap) clock. It nowhere near the accuracy needed for a "good" frequency counter.

author: EPAIII
author_rank: Super Contributor
timestamp: July 20, 2025, 09:24:03 am
content: This can illustrate the differences between long term and short term errors. You can spend thousands, even tens of thousands or more if you want a time base, an oscillator that has a high precision on a short term basis. One part in a billion. One part in a trillion. I don't know the level that an actual atomic clock can reach today. I am sure you can look it up. But that clock or oscillator will, I say WILL have an error over periods like days, months, years. We insert "leap seconds" as needed into our global time due to variances between the rotation of the earth and the best clocks that are in use at the standards agencies like NIST.On the other hand, a simple kitchen clock like the one my mother had in her kitchen and which became a family heirloom, used just a simple synchronous motor to stay in time with the power grid. That meant that it could be fast or slow by many seconds at various times of the day. But because the people who operated the AC generators for that power grid would run them just a bit faster during slack periods like overnight to make up for any time lost during the higher demand periods during the day as kept and constantly corrected by comparison with those atomic clocks, those generators and my mother's simple, synchronous clock kept perfect time over longer periods like weeks, months, and years. As long as the power grid does not experience an outage, that simple and inexpensive clock NEVER needs to be reset. Short term accuracy was horrible by laboratory standards. But long term accuracy was ALWAYS spot on.Modern, long term time standards, like "satellite time" and "internet time" are much like my mother's clock. They may have great long term accuracy but the short term accuracy can be off by a significant amount. Of course, the amounts of error are less today than they were back in the 1950s when that clock hung in our kitchen, but the idea remains the same. And the sources of the errors are also different. But you still must be aware of and take into account the differences between long term and short term accuracy. And any other sources/time frames of error.Quote from: Doctorandus_P on July 20, 2025, 12:40:00 am

author: iMo
author_rank: Super Contributor
timestamp: July 20, 2025, 09:48:08 am
content: The only "simple" way to get a quality clock for evaluating OP's counters is an OCXO disciplined by GPS. Everything else is just wasting the time and effort and money (my lesson learned)..

author: Infraviolet
author_rank: Super Contributor
timestamp: July 21, 2025, 10:35:29 pm
content: How can someone else running those pieces of arduino/rp2040 code tell you about your own system's accuracy? The main error source on an arduino nano or an rp2040 board when it comes to time measurements is the inaccuracy of the inbuilt oscillator. Nothing in software can improve upon that, you might be able to calibrate around it but I can't see how that calibration could be done without a known-accurate frequency source right next to your board to compare against. Also, nothing in well written software is likely to worsen timing accuracy either, it all comes down to the physical hardware's imperfections. Someone else testing code upon a board in their hands won't give you much clue as to the accuracy of the board you've got.Certainly on the nano this time keeping element is going to be a ceramic resonator, it would not be that hard to either build your own modified version of the nano board, but replace that resonator with a more accurate HC-49 crystal oscillator and two 22pF caps, or you could do some bodge wiring on an existing nano to replace the resonator with an oscillator in that manner. This would be more accurate than the resonator, crystals give you 50ppm accuracy vs 0.5% accuracy for a cermaic resonator.I'm not sure which dev board your rp2040 uses, but if it isn't using a crystal already it may be possible to add one. To my understanding some rp2040 boards don't use an external clock at all, using elements within the rp2040 instead which would be an RC oscillator of even lower accuracy than a ceramic resonator.Whether 50ppm is good enough for your use case is something you'd need to work out.