2009-05-25

Decoding WWVB Time Data -> Data Stream

Now that we've got a 5V TTL signal being output on the TCO pin, we can start seeing if the Arduino can make any sense of it. Somehow, the Arduino needs to be instructed to look for an edge on a square wave signal, and preferrably for our convenience, a rising edge. It also would be nice if the microprocessor were free to do something else while waiting around.

Our first WWVB sketch will make use of an external interrupt using the attachInterrupt() function. Digital Pin 2 is used by Interrupt 0 so we'll use this for our signal input pin.

attachInterrupt(0, readPulse, RISING); // attach interupt handler to INT0

This will trigger an interrupt handler when the rising edge of the signal is detected. Our interrupt handler is the readPulse() function. How this is supposed to work; the loop() sits there running its operations, and somewhere during these operations, the voltage rises on Pin 2. The ISR takes over and says, "Steady on, we have an important message, wait while I handle it, I need to measure the length of this pulse." readPulse() uses pulseIn() to time how long the pulse remains high on Pin 2 in microseconds.

pulseWidth = pulseIn(wwvbPin, HIGH); // record duration of pulse

While this might work just fine for intermittent pulses, the Arduino seems to go into a wait state while timing the pulse duration, not a very efficient use of clock cycles. It actually seems to block loop() from ever executing. A better method would be an interrupt driven routine that would detect a rising edge and the time it occured, then detect a falling edge and calculate the difference to report how long the pulse was. Quite a few clock cycles would be freed on the microprocessor for other things. Another issue for another day (plus that's already been done in the DCF-77 code).

Now that our sketch is calculating pulse lengths, a series of matches can figure out whether we've got noise, ones, zeros or marks. View the output on the Arduino IDE serial terminal screen. On to the code!

Clock WWVB Data Decode - External Interrupt - pulseIn()
/******************************************************************************
* Clock WWVB Data Decode - External Interrupt - pulseIn() by capt.tagon
*
* WWVB receiver input on digital pin 2
******************************************************************************/

#define wwvbPin 2 // WWVB receiver data input signal pin
#define ledMarkPin 7 // Data received mark inicator pin
#define ledFramePin 6 // Data received frame indicator pin
#define ledBitPin 5 // LED data decoded indicator pin

//#define DEBUG

/* received pulse width 200ms = 1, 500ms = 0, 800ms = markers and frame start */
/* volatile variables, changed by interrupt handler */
volatile unsigned long pulseWidth;


int bitVal = 0; // decoded bit
int badBit = 0; // noise decoded
int bitReady = 0; // bit waiting
int prevMark = 0; // store previous mark bit

void setup() {
pinMode(wwvbPin, INPUT);
pinMode(ledMarkPin, OUTPUT);
pinMode(ledFramePin, OUTPUT);
pinMode(ledBitPin, OUTPUT);
attachInterrupt(0, readPulse, RISING); // attach interupt handler to INT0
Serial.begin(9600);
}

void loop() {
Serial.println("NOP"); // test for interrupt handler release
delay(200);
}

/***********************************************************************
* readPulse()
*
* Pin 2 INT0 Interrupt Handler Reads pin state
************************************************************************/

void readPulse() {
pulseWidth = pulseIn(wwvbPin, HIGH); // record duration of pulse

// Once started, the interrupt handler never seems to release any time
// to loop() to run anything. Prove it by commenting out the following
// line. You should see NOP printed to the terminal. NOP only prints
// on reset. Worse than polling to decode, pulseIn() IS only useful for
// intermittent pulse duration measurement.

pulseValue();
}


/******************************************************************
* pulseValue()
*
* determine pulse width 200ms = 0, 500ms = 1, 800ms = mark
* output delimited data stream with colons at mark and new line at
* at frame start.
******************************************************************/

void pulseValue() {

badBit = 0;

if (pulseWidth > 500000) { // carrier dropout - no signal
bitVal = 5;
badBit = 1;
}
else if (pulseWidth > 310000) { // carrier 500 ms drop bit = 0
bitVal = 0;
bitReady = 1;
}
else if (pulseWidth > 135000) { // carrier 200 ms drop bit = 1
bitVal = 1;
bitReady = 1;
}
else if (pulseWidth > 115000) { // carrier 800 ms drop 1 = 10 sec, 2 = frame start
bitVal = 2;
bitReady = 1;
}
else { // spikes/noise
bitVal = 7;
badBit = 1;
}

#ifdef DEBUG
if (bitVal != 7) {
Serial.print(bitVal, DEC);
Serial.print(" : ");
Serial.print(badBit, DEC);
Serial.print(" : ");
Serial.print(bitReady, DEC);
Serial.print(" :");
Serial.println(pulseWidth, DEC);
}
#endif
if (bitVal != 7) {
printBitVal();
}
}

void printBitVal() {
if ((bitVal == 2) && (prevMark == 0)) {
Serial.print(" : ");
digitalWrite(ledMarkPin, HIGH);
prevMark = 1;
}
else if ((bitVal == 2) && (prevMark == 1)) {
Serial.print("\nBit Value: ");
Serial.print("| ");
digitalWrite(ledFramePin, HIGH);
prevMark = 0;
}
else {
Serial.print(bitVal);
digitalWrite(ledMarkPin, LOW);
digitalWrite(ledFramePin, LOW);
if (bitVal == 0) {
digitalWrite(ledBitPin, LOW);
}
if (bitVal == 1) {
digitalWrite(ledBitPin, HIGH);
}
prevMark = 0;
}
bitReady = 0;
}


And here's some WWVB data output. Time is streaming in, but not in a readily readable format, eh? Also notice that three of the lines had a noise problem.

Bit Value: | 01100111 : 000000100 : 000100100 : 011000101 : 001100000 : 100100011 : 
Bit Value: | 01101000 : 000000100 : 000100100 : 011000101 : 001100000 : 100100011 :
Bit Value: | 01101001 : 000000100 : 000100100 : 011000101 : 001100000 : 100100011 :
Bit Value: | 10000000 : 0000001001000100100 : 011000101 : 001100000 : 100100011 :
Bit Value: | 10000001 : 000000100 : 000100100 : 011000101 : 001100000 : 100100011 :
Bit Value: | 10000010 : 000000100 : 000100100 : 011000101 : 001100000 : 100100011 :
Bit Value: | 10000011 : 000000100 : 000100100 : 011000101 : 0001000001100000011 :
Bit Value: | 10000100 : 000000100 : 000100100 : 0110001011001100000 : 100000011 :
Bit Value: | 10000101 : 000000100 : 000100100 : 011000101 : 001100000 : 100100011 :
Bit Value: | 10000110 : 000000100 : 000100100 : 011000101 : 001100000 : 100100011 :

No comments:

Post a Comment