MSF is the UK’s equivalent of the German DCF77 time signal transmitter. This SDR project shows how a receiver and decoder for these (and other) time signals can be implemented quite simply and, above all, inexpensively. For the hardware you really won’t need much more than a low-cost Raspberry Pi Pico to receive, decode, and display MSF time signal information. In Germany the DCF77 transmitter in Mainflingen transmits an encoded long-wave time signal. Its equivalent based in the UK is the MSF signal formerly known as “The Rugby Clock.” It sends out time signals using a 60-kHz long-wave carrier signal. In the early days, it served as a frequency standard, and sent out a five-minute pulse train twice a day. The transmission protocol of the signal has changed several times over the decades, but it was not until 1977 that the encoding included time-of-day and date information that could be evaluated by the receiver.

The Project with Pico

Over the years, there have been many receiver/decoder circuits described in various Elektor articles that make use of the DCF77 signal, but this is probably the first time a design for an MSF receiver has been featured. There was, however, an add-on circuit for the good-old 6502 Junior Computer described in the English language edition of Elektor.

A lot of water has flowed under the bridge since then, and technology relating to receivers/decoders has progressed in leaps and bounds. In this article, we will use the latest “up-to-date” concepts to build a software-defined radio (SDR) using a small microcontroller board. The Raspberry Pi Pico board, which uses an RP2040 CPU clocked at 125 MHz (the in-house controller of the Raspberry Pi Foundation is equipped with dual 32-bit ARM Cortex M0+ cores), is a suitable, you could say, predestined piece of hardware for this application. Its analog-to-digital converter can run at 500 ksps. All this processing power can be purchased for an almost ridiculous €5. (See the Related Products below.)

Here we show how to implement a complete receiver in hardware and software for the 60-kHz MSF time signal. The entire receiver, without a display but with an RS232 output and antenna connection, is shown in Figure 1.

Raspberry Pi Pico board as a software-defined radio
Figure 1: The Raspberry Pi Pico board as a software-defined radio for MSF reception.


First, we will take a look at the hardware necessary to build the SDR. There are just a few additional items to connect to our pico board.

Antenna Input: We use the analog input pin ADC2 (GPIO28, on the Pico board pin 34) to receive signals from the antenna. The ADC uses the internal 3.3 V as a reference voltage. This pin must therefore be biased at half the reference voltage. The two resistors in Figure 2 take care of this. The 10-µF capacitor C1 provides AC coupling for the incoming signal.

Components require the A/D signal.
Figure 2: Components required at the A/D signal input.

RS232 Output: In its simplest form (without an LC display), the receiver uses a serial interface (115,200 Bit/s) to output the data. The interface is implemented by the circuit shown in Figure 3. We cannot use the USB port to output the serial data because it would generate interrupts to our software in an unpredictable way.

RS232 output from the Pico Board.
Figure 3: The RS232 output from the Pico Board.

PWM DACs: When no LC display is connected, it is possible to use the DACs with pulse width modulated (PWM) signals to create an easy aid for debugging. We have set up two PWM DACs with the associated low-pass filters as shown in Figure 4.

Two low pass filters for Pico project
Figure 4: Two low-pass filters for the PWM-DAC debug signals.

Using GPIO 2 and GPIO 3 as PWM outputs, for example, the demodulated signal and Bit-Timer signals can be displayed on a scope (Figure 5).

PWM-Test signals
Figure 5: The PWM-Test signals: Top trace is the ampl-value, middle trace is
the SecondTimer showing Sampling trigger pulses and below is the digital sigValue signal.

LCD: The 3.5-inch Arduino 8-bit module ILI9486 (non touch screen version SKU MAR3502 [4]) can be used as the LCD. This 3.5-inch Arduino shield has 480x320 coloured pixels and retails for around 10. Its connection to the Raspberry Pi Pico is shown in Figure 6.

Connections for the 3,5“ LCD for Pico project
Figure 6: Connections for the 3,5“ LCD.

