
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