Cheap GPS Disciplined Oscillator

A 13$ GPS Disciplined Oscillator using Beitian BN220

Public Chat
Similar projects worth following
I recently acquired a second hand PFM3000 Frequency Counter, and I needed to make sure was is calibrated. How to do that if you don't have access to any calibration equipment? Well, with a cheap GSP module of, of course!

IMPORTANT: Getting a clean sinusoidal or square wave signal is not in scope of this project.

In the comments, @hartl pointed out that:
There's an easier way to check or calibrate a frequency counter with a GPS module: leave the module at its default settings and measure the period of the PPS signal.

still, it's a fun project :)

<TL>/<DR> If you just want to know the solution scroll down.

Problem (short)

I needed a (even dirty) source of reference frequency to calibrate a PFM3000. 


So it all started when I was searching the internet on how to calibrate a Frequency Counter, after I acquired a second hand PFM3000. Not knowing if the previous owner played with the calibration pot or not, that was a must.

In my sailing through the rough seas of the world wide web, I found a paper describing the different methods and one of them was using a GPS module. Turns out GPS modules have the possibility of outputting a frequency that is as exact as any reference one.

Great! I have a couple of GPS module for my

RC airplanes. Can I use those? More sailing.

 I decide to open the shield on one and discover it uses the M8030-KT (U-Blox). Searching through datasheets, yes, it has a configurable TIMEPULSE (whatever that is). But what pin is it connected to??? U-Blox don't provide pinout for their M8030-KT for the likes of us (hobbyists), and after more digging in the dark corners of the internet, I discover the  chinese clone/equivalent chip AT6558R and it's datasheet which explains the pinout:

Playing with the multimeter I confirmed it that it is similar. But Still couldn't figure out which pin was the TIMEPULSE. Next I stumbled upon the Youtube video:

Doh.. I didn't have to waste so much time.. it's the Red Led pin!!

OK.. What next.. installing the U-Center from U-Blox and trying to configure the BN-220 through the nice UI.. (I wired the BN-220 through an Arduino UNO, and shorted the RESET pin to GND, yes you can do that!) I got the NMEA messages, but the configuration just wouldn't work.. no ACK came back. A few hours later I gave up on them.  

Second day. I was still thinking that something is missing, since in the iNav it would auto-configure the GPS to another baud rate, refresh rate and use UBX. So why can't I use it in the UI??

I get my arduino Zero, find the library from Sparkfun for M8 GPS chips and start hacking away.

2 hours later (after lots of stupid errors and bad documentation) I had the GPS module outputting the frequency of my liking. They even have a sample that does exactly that. Time for a nice cup of tea.

P. S. My PFM3000 was 1.4Hz off on 1MHz. Well within specs (2PPM).


You can use any U-Blox M8 module (or newer, I believe). Beitian BN220 was on hand so I just used that one. Need to check the protocol version that it's >=18 (the first lines when powering the GPS module).

Arduino Zero is the weapon of my choice because it's not using a serial chip so you have a dedicated serial for USB communication and another one for peripherals (also lots of horsepower that weren't used in this project).

Check the Instructions for wiring and sketch.


Yellow is the signal entering the resistor, green is the one exiting the resistor and entering the LED.

A pretty good square wave for 1KHz:

At 10KHz it's still good:

Things just start getting slightly messy at 100KHz, occasional spikes, and the waveform starts deforming:

At 1MHz we see a totally different picture:

Next we have the 10MHz and that's where things go wrong, as the waveform is all over the place because of PLL trying to divide the 48MHz to get a 10MHz. There is no integer number to divide by. That's also where I stop trusting my Hantek and probes as to vertical accuracy.

My PFM3000 still managed to get an accurate reading, with just 14Hz error (within 2PPM).

12MHz - there might be some spikes that my Hantek can't catch, so I wouldn't trust it too much, but the main thing is the wave is very uniform, unlike the 10MHz:

And because I'm considering...

