Skip to content

Instantly share code, notes, and snippets.

@Jacajack
Last active October 27, 2018 00:29
Show Gist options
  • Save Jacajack/03ead9ee02321841e33a5ac131a3bfe8 to your computer and use it in GitHub Desktop.
Save Jacajack/03ead9ee02321841e33a5ac131a3bfe8 to your computer and use it in GitHub Desktop.
DCF77 frame parser
#include "dcf.h"
#include <time.h>
#include <inttypes.h>
// 8-bit BCD decoder
static inline int dcf_decode_bcd( uint8_t bits )
{
return ( bits & 0x0f ) + 10 * ( ( bits >> 4 ) & 0x0f );
}
// Checks parity of `n' bits starting at `start'
static int dcf_parity( uint64_t frame, int start, int n )
{
int parity = 0;
uint64_t b;
for ( b = ( 1ull << start ); b != ( 1ull << ( start + n ) ); b <<= 1 )
parity ^= !!( frame & b );
return parity;
}
// Convert DCF77 frame to `struct tm`
static int dcf_parse( struct tm *t, uint64_t frame )
{
if ( t == NULL ) return 0;
// Validate frame
if ( frame & ( 1 << 0 ) ) return 0; // Start of minute - 0
if ( ~frame & ( 1ul << 20 ) ) return 0; // Start of time - 1
if ( ~( frame ^ ( frame >> 1 ) ) & ( 1ul << 17 ) ) return 0; // Exclusive CET / CEST
if ( dcf_parity( frame, 21, 8 ) ) return 0; // Minute parity
if ( dcf_parity( frame, 29, 7 ) ) return 0; // Hour parity
if ( dcf_parity( frame, 36, 23 ) ) return 0; // Date parity
// Construct time struct
t->tm_sec = 0;
t->tm_min = dcf_decode_bcd( ( frame >> 21 ) & 0x7f ); // 7 bits
t->tm_hour = dcf_decode_bcd( ( frame >> 29 ) & 0x3f ); // 6 bits
t->tm_mday = dcf_decode_bcd( ( frame >> 36 ) & 0x3f ); // 6 bits
t->tm_mon = dcf_decode_bcd( ( frame >> 45 ) & 0x1f ) - 1; // 5 bits
t->tm_year = dcf_decode_bcd( ( frame >> 50 ) & 0xff ) + 100; // 8 bits
t->tm_wday = dcf_decode_bcd( ( frame >> 42 ) & 0x07 ) - 1; // 3 bits
t->tm_yday = 0; // FIXME?
t->tm_isdst = !!( frame & ( 1ul << 17 ) ); // CEST
return 1;
}
// Enqueues an incoming data bit for parsing
int dcf_pushbit( struct tm *t, int state, int duration )
{
static uint64_t buffer = 0; // Frame buffer
static int length = 0; // Number of bits received so far
int bit = 0; // Received bit value. Ignore if `err' is set
int err = 0; // Set if impulse has invalid duration
// Determine bit value
if ( state == 0 )
{
if ( duration < 600 || duration > 1400 ) err = 1;
}
else
{
if ( duration >= 40 && duration <= 130 ) bit = 0; // 0 - 100ms
else if ( duration >= 140 && duration <= 250 ) bit = 1; // 1 - 200ms
else err = 1;
}
// Check bit error
if ( err )
{
if ( length == 59 )
{
err = dcf_parse( t, buffer );
buffer = 0;
length = 0;
return err;
}
else
{
buffer = 0;
length = 0;
return 0;
}
}
else if ( state == 1 )
{
buffer >>= 1;
buffer |= ( (uint64_t) bit << 58 );
length++;
}
return 0;
}
#ifndef DCF_H
#define DCF_H
#include <time.h>
/**
\brief Enqueues incoming data bit from DCF77 for parsing
\param t Pointer to `tm` struct that shall be written upon succesful parsing
\param state The input state (high/low)
\param duration The duration of the input state
\returns A non-zero value if a meaningful frame was parsed
*/
extern int dcf_pushbit( struct tm *t, int state, int duration );
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment