Decoding time station signals (DCF77 etc.) with a microcontroller

By Peter Baier (DK7IH)


This article shows how to apply a off-the-shelf time signal receiver to a microcontroller, refers about the signal coding and describes the necessary software written in “embedded C”.


Receiver modules are available through various vendors on the internet, a respective search pattern could be this one.

Time signal transmitters

In the LF bands there are a handful (at least) of stations available that provide a standardized time signal based on the time markers produced by high accuracy atomic clocks. In central Europe DCF77 is the a well known time signal station for radio controlled clocks and watches.

The call sign “DCF77” originates from “D” which is for “Deutschland” (Germany), “C” stands for LF and “F” is for the proximity for Frankfurt/Main. DCF77’s output power is about 50kW of which about 30kW are transmitted into the atmosphere. You can see the station’s facilities on Google Maps.

Other transmitters are sited in the UK, USA, Japan etc:

  • DCF77 (Germany) in Mainflingen in Hessen
  • MSF (UK) in Anthorn
  • JJY (Japan)
  • WWV (USA) in Fort Collins, Colorado
  • WWVH (USA) in Hawaii
  • BPC (China) in Shangqiu (Source)

DCF77 is intended for reception within a range of max. 2000km, thus it covers most of the central of Europe. Ground wave propagation is possible within a radius of about 600km from Frankfurt, sky wave might reach up to between 2000 to 2500km. But even greater distances occasionally have been reported.

Other stations transmit on lower frequencies, MSF for example (from Northern England) on 60 kHz. Some of the off-the-shelf available receiver modules are capable of alternating the reception frequency by soldering the on-board clock crystal (32768 kHz) to another terminal on the PCB.

Signal structure of DCF77

DCF77 uses amplitude modulation (AM). The signals amplitude is reduced down to about 15% of its nominal power thus defining 2 different states:

  • Full amplitude
  • Reduced amplitude

Therefore binary information can be coded. Usually with the off-the-shelf receivers available on the market you will find a “low” signal when the transmitters amplitude is “high” and vice versa. Every second one pulse is transmitted. There is one exception: The last second of every minute this pulse is cancelled marking the beginning of the next minute. A pulse’s length defines the “1” or “0” state of the respective bit. This makes the signal that you can see with the receivers output pin:

DCF77 signal structure a handed over by the receiver
DCF77 signal structure as put out by the receiver

This receiver output makes decoding easy because this output signal can be fed directly to a digital pin of the microcontroller.

As mentioned before, the gap (missing bit 59) between bit 58 and bit 0 is the marker for a new minute to start. If pause length is longer than, let’s say 1200ms, you can deduce the next minute is starting right now. The transmitter always transmits the following minute, so if you have read 58 bits completely, this represents the data of the current minute.

The single bits have got the following representations:

DCF77 bit coding
DCF77 bit coding

There are two “weird” things to be mentioned:

  • Where a two-digit number has to be transmitted, the decade comes first, then the unity position follows, each of them binary coded. Both digits thus are transmitted individually!
  • Byte order is LSB first!

The Software

The code is written in “Embedded C” (GNU CC)  and can be downloaded from my Github repo.


Timing and measuring pulse length

For this software, where time lapse decoding is applied, precise timing is mandatory. This software uses an ATmega8-MCU clocked with an external oscillator (crystal) f=16MHz. There is an interrupt routine that increases a counter every millisecond so the the duration of a pulse can be measured precisely.

Timer setup for the ATmega8 (f.clock=16MHz) for a 1ms counter rate:

//Timer 2 as counter for 1 millisecond fclock = 8MHz
OCR2 = 62;
TCCR2 |= (1 << WGM21); // Set to CTC Mode
TIMSK |= (1 << OCIE2); //Set interrupt on compare match
TCCR2 |= (1 << CS21)|(1 << CS22); // set prescaler to 256

Timer 2 (an 8-Bit timer) is used in CTC (Clear Timer on Compare Mode). OCR2 register defines the maximum value in the timer register. The timer increase per time unit is defined by the prescaler in TCCR2, dividing clock rate by 256 in this case. With a system clock of 16.000.000/s divided by 256 the timer is theoretically increased 62500 times per second. When counting to 62 this is done in about 0.00099 seconds which is equivalent nearly to 1ms. This degree of precision is suffice for what we intend to do.

If the value of 62 has been reached, an interrupt is triggered and the respective interrupt service routine is called once. This routine increases a milliseconds counter by one:


With this setting it is possible to decode the timing of the incoming signals very exactly. This is done inside a loop structure, first the loop waits for a pause of a length to be measured. Afterwards, when a bit present, the software takes the “hi”- time of the pulse an decides if this represents a “1” or a “0”.

First all the 58 bits are recorded and stored into an integer array of appropriate length, later the parity checks are performed to estimate the likelihood of a valid signal. With this very simple kind of check it it absolutely possible that there is a faulty result that could not be detected.

Parity checking

A simple way of checking plausibility of data is included by doing a parity check:

int get_parity(int b[], int sta, int end)
    int t0, n = 0;

    for(t0 = sta; t0 < end + 1; t0++)

    if((n / 2) * 2 == n)
        return 0; //even parity
        return 1; //odd parity

The number of “1”s in a specific part of the data stream, defined by “start” and “end” are counted. If the number is even, the function returns “0” which stands for “Even parity” and vice versa.

If you need higher security for error-free receive operation, plausibility checks should also be included. In my software, because I live about 150km from Frankfurt and therefore have sufficient field strength all over the day and night, I did not apply functions for enhanced error checking.

The receiver’s signal subsequently is fed into PB0 port, a LED connected to PD0 provides an optical indicator if the signal level is OK. You should see a 1 second clock rate here with pulses of two different lengths.

The software takes advantage an OLED display with TWI connection (SDA, SCL) and uses SH1106-chipset/driver to display all the data on screen:

With this OLED it is a good idea to dislocate the antenna from the display a little bit and use a parallel electrolytic capacitor for the VDD/GND connection of the display because the electromagnetic noise generated by the OLED can disturb the receiver. BTW: The angle between the antenna and the station that you are going to receive should be approx. 90°.

Thanks for watching!

Peter (DK7IH)

Leave a Reply

Your email address will not be published. Required fields are marked *