2009-05-31

C-Max CMMR-6P-60 to Human Readable

What's not to like about a string of zeros and ones? According to Futurama, in Robot Church, two is the same as Amen. And with that oddball thought disposed of, the next step is to provide something readable by something besides a robot.

If the bit order is correct, you can stuff a variable containing your 8-bit BCD into the input of a function like the following and have it spit back a decimal number:

This works really well if you are receiving the DCF-77 time signal code. The DCF-77 code transmits all its data in 3 to 8 bit BCD data points that start with the least significant bit and end with the most significant bit. The code merely has to parse out the section we're interested in, submit it to our "BCD to Decimal" function and we get back a meaningful number. Minute, Hour, Day, Month and Year are directly accessible.

The updated WWV/WWVH time code format is almost as easy to decode. At least the bit order is right. Minute and Hour are nearly directly accessible, they have a Zero bit between the tens and ones that will have to be dropped. The major problems are surmountable. First, there is a year tens nibble at the beginning that needs to be added to the year ones nibble at the end of the data stream. Second, if you are wanting a direct month and day readout, you will have to calculate it. US time codes transmit a "DAY OF YEAR" and a "Leapyear Flag". The "Day of Year" is 10 bits so it will have to be broken into two bytes to enable our function to decode it.

And then we get to WWVB time code format which reverses the bit order. The positions make sense if you were reading a bank of lamps as the BCD bit order follows natural decimal place order.

One method of decoding WWVB BCD time format consists of finding the double mark frame reference and then doing a bank of if statements and compound addition to decode the decimal data. The following code follows the natural decimal place order, accumulates the addition and spits out the numbers. For a simple "display UTC as it happens" it's pretty efficient as it accumulates the time and notifies you 20 seconds after the minute marker.

The next step is to reverse the bit order on the received data so that the bcdToDec() function can use the input variable contents to generate the number. Using shift left << and a shift counter that decrements from 7, we can stuff a byte variable with each decoded bit. It looks something like this for decoding minutes:

A frameReject() resets all the counters and buffers to zero and lets us start again.

Upon getting the correct bit count and frame count triggers the frameAccept() which finishes out with a shift right >> to byte align our time and date data that are less than eight bits. Once again for minutes which is seven bits, shift right one bit:

mns = bcdToDec(mns >> 1); // minutes 7 bits

One item I've noticed that never seems to get addressed is future prediction. We receive a frame reference, then the next minute is spent telling us history about that marker that happened. By the time we get a time readout, a minute has already passed. Let's fix that:

And then to do something about that annoying "Day of Year". When's the last time you've told someone to meet you at Day 168 Leapyear 1 and had them instantly recognize what you meant. Hmm, I didn't think so!

First we need to know the month end dates. This is easily contained by a multi-dimensional array with the first field representing a normal year and the second field a leapyear:

Now by knowing the "Day of Year" and the value of the leapyear flag, the following bit of code can tell us the month and day:

/* DCF77 sends month and day information, we aren't so lucky. * WWVB only sends Day of Year, Month and Day will need to be calculated, and 02/29 added for leapyear. * We're given Day of Year, compare against month ending day and calculate month and day. * Use leapyear flag to add one day to February. */ int eom = 0; // eom counter used to determine month and day while (eomYear[eom][lyr] < doy) { // calculate month and day for UTC DD = doy - eomYear[eom][lyr]; MM = (eom + 1) % 12; eom++; }

Now the rest is acdemic. Some simple formatting and printing gives Time UTC and a Month, Day, Year display. I've left all the debug code in the current copy of the sketch so you can see the different serial terminal outputs used to debug this into its final current working version. If you have a serial LCD display, it currently displays UTC time, a non-functioning signal quality indicator and the date.

// Volatile variables for clock input. Will be changed at any time as// a result of a chain of events precipitated by readlevel()volatile byte mm;volatile byte hh;volatile byte MM;volatile byte DD;volatile word YYYY;

/* DCF77 sends month and day information, we aren't so lucky. * WWVB only sends Day of Year, Month and Day will need to be calculated, * and 02/29 added for leapyear. * We're given Day of Year, compare against month ending day and calculate month and day. * Use leapyear flag to add one day to February. */ int eom = 0; // eom counter used to determine month and day while (eomYear[eom][lyr] < doy) { // calculate month and day for UTC DD = doy - eomYear[eom][lyr]; MM = (eom + 1) % 12; eom++; }