Read more »

  • 1 × Arduino of your choice
  • 1 × Beitian GPS (Ex: BN220)

  • 1

    Wiring is:

    • Black - Ground
    • White -  to RX on the Arduino
    • Green - to TX on the Arduino
    • Red - to 5V (3.3v should also do).

    The green and yellow arrows are the test points as shown in the Oscilloscope screenshots.

    If you want to embed it into a project, you can either solder a lead to the pad marked by the yellow arrow, or unsolder the Led and the resistor and solder a lead across the resistor pads.

  • 2

    The Arduino sketch is pretty simple.

    In the Library Manager install "SparkFun u-blox GNSS Arduino Library".

    The sketch code (much of it is from the SparkFun Github examples):

    #include <SparkFun_u-blox_GNSS_Arduino_Library.h>
    // desired frequency
    #define FREQ 12000000
    // serial for communicating with the host, on Arduino Zero
    #define HOST_SERIAL SerialUSB
    //serial for pins D0/D1 on Arduino Zero
    #define PER_SERIAL Serial1
    void setup() {
      // put your setup code here, to run once:
      if (!myGNSS.begin(PER_SERIAL)){
        HOST_SERIAL.println("\nFailed connecting to GPS module.");
      } else {
        HOST_SERIAL.println("GPS Module connected.");
        HOST_SERIAL.print(F("Version: "));
        byte versionHigh = myGNSS.getProtocolVersionHigh();
        byte versionLow = myGNSS.getProtocolVersionLow();
    void setPPS() {
      // Create storage for the time pulse parameters
      UBX_CFG_TP5_data_t timePulseParameters;
      // Get the time pulse parameters
      if (myGNSS.getTimePulseParameters(&timePulseParameters) == false)
        HOST_SERIAL.println(F("getTimePulseParameters failed! Freezing..."));
        while (1) ; // Do nothing more
      // Print the CFG TP5 version
      HOST_SERIAL.print(F("UBX_CFG_TP5 version: "));
      timePulseParameters.tpIdx = 0; // Select the TIMEPULSE pin
      //timePulseParameters.tpIdx = 1; // Or we could select the TIMEPULSE2 pin instead, if the module has one
      // We can configure the time pulse pin to produce a defined frequency or period
      // Here is how to set the frequency:
      // While the module is _locking_ to GNSS time, make it generate 5Hz
      timePulseParameters.freqPeriod = 5; // Set the frequency/period to 5Hz
      timePulseParameters.pulseLenRatio = 0x55555555; // Set the pulse ratio to 1/3 * 2^32 to produce 33:67 mark:space
      // When the module is _locked_ to GNSS time, make it generate select FREQ
      timePulseParameters.freqPeriodLock = FREQ; // Set the frequency/period
      timePulseParameters.pulseLenRatioLock = 0x80000000; // Set the pulse ratio to 1/2 * 2^32 to produce 50:50 mark:space
  = 1; // Make sure the active flag is set to enable the time pulse. (Set to 0 to disable.)
      timePulseParameters.flags.bits.lockedOtherSet = 1; // Tell the module to use freqPeriod while locking and freqPeriodLock when locked to GNSS time
      timePulseParameters.flags.bits.isFreq = 1; // Tell the module that we want to set the frequency (not the period)
      timePulseParameters.flags.bits.isLength = 0; // Tell the module that pulseLenRatio is a ratio / duty cycle (* 2^-32) - not a length (in us)
      timePulseParameters.flags.bits.polarity = 1; // Tell the module that we want the rising edge at the top of second. (Set to 0 for falling edge.)
      // Now set the time pulse parameters
      if (myGNSS.setTimePulseParameters(&timePulseParameters) == false)
        HOST_SERIAL.println(F("setTimePulseParameters failed!"));
    void loop() {

     A few things that you can just change out of the box:

    FREQ - set to the desired frequency.

    HOST_SERIAL - the serial to use for communicating to the host (meaning your computer).

    PER_SERIAL - the serial for communicating to the GPS module. 

    The last two you might need to change depending on the flavour of your Arduino. The #defines are set for Arduino Zero (Adafruit Metro M0 if to be precise).

    And the expected output is:

    GPS Module connected.
    Version: 18.0
    UBX_CFG_TP5 version: 1

    Your Red LED should be flickering at a 5Hz frequency until a GPS lock is acquired. After that it should change to constant red if your FREQ frequency is high enough (probably over 50Hz).

  • 3

    Enjoy a Reference Frequency that you can use either to calibrate a frequency counter, or use it as a oscillator for your project.

View all 3 instructions

Enjoy this project?



hartl wrote 06/12/2021 at 13:06 point

There's an easier way to check or calibrate a frequency counter with a GPS module: leave the module at its default settings and measure the period of the PPS signal. 

  Are you sure? yes | no

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates