Precision Frequency measurement to 3 decimal places

Hi all,

I am in the process of building a Proton Precession Magnetometer to calculate the earths magnetic field strength by measuring the precession frequency of protons in distilled water. I am using an Arduino Nano as my micro controller to controls energizing the magnetometer, which is basically a resonant coil wound around a tube of distilled water, and then reading the returned precession frequency.

My set up is the Nano switches a DPDT relay to energise the coil from a 12v source then switch over to a receiving set of opamp / schmitt trigger IC into Pin 5 of the Nano to read the frequency which is around 2.35 kHz +/- 500hz.

The problem I have is at the moment I can only get a frequency reading to 0.1hz resolution after counting the frequency for 5 seconds (ie 2351.5Hz).

The result is quite stable compared to some other frequency codes I have tried.

I really want to get a stable result at least to 2 decimal places or better 3 decimal places over a shorter counting period ie (2351.523Hz).

Can someone help me please.

Also I’m pretty new to arduino and coding.

My code at the moment is as follows:

#include <FreqCounter.h>
#include <LiquidCrystal.h>

LiquidCrystal lcd(2, 3, 4, 8, 6, 7);

int mosfetPin = 9; // sets mosfet pin to pin 9 on arduino
int relayPin = 10; // sets relay pin to pin 10 on arduino

void setup() {

lcd.begin(16, 2);
pinMode(mosfetPin, OUTPUT); // sets mosfet pin, pin 9 on arduino as output
pinMode(relayPin, OUTPUT); // sets relay pin, pin 10 on Arduino as output
digitalWrite(mosfetPin, LOW); // sets 0v to pin 9, creating open cicuit and no power going thru negative line
digitalWrite(relayPin, HIGH); // sets relay pin, pin 10 to 5v and switches relay and connects the common to the NO and creates circuit from sensor to amp.


float frq = 0;

void loop() {

digitalWrite(relayPin, LOW); //set relay to NC and connecting polarising battery to sensor
delay (50); // small delay prior to polarising the coil
digitalWrite(mosfetPin, HIGH); // connects negative power supply through mosfet and polarises the coil.
Delay(4000); // polarises the coil for 4 seconds
digitalWrite(mosfetPin, LOW); // cuts power thru negative supply
delay(50); // small delay prior to switching relay to sensor circuit
digitalWrite(relayPin, HIGH); // switches relay over NO state to allow sensor signal to amp.

FreqCounter::f_comp= 300; // Set compensation to 12
FreqCounter::start(5000); // Start counting with gatetime of 100ms
while (FreqCounter::f_ready == 0) // wait until counter ready

frq=FreqCounter::f_freq; // read result
lcd.print(" Hz");


Cool project!

I'll add that you might want to also post on electronics forums, where electronics knowledge is high. Yeah, there are a few people here who know their stuff, but there are many beginners on here.

Doesn't hurt to spread your question around. Please post back on your progress. I'm interested in hearing more about this.

If you are counting events (as it appears the library FreqCounter.h is doing), basic statistics states that the expected error in the number of counts N is sqrt(N).

So, to get accurate frequency measurements by that method, you have no choice but to count for a long time.

Why should that be a problem? The Earth's magnetic field is not changing rapidly.

You should be aware that the Arduino Nano is not an accurate timebase.

The problem for counting over a long period is that the returning signal from the sensor is a decaying sine wave which lasts for maybe 2 seconds, sounds a bit like a tuneful 'ping' if you connect a speaker.

So the idea is to try and get a good accuracy in a short period.

I understand the arduino has it's limitations with regard to accuracy but at the moment it's all I've learnt about and so I'm hoping there is a work around. Or maybe someone can offer advice on external stuff to make it accurate.

Eventually I want to include an SD card reader and GPS module so when the frequency is measured it is saved along with the GPS coordinates onto the SD card. This bit has quite a few more hurdles and is a fairway down the track for me though.

A short pulse like you describe does not have a well defined frequency. Wave packet - Wikipedia

This project will be a great learning experience!

Actually they do produce an very accurate frequency directly proportional to the strength of the earths magnetic field, but only for a short period. Proton magnetometer - Wikipedia.

And yep it's very much a learning experience, I go a bit 'mad scientist' at times.

The article you linked does not support your claim to accuracy, for the pulse counting experiment you describe. You can do much, much better if you sample the data very rapidly and, for example, fit a sine wave segment to selected points.

Have fun!

Cool, so can you show me how to to do this? This is the whole reason I posted because I need help.

The problem with zero-crossing method is that as the amplitude falls the zero-crossing point might shift in phase a bit, leading to error.

If you sample the full waveform and heterodyne it down to a much lower frequency it will be easy to
measure. IE take your samples and multiply by a 2300Hz synthesized signal, then digitally low pass filter to
yield approx 50Hz resultant - then measure some zero-crossings on that using linear interpolation to get
microsecond precision period.

But you need a pretty accurate crystal on your Nano to get the best precision, and a fast enough way to
generate the LO tone.

It might be simpler to just sample at 2.2kHz and measure the 50Hz alias of the signal directly.