Decoding the WWVB “Atomic Clock” Signal
This project presents a method for decoding and displaying the coded time signal from WWVB.
Components and supplies
1
WWVB Atomic Clock Receiver
1
4N27 Opto-isolator
Apps and platforms
1
uno
Project description
Code
WWVB Receiver Decoder
cpp
Decodes WWVB coded time data, one character/second
1/* 2 WWVB RECEIVER DECODER ---- Reads the 1 pulse per second time code from WWVB 3 Arduino UNO - - - L. Thomas - March 2024 4 5 Input 6 senses the NOT output signal of the WWVB Receiver from Universal-Solder. 6 Output 7 controls the PDN signal to the WWVB Receiver (enable/disable) 7 Input 2 receives the 60Hz power line signal and functions as an interrupt 8 A positive going edge on the NOT output signal kicks off decoding of the signal 9 each second. Following this event, the NOT output signal is examined 21 AC cycles 10 later (350ms) to see if the signal is still HIGH (a 1) or if it is LOW (a 0). 11 If it was a 1 the signal is checked again after a total of 42 AC cycles (700ms) 12 to find out if it is still high and actually a Position Marker. 13 The green LED is turned on whenever the output signal is HIGH. 14 Uses 60Hz signal (pin 2, INT0) as interrupt for timing. 15 Displays "P" when an 800ms Position Marker is detected (6 per minute). 16 Displays "F" when 2 consecutive Position Markers are detected (1 per minute). 17 Display a "0" or "1" for BCD data detection each second. 18 Displays a "-" while waiting for a decoded result. 19*/ 20// 21int Signal = 6; // pin 6 connects to the WWVB receiver NOT output signal 22// NOTE: pin 6 also connects to S4 push-button 23int PDN = 7; // pin 7 connects to the WWVB receiver PDN pin 24int GrnLED = 3; // pin 3 is connected to the green LED 25int DataIn = 8; // pin 8 is serial in data for the shift register 26int ClkIn = 9; // pin 9 clocks data from pin 8 into the shift register 27int RegClk = 10; // pin 10 clocks byte of data to the shift register outputs 28int OutEnable = 11; // pin 11, when LOW, enables the shift register output (HIGH disables) 29int Clear = 12; // pin 12, when LOW, clears the shift register to all zeros 30int Sigtest = 0; // Used to watch for positive going edge of receiver NOT output 31int PMflag = 0; // Used to record that a Position Marker was decoded 32int Pmark = 43; // AC count in Decode to decode a 1 or a Position Marker (700ms) 33int Dmark = 22; // AC count value in Decode to decode either a 0 or a 1 (350ms) 34int Sigend = 55; // AC count limit - point to reset and decode next digit 35int Digit = 0; // Digit = 0 or 1 for BCD values, or 2 for a Position Marker 36// or 3 for a Frame Marker (2 consecutive Position Markers) 37volatile int SUBsec = 1; // Accumulates 60 AC cycles for each second of the clock 38volatile int Seconds = 0; // Accumulate seconds up to 59 then is reset to 0 39// (The above 2 are for future use of digital clock) 40volatile int Decode = 1; // Accumulates AC cycles for BCD data decoding 41// Decode is set to 1 at the positive going edge of 42// the WWVB receiver NOT output signal (on pin 6). It is 43// incremented each AC cycle until it reaches Sigend (55) 44byte Chardash = 64; // Value to display a "-" 45// 46//Contents of array below are values for display of a 0, 1, P, and F 47byte AllDigts[] = {63, 6, 115, 113}; 48// 49// The setup routine runs once during start-up or when RESET is pressed. 50// The setup commands are inside the pair of curly braces following "void setup()" 51void setup() 52{ 53 pinMode(Signal, INPUT); // Sets the WWVB NOT signal as an input (also S4) 54 pinMode(PDN, OUTPUT); // Sets the WWBV PDN control as an output 55 pinMode(2, INPUT); // Sets pin 2 as the 60Hz interrupt input 56 pinMode(DataIn, OUTPUT); // This sets pin 8 as an output 57 pinMode(ClkIn, OUTPUT); // This sets pin 9 as an output 58 pinMode(RegClk, OUTPUT); // This sets pin 10 as an output 59 pinMode(OutEnable, OUTPUT); // This sets pin 11 as an output 60 pinMode(Clear, OUTPUT); // This sets pin 12 as an output 61 pinMode(GrnLED, OUTPUT); // This sets pin 3 as an output 62// 63 digitalWrite(DataIn, LOW); // Make shift register data pin a zero (LOW) 64 digitalWrite(ClkIn, LOW); // Initialize the shift register input clock to LOW status 65 digitalWrite(RegClk, LOW); // Initialize the shift register clock pin LOW 66 digitalWrite(OutEnable, LOW); // Enable the shift register output 67 digitalWrite(Clear, HIGH); // Initialize the CLEAR pin to HIGH (not cleared) 68 digitalWrite(PDN, HIGH); // Initialize the PDN signal as HIGH (Receiver off) 69 delay (250); // Delay a bit 70 digitalWrite(PDN, LOW); // Now let the WWVB receiver operate - PDN is LOW 71 attachInterrupt(0, Update, RISING); //Enable the interrupt now 72} 73// Now that the setup has been done, the main loop is started 74// **** MAIN LOOP ********************************************************************* 75void loop() { 76if (Decode >= Dmark) //Display a "-" during decode time 77{ // otherwise show value of Digit 78 shiftOut(DataIn, ClkIn, LSBFIRST, Chardash); //This loads a "-" into the shift register 79 DisplayON(); //then displays it 80} 81 else //Display number or letter as per Digit 82// This shows what was just decoded 83{ // in the previous second 84 shiftOut(DataIn, ClkIn, LSBFIRST, AllDigts[Digit]); //This loads a digit from 85 DisplayON(); // the array into the shift register 86} // and displays it 87// 88// The following catches the positive going edge of the WWBV receiver NOT output 89 if (digitalRead(Signal) == HIGH) //Watch for positive going edge of Signal 90{ 91 Sigtest = 1; //It is HIGH so make Sigtest = 1 and 92 digitalWrite(GrnLED, HIGH); //Turn on the green LED 93} 94 else 95{ 96 Sigtest = 0; //It is LOW so make Sigtest = 0 and 97 digitalWrite(GrnLED, LOW); //Turn off the green LED 98} 99 if (Sigtest == 0 && Decode >= Sigend) //Signal now returned to LOW. At end of testing? 100 Decode = 0; //Yes, Reset for next digit test 101 if (Sigtest == 1 && Decode == 0) //Found a positive going edge? 102{ 103 Decode = 1; //Yes, kick off reading of a new digit 104 // and display what we just decoded 105 shiftOut(DataIn, ClkIn, LSBFIRST, AllDigts[Digit]); //This loads a digit from 106 DisplayON(); // the array into the shift register 107} // and displays it 108} //END OF LOOP 109// ************************************************************************************ 110void DisplayON() //This function activates the display 111{ 112 digitalWrite(RegClk, HIGH); //Clock the data to the output register 113 digitalWrite(RegClk, LOW); //Put Clock signal back low - creating a pulse 114 digitalWrite(ClkIn, LOW); //Be sure clock is initialized to LOW status 115} 116// 117// Interrupt Service - Accumulates AC cycles ***************************************** 118 void Update (void) //This is executed once each 60Hz AC cycle 119{ 120 Decode = Decode + 1; //Add 1 AC cycle to Decode counter 121 SUBsec = SUBsec + 1; //Add 1 AC cycle to the sub-seconds counter 122// Clock update: (for future use) 123 if (SUBsec > 59) //Do we have a new second? 124{ 125 SUBsec = 0; //Yes-so reset for another 126 Seconds = Seconds + 1; // and increment the seconds 127} 128 if (Seconds > 59) //Limit Seconds to 59 then reset 129 Seconds = 0; 130// WWVB Decode update: 131 if (Decode == 1) //Don't increment Decode if it was at 0 and now 1 132 Decode = 0; //Put it back to zero (wait for new signal edge) 133 if (Decode == Dmark && Sigtest == 1) //Time to check - Is it a 1? (22) 134 Digit = 1; //HIGH makes it a 1 135 if (Decode == Dmark && Sigtest == 0) //Time to check - Is it a 0? (22) 136{ 137 Digit = 0; //LOW makes it a 0 138 PMflag = 0; //Can't be a Position Marker so reset this 139} 140 if (Decode == Pmark && PMflag == 1 && Sigtest == 1) 141// Check PMflag for a 2nd Position Marker 142 Digit = 3; //Yes, so it is a Frame Marker 143 if (Decode == Pmark && Digit == 1 && Sigtest == 1) 144// Check for a first Position Marker? 145// Digit will not change if Signal is LOW 146{ 147 Digit = 2; //Still HIGH so we have a Position Marker 148 PMflag = 1; //And record this event (might get 2 in a row) 149}} 150// End of Interrupt Service: *********************************************************
Downloadable files
Video of sketch running.
Shows a few seconds of data at start of a minute.
WWVB data.mp4
Documentation
Schematics for 60Hz timing signal & WWVB Receiver interfacing
Shows interface wiring to Arduino UNO
Schematics-2.pdf
Schematic for adding WWVB Receiver to Experimental & Learning Shield
Uses Experimental & Learning Shield on Project Hub
ExperimentalShield_WWVB.pdf
Comments
Only logged in users can leave comments