The received signal is shown on the LCD together with a waveform showing bit timing information. The received time information is shown in plain text above the waveform (Figure 7). If you do not need to display this information you can choose to simply omit the LCD without the need to make any changes to the software.

 Receiver information on an LCD for Pico project
Figure 7: Receiver information on an LCD.

Active Antenna: We have already covered the antenna connection; the circuit of the active antenna can be seen in Figure 8. It is essentially based on the LM6132 dual operational amplifier. This op-amp is particularly suited for this application with an operating voltage of 2.7 to 24 V, 10 MHz gain bandwidth product, rail-to-rail input and output signal capability and low current consumption of 360 µA per amplifier. No doubt other op-amps would also work here but if you intend to replace the LM6132, check carefully whether it will match the spec.

Active antenna for the 60 kHz MSF signal.
Figure 8: Active antenna for the 60 kHz MSF signal.

Programming the Input Mixer

After the hardware, we come to the programming. The analogue paths of the SDR is structured as shown in Figure 9. The Raspberry Pi Pico can be programmed using several different languages. For this application we chose C using the Microsoft Visual Studio Code development environment running on a PC under Windows 10. Let’s look at how the different parts function.

MSF signal paths in RPi Pico

The ADC Sample Routine is triggered by the PWM and called 500,000 times per second via interrupt. The Offset ADCoffset = 2048 is subtracted from the ADC value and the result is then multiplied by ADCscale = 10 (Listing 1).

A/D sampling and Local oscillator Operation
Listing 1: A/D sampling and Local oscillator Operation. Summing and downsampling with a factor of 1250 at 400 Samples/s.

The local oscillator (LO-DDS) phase is updated and the input value is multiplied by the cosine (in-phase or I signal) and the sine (quadrature-phase or Q signal). The products are summed over 1250 samples (in Isum and Qsum). The values ​​are then (in Listing 2) passed to a FIFO for further processing, which then takes place at 500000/s/1250 = 400 samples/s. This sample rate is so low that all the further processing can be carried out using double variable values.

Filtering the I- and Q- values
Listing 2: Filtering the I- and Q- values and downsampling by a factor of 4 at 100 samples/s.

The values ​​are read from the FIFO and passed through a fourth-order Butterworth low-pass filter with a cut-off frequency of 3 Hz. During development it was found that this low cut-off frequency was necessary because the author’s antenna received strong interference signals directly adjacent to the wanted signal. This is followed by another down-sampling, this time by a factor of 4, so that 100 samples/s are then processed.

The msfSample() routine in Listing 3 then calculates the carrier amplitude ampl from the I/Q components. The logarithm of ampl is derived and in turn stored in ampl which makes it easier to decode the bits.

Calculating the ampl amplitude,
Listing 3: Calculating the ampl amplitude, the switching threshold and the pulse duration to recover bits.

The switching level threshold is derived from ampl via a first-order recursive filter calculation. The signal ampl is then compared with the switching level threshold to determine its digital receive value sigValue. Now with the analogue signal processing covered we can look at how the data is recovered from the received signal and how this corresponds to the time-of-day information.

Reading the Bits

The MSF transmitter sends out RF carrier pulses at each second as shown in Figure 10. At second 0 of every minute the carrier switches off for 500 ms. The SDR uses this pulse for synchronization. The pulses emitted at each of the following 59 seconds contain two bits of information: A and B. At the start of each of these seconds the carrier is off for 100 ms (corresponding to 10 samples in our application). If Bit A = 1, the carrier remains off for a further 100 ms, and if Bit B = 1, the carrier is off for another 100 ms.

The MSF seconds pulse. Pico project
Figure 10: The MSF seconds pulse showing Bit A and Bit B coding.

In SecondTimer, a timer, synchronised to each second, runs from 0 to 99. The software decoding works as follows: In Duration, the pulse length of the current pulse is measured. If a 0.5 s absence of the carrier is detected, SecondTimer is set to the value 50-2 = 48 so that the SecondTimer timer now runs synchronously with the second (Listing 4). At the same time, the minute is synchronized by setting the value of the current second to 0 in doMinuteSync().

