An MSF Atomic ClockCircuit description The heart of the project is a small circuit board retrieved from a
radio controlled alarm clock bought at a Lidl store. This was originally
for a Frankfurt DSF77 receiver, but with a change of antenna and a
60kHz crystal, it proved to be the most reliable MSF receiver I've come
across. The circuit diagram is the same as shown in my DCF77 project. I have used a 7404 NOT gate as a buffer. This also allowed me to
produce an inverted signal and to customize the pulse sent to the LED. If you need to test the quality of the signal your receiver produces, you could try the testing program mentioned below or make use of my Slow-scope (which is the slowest oscilloscope on the planet!). Below is part of a trace produced by the little program. I've marked the 500ms 0-second marker three quarter of the way along.
The Program The MSF clock takes exactly one minute to transmit the date and time. The timing diagram of three seconds is shown below.
Every second carries one bit. To show a 0 the carrier is turned off
for 100ms and for a one it is turned off for 200ms. Second 0 of the sequence
is the marker second, when the carrier is turned off for 500ms. Bits 52
to 59 can carry a secondary bit, used for BST indication and parity
checking. When this bit is set, the carrier is off for 300ms - shown in
red in the drawing above.. However, my program makes no use of this
feature. The coding scheme is shown below. The red numbers indicate the binary coded decimal place values of the set bits. The program recognises the beginning of the cycle by measuring the length of the low bit. This is always 500ms at the beginning of the sequence, which is a unique value. Alternatively one could use the unique pattern of bits 52 to 59, which is always 01111110 - a pattern which can not occur elsewhere. I have written the program to be as simple as possible. The main aim is to demonstrate how the data bits arrive one by one and how the time is decoded as the sequence develops.Below is a sample output.The screen above shows the complete pulse-train for one minute which the program updates in real time. The data sections of relevance are named. An explanation may be useful. The time of acquisition was the 21st of March 2012 at 19:39 hours. This is made up of a binary coded decimal sequence with the place values:
80 40 20 10 8 4 2 1
Depending on the largest number to be expected the digits used range
from eight for the year to (0-99) to three for the day of the week (0-6)Year . . . . . . . . . . no 80, no 40, no 20, one 10, no 8, no 4, one 2, no 1. 10 + 2 = 12. The 2000 is assumed Month . . . . . . . . no 10, no 8, no 4, one 2, one 1. 2+1 = 3 = March Day . . . . . . . . . . one 20, no 10, no 8, no 4, no 2, one 1. 20+1 = 21. Day of week . . . no 4, one 2, one 1. 2+1 = 3 = Wednesday. Apparently the week starts with Sunday. I leave the amazed reader to work out the hour and the minute. Notice that the numerals appear in two colours. In our example the white represent the last complete cycle and the blue numbers are part of the new incoming cycle. The colour changes at the beginning of every minute. The latest version of the program is more complex than usual. Not only will it decode the signal inverted or non-inverted - depending on the receiver module in use, but it also displays the waveform at the bottom of the screen. I'm rather pleased with this effort. Below is a link to a zip archive containing the BBC BASIC program file and also a self-contained .exe file of the program below. This latest version fixes the problem with different display font sizes by only using the BBC system font. Please also note that this version uses digital input 4 rather than an ADC input of the board.
K8055_MSF_5_0 Version 5.0 September 2013
Here is an older version of the program program for the K8061 interface board: K8061_MSF_Atomic_3_7.zip There is also a YouTube video of the screen output of the project taken during the insertion of a leap second in 2012 |
10 REM K8055_MSF_5_0 20 REM Display MSF atomic clock signal 30 REM (c) Jochen Lueg 40 REM http://roevalley.com 50 REM Limavady, October 2013 60 REM Version 5.0 70 REM Use digital channel 5 80 90 100 ON ERROR PRINT REPORT$;" at line ";ERL : END 110 PROCK8055_init 120 ON ERROR PROCerror 130 ON CLOSE PROCclose 140 PROCport_setup 150 PROCinit 160 SYS K8055_CloseDevice% 170 SYS K8055_OpenDevice%,Board% 180 SYS K8055_ClearAllDigital% 190 200 PROCcheck_for_inversion 210 PRINTTAB(1,1);"Waiting for the beginning of the sequence" 220 IF Invert% = 1 THEN 230 PROCfind_start_inverted 240 PROCprint_info 250 PROCread_pulse_inverted 260 ENDIF 270 280 IF Invert% = 0 THEN 290 PROCfind_start_noninverted 300 PROCprint_info 310 PROCread_pulse_noninverted 320 ENDIF 330 PROCclose 340 END 350 360 REM ******************************************************* 370 380 DEFPROCclose 390 SYS K8055_ClearAllDigital% 400 SYS K8055_CloseDevice% 410 SYS "FreeLibrary",K8055_Board% 420 QUIT 430 ENDPROC 440 450 460 DEFPROCprint_info 470 PRINTTAB(1,4);"Incoming MSF bits" 490 PRINTTAB(104,5)"S = Summertime" 500 PRINTTAB(104,5)"! = Summertime to change" 510 PRINTTAB(0,1)" " 520 ENDPROC 530 540 550 DEFPROCcheck_for_inversion 560 LOCAL Low%,High%,State% 570 Low%=0:High%=0 580 PRINT " It will take a few seconds to check for signal inversion" 590 PRINT 600 REPEAT 610 SYS K8055_ReadDigitalChannel%,Channel% TO State% 620 UNTIL State%=1 630 FOR J%=1 TO 3 640 TIME=0 650 REPEAT 660 SYS K8055_ReadDigitalChannel%,Channel% TO State% 670 UNTIL State%=0 680 High%+=TIME 700 TIME=0 710 REPEAT 720 SYS K8055_ReadDigitalChannel%,Channel% TO State% 730 UNTIL State%=1 740 Low%+=TIME 750 NEXT 760 IF Low%>High% Invert%=1 ELSE Invert%=0 770 CLS 780 IF Invert%=1 PRINT" Inverted signal" 790 IF Invert%=0 PRINT" Non-inverted signal" 800 ENDPROC 810 820 830 DEFPROCread_pulse_noninverted 840 PROClegend 850 REPEAT 860 SYS K8055_ReadDigitalChannel%,Channel% TO State% 870 IF G%=1 X%+=1:PLOT 5,X%/S%,State%*222 :IF X%/S%>2900 X%=0:CLG:MOVE 0,0 880 UNTIL State%=0 890 TIME=0 900 910 REPEAT 920 COLOUR Col1% 930 REPEAT 940 SYS K8055_ReadDigitalChannel%,Channel% TO State% 950 IF G%=1 X%+=1:PLOT 5,X%/S%,State%*222 :IF X%/S%>2900 X%=0:CLG:MOVE 0,0 960 UNTIL State%=1 970 Low%=TIME 980 SYS K8055_WriteAllDigital%,128 990 TIME=0 1000 REPEAT 1010 SYS K8055_ReadDigitalChannel%,Channel% TO State% 1020 IF G%=1 X%+=1:PLOT 5,X%/S%,State%*222 :IF X%/S%>2900 X%=0:CLG:MOVE 0,0 1030 UNTIL State%=0 1040 High%=TIME 1050 IF High%<15 THEN 1060 IF C%<17 DUT%+=1:IF C%<9 Sign$="+" ELSE Sign$="-" 1070 Low%+=High% 1080 TIME=0 1090 REPEAT 1100 SYS K8055_ReadDigitalChannel%,Channel% TO State% 1110 IF G%=1 X%+=1:PLOT 5,X%/S%,State%*222 :IF X%/S%>2900 X%=0:CLG:MOVE 0,0 1120 UNTIL State%=1 1130 Low%+=TIME 1140 TIME=0 1150 REPEAT 1160 SYS K8055_ReadDigitalChannel%,Channel% TO State% 1170 IF G%=1 X%+=1:PLOT 5,X%/S%,State%*222 :IF X%/S%>2900 X%=0:CLG:MOVE 0,0 1180 UNTIL State%=0 1190 High%=TIME 1200 ENDIF 1210 TIME=0 1220 SYS K8055_WriteAllDigital%,64 1230 IF Low%<16 Time%(C%)=0 1240 IF Low%>16 Time%(C%)=1 1250 IF Low%>26 AND Low%<35 BitB%=1 ELSE BitB%=0 1260 IF Low%>45 C%=0 :SWAP Col1%,Col2%:DUT%=0 1270 PROCdecode(C%) 1280 C%+=1 1290 PROCcheck_keyboard 1300 UNTIL FALSE 1310 ENDPROC 1320 1330 1340 DEFPROCread_pulse_inverted 1350 PROClegend 1360 REPEAT 1370 SYS K8055_ReadDigitalChannel%,Channel% TO State% 1380 IF G%=1 X%+=1:PLOT 5,X%/S%,State%*222 :IF X%/S%>2900 X%=0:CLG:MOVE 0,0 1390 UNTIL State%=1 1400 TIME=0 1410 REPEAT 1420 COLOUR Col1% 1430 REPEAT 1440 SYS K8055_ReadDigitalChannel%,Channel% TO State% 1450 IF G%=1 X%+=1:PLOT 5,X%/S%,State%*222 :IF X%/S%>2900 X%=0:CLG:MOVE 0,0 1460 UNTIL State%=0 1470 High%=TIME 1480 SYS K8055_WriteAllDigital%,128 1490 TIME=0 1500 REPEAT 1510 SYS K8055_ReadDigitalChannel%,Channel% TO State% 1520 IF G%=1 X%+=1:PLOT 5,X%/S%,State%*222 :IF X%/S%>2900 X%=0:CLG:MOVE 0,0 1530 UNTIL State%=1 1540 Low%=TIME 1550 IF Low%<15 THEN 1560 IF C%<17 DUT%+=1:IF C%<9 Sign$="+" ELSE Sign$="-" 1570 High%+=Low% 1580 TIME=0 1590 REPEAT 1600 SYS K8055_ReadDigitalChannel%,Channel% TO State% 1610 IF G%=1 X%+=1:PLOT 5,X%/S%,State%*222 :IF X%/S%>2900 X%=0:CLG:MOVE 0,0 1620 UNTIL State%=0 1630 High%+=TIME 1640 TIME=0 1650 REPEAT 1660 SYS K8055_ReadDigitalChannel%,Channel% TO State% 1670 IF G%=1 X%+=1:PLOT 5,X%/S%,State%*222 :IF X%/S%>2900 X%=0:CLG:MOVE 0,0 1680 UNTIL State%=1 1690 Low%=TIME 1700 ENDIF 1710 TIME=0 1720 SYS K8055_WriteAllDigital%,64 1730 IF High%<16 Time%(C%)=0 1740 IF High%>16 Time%(C%)=1 1750 IF High%>26 AND High%<35 BitB%=1 ELSE BitB%=0 1760 IF High%>45 C%=0 :SWAP Col1%,Col2%:DUT%=0 1770 PROCdecode(C%) 1780 C%+=1 1790 PROCcheck_keyboard 1800 UNTIL FALSE 1810 ENDPROC 1820 1830 1840 DEFPROClegend 1850 Col1%=15 : REM White 1860 Col2%=6 : REM Cyan 1870 PRINTTAB(1,TabY_exp%)"| DUT1 positive | DUT1 negative | Year | Month | Day | DoW | Hour | Minute | ! Frame S |" 1880 C%=1 1890 DUT%=0 1900 ENDPROC 1910 1920 1930 DEFPROCcheck_keyboard 1940 IF INKEY(-84) G%=G% EOR 1 1950 IF INKEY(-49) S%=1:CLG:MOVE 0,0 :X%=0 1960 IF INKEY(-50) S%=2 :CLG:MOVE 0,0 :X%=0 1970 IF INKEY(-18) S%=3 :CLG:MOVE 0,0 :X%=0 1980 IF INKEY(-19) S%=4 :CLG:MOVE 0,0 :X%=0 1990 IF INKEY(-98) CLG:MOVE0,0:X%=0 2000 ENDPROC 2010 2020 2030 DEFPROCdecode(C%) 2050 IF C%=25 THEN 2060 Year%=2000+80*Time%(17)+40*Time%(18)+30*Time%(19)+10*Time%(20)+8*Time%(21)+4*Time%(22)+2*Time%(23)+Time%(24) 2070 Decoded$+= STR$(Year%)+" " 2080 PRINTTAB(TabX_decoded%,TabY_decoded%)Decoded$ 2090 ENDIF 2100 IF C%=30 THEN 2110 Month%=10*Time%(25)+8*Time%(26)+4*Time%(27)+2*Time%(28)+Time%(29) 2120 Decoded$+=Month$(Month%)+" " 2130 IF Month%<13 THEN PRINTTAB(TabX_decoded%,TabY_decoded%)Decoded$ 2140 ENDIF 2150 IF C%=36 THEN 2160 Day%=20*Time%(30)+10*Time%(31)+8*Time%(32)+4*Time%(33)+2*Time%(34)+Time%(35) 2170 IF Day%=1 OR Day%=31 Decoded$+=STR$(Day%)+"st " 2180 IF Day%=2 Decoded$+=STR$(Day%)+"nd " 2190 IF Day%=3 Decoded$+=STR$(Day%)+"rd " 2200 IF Day%>3 AND Day%<31 Decoded$+=STR$(Day%)+"th " 2210 PRINTTAB(TabX_decoded%,TabY_decoded%)Decoded$ 2220 ENDIF 2230 2240 IF C%=39 THEN 2250 DoW%=4*Time%(36)+2*Time%(37)+Time%(38) 2260 Decoded$+=Weekday$(DoW%)+" " 2270 PRINTTAB(TabX_decoded%,TabY_decoded%)Decoded$ 2280 ENDIF 2290 IF C%=45 THEN 2300 Hour%=20*Time%(39)+10*Time%(40)+8*Time%(41)+4*Time%(42)+2*Time%(43)+Time%(44) 2310 IF Hour%<10 Decoded$+="0" 2320 Decoded$+=STR$(Hour%)+":" 2330 PRINTTAB(TabX_decoded%,TabY_decoded%)Decoded$ 2340 ENDIF 2350 2360 IF C%=52 THEN 2370 Minute%=40*Time%(45)+20*Time%(46)+10*Time%(47)+8*Time%(48)+4*Time%(49)+2*Time%(50)+Time%(51) 2380 IF Minute%<10 Decoded$+="0" 2390 Decoded$+=STR$(Minute%) 2400 PRINTTAB(TabX_decoded%,TabY_decoded%)Decoded$ 2410 TabX_seconds%=LEN(Decoded$)+TabX_decoded%+1 2420 Decoded$="" 2430 ENDIF 2440 IF C%<10 PRINTTAB(TabX_seconds%,TabY_decoded%);"0";C%;" " 2450 IF C%>9 PRINTTAB(TabX_seconds%,TabY_decoded%);C%;" " 2470 IF C%=53 PRINTTAB(C%*2,TabY_BitB%);BitB% 2480 IF C%=58 PRINTTAB(C%*2,TabY_BitB%);BitB% 2490 IF C%>0 PRINTTAB(C%*2,TabY_Pulse%);Time%(C%);"" 2500 IF C%=0 PRINTTAB(120,TabY_Pulse%);Time%(C%);"" 2510 IF C% = 17 PRINTTAB(52,TabY_gmt%)"| GMT - UTC = ";Sign$;DUT$(DUT%) 2520 2530 ENDPROC 2540 2550 2560 DEFPROCfind_start_noninverted 2570 LOCAL State%,Off% 2580 REPEAT 2590 SYS K8055_ReadDigitalChannel%,Channel% TO State% 2600 IF G%=1 X%+=1:PLOT 5,X%/S%,State%*222 :IF X%/S%>2900 X%=0:CLG:MOVE 0,0 2610 UNTIL State%=0 2630 REPEAT 2640 TIME=0 2650 REPEAT 2660 SYS K8055_ReadDigitalChannel%,Channel% TO State% 2670 IF G%=1 X%+=1:PLOT 5,X%/S%,State%*222 :IF X%/S%>2900 X%=0:CLG:MOVE 0,0 2680 UNTIL State%=1 2690 Low%=TIME 2700 SYS K8055_WriteAllDigital%,128 2710 TIME=0 2720 REPEAT 2730 SYS K8055_ReadDigitalChannel%,Channel% TO State% 2740 IF G%=1 X%+=1:PLOT 5,X%/S%,State%*222 :IF X%/S%>2900 X%=0:CLG:MOVE 0,0 2750 UNTIL State%=0 AND TIME >20 2760 High%=TIME 2770 SYS K8055_WriteAllDigital%,64 2780 PRINTTAB(1,4)"Low: ";Low%;" High: ";High%;" Both: ";High%+Low%;" " 2790 PROCcheck_keyboard 2800 UNTIL Low%>40 2810 X%=0:CLG:MOVE 0,0 2820 C%=1 2830 PRINTTAB(1,4)" " 2840 ENDPROC 2850 2860 2870 DEFPROCfind_start_inverted 2880 LOCAL State% 2890 REPEAT 2900 SYS K8055_ReadDigitalChannel%,Channel% TO State% 2910 IF G%=1 X%+=1:PLOT 5,X%/S%,State%*222 :IF X%/S%>2900 X%=0:CLG:MOVE 0,0 2920 UNTIL State%=1 2930 REPEAT 2940 TIME=0 2950 REPEAT 2960 SYS K8055_ReadDigitalChannel%,Channel% TO State% 2970 IF G%=1 X%+=1:PLOT 5,X%/S%,State%*222:IF X%/S%>2900 X%=0:CLG:MOVE 0,0 2980 UNTIL State%=0 2990 High%=TIME 3000 SYS K8055_WriteAllDigital%,128 3010 TIME=0 3020 REPEAT 3030 SYS K8055_ReadDigitalChannel%,Channel% TO State% 3040 IF G%=1 X%+=1:PLOT 5,X%/S%,State%*222:IF X%/S%>2900 X%=0:CLG:MOVE 0,0 3050 UNTIL State%=1 AND TIME>20 3060 Low%=TIME 3070 SYS K8055_WriteAllDigital%,64 3080 PRINTTAB(1,4)"Low: ";Low%;" High: ";High%;" Both: ";High%+Low%;" " 3090 PROCcheck_keyboard 3100 UNTIL High%>40 3110 X%=0:CLG:MOVE 0,0 3120 C%=1 3130 ENDPROC 3140 3150 3160 DEFPROCport_setup 3170 PROCwindow(1500,500,"MSF atomic clock decoder") 3180 VDU 28,0,15,185,0 3190 VDU 24,40;40;2940;390; 3200 COLOUR12,60,60,60 3210 GCOL 140 3220 CLG 3230 ORIGIN 40,50 3240 VDU5 3250 MOVE 0,330 3260 PRINT" To stop or start plotting press 'G' for possibly up to a second" 3270 PRINT" Press 1 to 4 to change time-base" 3290 VDU 23,23,1;0;0;0; :REM Lines double thickness 3300 GCOL11 3310 MOVE 0,0 3320 X%=0 3330 OFF 3340 VDU 4 3350 ENDPROC 3360 3370 3380 DEFPROCinit 3390 G%=1 3400 S%=3 3410 Sign$=" " 3420 Decoded$="" 3430 TabY_exp%=8 3440 TabY_decoded%=11 3450 TabX_decoded%=5 3460 TabX_seconds%=42 3470 TabY_BitB%=6 3480 TabY_Pulse%=7 3490 TabY_gmt%=11 3500 DIM Time%(100) 3510 DIM Weekday$(6) 3520 Weekday$(0)="Sunday" 3530 Weekday$(1)="Monday" 3540 Weekday$(2)="Tuesday" 3550 Weekday$(3)="Wednesday" 3560 Weekday$(4)="Thursday" 3570 Weekday$(5)="Friday" 3580 Weekday$(6)="Saturday" 3590 DIM Month$(12) 3600 Month$(1)="January" 3610 Month$(2)="February" 3620 Month$(3)="March" 3630 Month$(4)="April" 3640 Month$(5)="May" 3650 Month$(6)="June" 3660 Month$(7)="July" 3670 Month$(8)="August" 3680 Month$(9)="September" 3690 Month$(10)="October" 3700 Month$(11)="November" 3710 Month$(12)="December" 3720 3730 DIM DUT$(9) 3740 DUT$(0)="0ms" 3750 DUT$(1)="100ms" 3760 DUT$(2)="200ms" 3770 DUT$(3)="300ms" 3780 DUT$(4)="400ms" 3790 DUT$(5)="500ms" 3800 DUT$(6)="600ms" 3810 DUT$(7)="700ms" 3820 DUT$(8)="800ms" 3830 3840 Channel%=5 3850 Board%=0 3860 ENDPROC 3870 3880 3890 DEFPROCwindow(WindowWidth%,WindowHeight%,WindowTitle$) 3900 MODE 30 3910 SYS "SetWindowPos",@hwnd%,0,0,0,WindowWidth%,WindowHeight%,6 3920 COLOUR 128 3930 CLS 3940 COLOUR 15 3950 VDU 26 3960 SYS "SetWindowText",@hwnd%,WindowTitle$ 3970 ENDPROC 3980 3990 4000 DEFPROCerror 4010 CLS 4020 PRINT REPORT$;" at line ";ERL : 4030 SYS K8055_ClearAllDigital%,1 4040 SYS K8055_ClearAllAnalog%,1 4050 SYS K8055_CloseDevice%,1 4060 SYS "FreeLibrary",K8055_Board% 4070 END 4080 ENDPROC 4090 4100 4110 DEFPROCK8055_init 4120 REM Typing errors in routine name do not generate an error message - they just hang up the program. 4130 REM These are all the system calls in the order found in the latest manual 4140 SYS"LoadLibrary","K8055D.dll" TO K8055_Board% 4150 SYS"GetProcAddress",K8055_Board%,"OpenDevice" TO K8055_OpenDevice% 4160 SYS"GetProcAddress",K8055_Board%,"CloseDevice" TO K8055_CloseDevice% 4170 SYS"GetProcAddress",K8055_Board%,"SearchDevices" TO K8055_SearchDevices% 4180 SYS"GetProcAddress",K8055_Board%,"SetCurrentDevice" TO K8055_SetCurrentDevice% 4190 SYS"GetProcAddress",K8055_Board%,"CloseDevice" TO K8055_CloseDevice% 4200 SYS"GetProcAddress",K8055_Board%,"Version" TO K8055_Version% 4210 SYS"GetProcAddress",K8055_Board%,"ReadAnalogChannel" TO K8055_ReadAnalogChannel% 4220 SYS"GetProcAddress",K8055_Board%,"ReadAllAnalog" TO K8055_ReadAllAnalog% 4230 SYS"GetProcAddress",K8055_Board%,"OutputAnalogChannel" TO K8055_OutputAnalogChannel% 4240 SYS"GetProcAddress",K8055_Board%,"OutputAllAnalog" TO K8055_OutputAllAnalog% 4250 SYS"GetProcAddress",K8055_Board%,"ClearAnalogChannel" TO K8055_ClearAnalogChannel% 4260 SYS"GetProcAddress",K8055_Board%,"ClearAllAnalog" TO K8055_ClearAllAnalog% 4270 SYS"GetProcAddress",K8055_Board%,"SetAnalogChannel" TO K8055_SetAnalogChannel% 4280 SYS"GetProcAddress",K8055_Board%,"SetAllAnalog" TO K8055_SetAllAnalog% 4290 SYS"GetProcAddress",K8055_Board%,"WriteAllDigital" TO K8055_WriteAllDigital% 4300 SYS"GetProcAddress",K8055_Board%,"ClearDigitalChannel" TO K8055_ClearDigitalChannel% 4310 SYS"GetProcAddress",K8055_Board%,"ClearAllDigital" TO K8055_ClearAllDigital% 4320 SYS"GetProcAddress",K8055_Board%,"SetDigitalChannel" TO K8055_SetDigitalChannel% 4330 SYS"GetProcAddress",K8055_Board%,"SetAllDigital" TO K8055_SetAllDigital% 4340 SYS"GetProcAddress",K8055_Board%,"ReadDigitalChannel" TO K8055_ReadDigitalChannel% 4350 SYS"GetProcAddress",K8055_Board%,"ReadAllDigital" TO K8055_ReadAllDigital% 4360 SYS"GetProcAddress",K8055_Board%,"ResetCounter" TO K8055_ResetCounter% 4370 SYS"GetProcAddress",K8055_Board%,"ReadCounter" TO K8055_ReadCounter% 4380 SYS"GetProcAddress",K8055_Board%,"SedtCounterDebouceTime" TO K8055_SetCounterDebounceTime% 4390 SYS"GetProcAddress",K8055_Board%,"ReadBackDigitalOut" TO K8055_ReadBackDigitalOut% 4400 SYS"GetProcAddress",K8055_Board%,"ReadBackAnalogOut" TO K8055_ReadBackAnalogOut% 4410 ENDPROC 4420 |
Back to the start
|