The signal has an official range of 2000 km which covers most of Western Europe.
More details in Wikipedia
Some years ago I wrote a Turbobasic program to interpret and display the signal. It uses a receiver from Conrad electronics. Its signal goes through an ATmega8 in the hope of sharpening it up a bit - probably not essential - and is fed into a printer port (D sub 25) of a computer running MS-DOS 6.22 - obsolete but rock-solid. The signal format itself has not changed so you may like to convert the program ( listed below 'as is' ) to a modern platform. It can be used for the indoor sport of catching leap seconds.
I later wrote a pendulum-clock-checking program which compares the DCF77 signal to the swings of the pendulum and displays the variations on a graph.
' This gets the signal from the DCF77 transmitter and prints the time to screen ' It takes about 2 minutes for the program get synchronised with the signal ' There is no signal on the 59th second so there is a pause ' in the display on the 58th second while waiting for the end of the 59th second. ' The program uses a Conrad receiver module via an ATmega8 chip and LPT2: ' The receiver should be sited away from a CRT monitor. ' The ferrite core of the receiver should be oriented flat-on to Frankfurt-am-Main. ' The signal consists of a 77.5 kHz signal which is reduced in amplitude to 15% ' to form spaces at 1 second intervals. ' The second starts on the leading (falling) flank of the space. ' The spaces are either 100 ms long which means: 0 ' OR 200 ms long which means: 1 ' The series of 1's and 0's provides the time code. ' The 59th space in each minute is omitted to syncronise the receiver with ' the code. ' The program makes a note of the marks rather than the spaces since they are said ' to be less affected by thunderstorms. ' The code includes 3 parity bits (even parity) and 3 fixed bits. Actually, the ' second "fixed" bit, normally low, indicates a transmitter malfunction if high. ' The program "beeps" if the parity or fixed bits are incorrect. ' This version of the program uses a software loop to get the time values. ' Previous versions used the Turbo Basic TIMER function but its accuracy ' falls off after 54 ms. ' The marks are all circa 50 ms too short. ' Another approach would be to measure the events using the external ' microcontroller which would then relay the results via the serial port. Start: ON KEY(1) GOSUB Sayonara KEY(1) ON ON ERROR GOTO Start CLS ? ? "Input ------- DCF time signal 77.5 kHz: Space: ms Mark: ms COLOR 2,0 ? ? "-------------------------------------------------------------------------------" ? " 10's ------ 1 2 3 4 5 6" ? "Seconds ----- 123456789012345678901234567890123456789012345678901234567890 ? " this line is overwritten while synchronising" COLOR 14,0 ? "Signal ------ COLOR 2,0 ? "Values ------ 1248124 124812 1248121241248112481248 ? " 10's ------ 000 00 00 0 0000 COLOR 10,0 ? "Interpretation Weather code--- CET- Mins--- Hours- Days- Wk Mon Year---- COLOR 14,0 ? "Parity ------ 0 1 P P P COLOR 2,0 ? "-------------------------------------------------------------------------------" COLOR 7,0 ? ? "DCF time ---- ? ? "DCF date ---- ? ? "PC time ----- '################# MAIN LOOP ##################### DO '------------------------------------- Get signal - METHOD 1 Count = 0 ' Reset WHILE Signal = OldSignal ' Wait while the signal is unchanged Signal = INP(&H278+1) ' read parallel port LPT2: INCR Count ' count number of times Signal was read WEND OldSignal = Signal ' update Signal Msec = INT(Count/277) ' fudge Count into "milliseconds" Msec ' (divisor to suit your computer speed) ' ? Msec; '------------------------------------- Get signal - METHOD 2 ' MTIMER ' Start micro timer ' WHILE Signal = OldSignal ' Wait while the signal is unchanged ' Signal = INP(&H278+1) ' read parallel port LPT2: ' WEND ' OldSignal = Signal ' update Signal ' Msec = INT(MTIMER/1000) ' read and cook microseconds ' ? Msec ' The lengths of the incoming marks and spaces are not recorded very ' accurately - there is interference on the leading flanks of the marks - ' so there is a range: ' 50 - 150 Msecs counts as a short space (nominally 100 Msecs) ' 151 - 250 Msecs long space (nominally 200 Msecs) ' 500 - 850 Msecs short mark, (nominally 800 Msecs) ' 851 - 1200 Msecs long mark, (nominally 900 Msecs) ' > 1200 Msecs end of minute marker ' This program reads the marks only (it could be the spaces - or both) SELECT CASE Msec ' Sort out what length of mark (or space) ' was received and act on it CASE 500 TO 850 ' 800 ms. mark following a 200 ms. space CALL GetSignal(Sec,Min,Hour,Day,WDay,Month,Year,CET$,Imm$,Pm,Ph,Pc) ' Interpret that signal CALL ShowSec(Sec,"1",Msec) ' Display seconds and 1 CASE 851 TO 1200 ' 900 ms. mark following a 100 ms. space CALL ShowSec(Sec,"0",Msec) ' Display seconds and 0 CASE > 1200 ' long "end of minute" marker CALL ShowSec(Sec,"0-0",Msec) ' Display seconds CALL ShowTime(Hour,Min,CET$,Imm$,Pm,Ph) ' Display time and CET/CEST CALL ShowCalendar(Day,WDay,Month,Year,Pc) ' Display calendar SOUND 1000, .5 Sec = 0 CASE 10 TO 499 CALL ShowSpace(Msec) END SELECT LOOP '#################### SUB ROUTINES ############## SUB ShowSpace(Msec) LOCATE 2,42 : ? " " ' blink signal LOCATE 2,51 : COLOR 15,0 : ? USING "#####"; Msec ' print space END SUB '--------------------------- SUB ShowSec(Sec,Signal$,Msec) ' display stuff IF Sec = 0 THEN LOCATE 7,75 : ? " " ' wipe pointer INCR Sec LOCATE 2, 42 : COLOR 14,0 : ? Signal$ LOCATE 2, 67 : COLOR 15,0 : ? USING "#####"; Msec ' mark LOCATE 7, Sec+16 : ? " |" ' pointer LOCATE 8, Sec+16: COLOR 14,0 : ? Signal$ : COLOR 15,0 LOCATE 15, 22: COLOR 15,0 : ? RIGHT$(STR$(100 + Sec),2) LOCATE 19, 16: COLOR 7,0 : ? TIME$ LOCATE 8, 17: COLOR 15,0 : ? " " ' wipe signal IF Sec = 59 THEN LOCATE 7,Sec + 17 : ? " |" ' minute marker END IF END SUB '---------------------------- SUB ShowTime(Hour,Min,CET$,Imm$,Ph,Pm) ' display hours, mins, CETs LOCATE 15, 16: COLOR 15,0 : ? USING "##:##:"; Hour, Min; IF Imm$ <> "" THEN LOCATE 20, 26: COLOR 15,0 : ? Imm$ : EXIT SUB ELSE LOCATE 20, 26: ? SPACE$(30) END IF LOCATE 15, 26: COLOR 15,0 : ? CET$ IF Ph/2 <> INT(Ph/2) THEN BEEP ' beep if parity error IF Pm/2 <> INT(Pm/2) THEN BEEP ' ditto END SUB '---------------------------- SUB ShowCalendar(Day,WDay,Month,Year,Pc) ' display calendar SELECT CASE WDay CASE 1: WDay$ = "Monday," CASE 2: WDay$ = "Tuesday," CASE 3: WDay$ = "Wednesday," CASE 4: WDay$ = "Thursday," CASE 5: WDay$ = "Friday," CASE 6: WDay$ = "Saturday," CASE 7: WDay$ = "Sunday," END SELECT SELECT CASE Month CASE 1: Month$ = "January" CASE 2: Month$ = "February" CASE 3: Month$ = "March" CASE 4: Month$ = "April" CASE 5: Month$ = "May" CASE 6: Month$ = "June" CASE 7: Month$ = "July" CASE 8: Month$ = "August" CASE 9: Month$ = "September" CASE 10: Month$ = "October" CASE 11: Month$ = "November" CASE 12: Month$ = "December" END SELECT LOCATE 17, 16 : COLOR 15,0 ? WDay$; Day; Month$; ? USING " 20## "; Year COLOR 7,0 IF Pc/2 <> INT(Pc/2) THEN BEEP ' beep if parity error END SUB '----------------------------- 'Interpret the signal bits according to the second on which they arrived SUB GetSignal(Sec,Min,Hour,Day,WDay,Month,Year,CET$,Imm$,Pm,Ph,Pc) Imm$ = "" SELECT CASE Sec CASE 0 ' : BEEP ' this bit is always reset ! CASE 1 TO 14: Min = 0 : Hour = 0 : Day = 0 : WDay = 0 : Month = 0 : Year = 0 Pm = 0 : Ph = 0 : Pc = 0 '1 to 14 is a coded commercial weather forecast. The code has been broken 'by Microcontroller.net but not published. It took them 3 years. 'I believe the time provided a key to the code CASE 15 : CET$ = "Abnormal transmitter operation" CASE 16 : Imm$ = "A time change is imminent " CASE 17 : CET$ = "Central European Summer Time " CASE 18 : CET$ = "Central European Time " CASE 19 : Imm$ = "A leap second is imminent " ' 20 this bit is always set CASE 21 : Min = Min + 1 : INCR Pm CASE 22 : Min = Min + 2 : INCR Pm CASE 23 : Min = Min + 4 : INCR Pm CASE 24 : Min = Min + 8 : INCR Pm CASE 25 : Min = Min + 10 : INCR Pm CASE 26 : Min = Min + 20 : INCR Pm CASE 27 : Min = Min + 40 : INCR Pm CASE 28 : : INCR Pm 'parity bit minutes CASE 29 : Hour = Hour + 1 : INCR Ph CASE 30 : Hour = Hour + 2 : INCR Ph CASE 31 : Hour = Hour + 4 : INCR Ph CASE 32 : Hour = Hour + 8 : INCR Ph CASE 33 : Hour = Hour + 10 : INCR Ph CASE 34 : Hour = Hour + 20 : INCR Ph CASE 35 : : INCR Ph 'parity bit hours CASE 36 : Day = Day + 1 : INCR Pc CASE 37 : Day = Day + 2 : INCR Pc CASE 38 : Day = Day + 4 : INCR Pc CASE 39 : Day = Day + 8 : INCR Pc CASE 40 : Day = Day + 10 : INCR Pc CASE 41 : Day = Day + 20 : INCR Pc CASE 42 : WDay = WDay + 1 : INCR Pc CASE 43 : WDay = WDay + 2 : INCR Pc CASE 44 : WDay = WDay + 4 : INCR Pc CASE 45 : Month = Month + 1 : INCR Pc CASE 46 : Month = Month + 2 : INCR Pc CASE 47 : Month = Month + 4 : INCR Pc CASE 48 : Month = Month + 8 : INCR Pc CASE 49 : Month = Month + 10 : INCR Pc CASE 50 : Year = Year + 1 : INCR Pc CASE 51 : Year = Year + 2 : INCR Pc CASE 52 : Year = Year + 4 : INCR Pc CASE 53 : Year = Year + 8 : INCR Pc CASE 54 : Year = Year + 10 : INCR Pc CASE 55 : Year = Year + 20 : INCR Pc CASE 56 : Year = Year + 40 : INCR Pc CASE 57 : Year = Year + 80 : INCR Pc CASE 58 : : INCR Pc 'parity bit calendar END SELECT END SUB Sayonara: END