Sampling of bits A and B
Listing 4: Sampling of bits A and B triggered by SecondTimer.

With the help of SecondTimer, the received signal is sampled at the mid-bit position of Bit A and Bit B (SecondTimer==15 and SecondTimer==25) in order to determine the values ​​of these transmitted bits. We simply output the received digital value via GPIO-Pin 4 (Pico-Pin 6):

gpio_put(GPIO4, sigValue); // Output sigValue at pico GPIO4=pin 6

The value of SecondTimer is also output later for debugging purposes via PWM, as are the values ​​of ampl and DAC. This is achieved with the following two statements:

pwm_set_gpio_level(PWM_PIN1, ampl/5.0 );  // Output amplitude

pwm_set_gpio_level(PWM_PIN2, DAC );       // Output timing

Decoding Time Information

Whenever SecondTimer is synchronized by the 0.5 s gap, one minute has passed and we can evaluate the latest time information. The received data bits are in the values ​​MSFbits[0 to 59]. The transmitter encodes the information listed in Figure 11 into these bits.

MSF time coding scheme.
Figure 11: MSF time coding scheme.

The time and date information is then simply reconstructed as in Listing 5 to give hours and minutes.

Decoding and serial BCD output
Listing 5: Decoding and serial BCD output of hours and minutes.

We also display the same information that we send out via the serial interface as text on the LCD. This is done using the instructions given in Listing 6.

Output Hours and minutes
Listing 6: Output hours and minutes information to the LCD.

A parity check on the received information is evaluated as in Listing 7. The monitored bits are the A-bits of the transmitted information. The four check bits are B-bits of each corresponding seconds pulse. Four parity checks are carried out, the integrity of up to 12 bits are protected by one parity bit.

Parity checking.
Listing 7: Parity checking.

Debug Signals

Classic superhet receiver designs mix the incoming RF signal with a variable frequency local oscillator signal to produce a lower intermediate frequency or IF. The IF signal is then filtered with a relatively narrow band filter. The IF signal of the MSF60 receiver can also be viewed using an oscilloscope. Our receiver mixes the input signal down to IF = 0 Hz. If you want to observe an AC IF signal, you can upconvert the 0 IF signal to an AC IF. The circuit block diagram is shown in Figure 12.

Upconversion to a 100 Hz IF
Figure 12: Upconversion to a 100 Hz IF

The software to perform the necessary upmixing is shown in Listing 8.

Coding for software upmixing.
Listing 8: Coding for software upmixing.

A PWM output is used as the DAC. The 100 Hz AM-modulated IF signal is shown in Figure 13.

100 Hz amplitude modulated IF Signal.
Figure 13: 100 Hz amplitude modulated IF Signal.

This completes the design and construction of the MSF receiver. Only one core of the processor is used in this application, leaving plenty of computing power for expansion. The bit decoding, for example, could be made more error-tolerant. A DCF77 receiver could be built in much the same way, only the bit decoding process would need to be adapted. The MSF signal reception here in Aachen (Germany) is much weaker than the DCF77 signal with an equivalent SDR. This often results in parity bits indicating errors in the received information but sufficient error-free messages still get through frequently enough to allow accurate time of day information to be displayed reliably.

Working with an RP2040

We have shown that with very little additional hardware, the Raspberry Pi Pico board can be turned into a complete MSF SDR. The most complex element of this build is construction of the active antenna. Given the processing power and low cost of the board this application shows what even a hobbyist with few resources can achieve nowadays. You often read how easy the Pico board can be programmed in Python, but in this application, it will not be able to cope with the 500k sample rate of the input signal. With C, however, the microcontroller hardware can be addressed more directly and programmed efficiently. We see here that even without the benefit of a floating point unit (FPU) the RP2040 is still more than capable of implementing a low-pass digital filter.

Questions or Comments?

If you have any technical questions regarding this article you can contact the author at or the Elektor team at

More on the Raspberry Pi Pico

Check out this Elektor video about the Raspberry Pi Pico!