This library collects the hard part of The Clock into a reusable library. With this library you can have the same extraordinary noise tolerance for your own clock projects. In case you wonder what exactly “extraordinary noise tolerance” means read on here.
Before we go into the details have a look at the architecture overview picture. It is somewhat similar to the architecture of “The Clock”.
The differences are mainly due to the fact that I only wanted the decoder stuff in the library. Unlike other DCF77 libraries this library is not bound to any specific means of signal acquisition. Especially it is not bound to any pin at all. Instead it executes the inputprovider callback each millisecond. The input provider must be of type
typedef uint8_t (*input_provider_t)(void);. That is it must have no arguments and return either 0 or 1 depending on the current state of the DCF77 signal.
Once per second it will execute the output handler callback and pass the current time to it. The output handler if of type
typedef void (*output_handler_t)(const time_t &decoded_time);
These callbacks can be set with the functions
The input provider is mandatory the output handler is optional. If you do not set the output handler the clock will work anyway. However the output handler is very convenient in order to set a display in sync no matter what the main loop of your sketch is currently processing.
Alternatively you can retrieve the current time by calling the functions
DCF77_Clock::void get_current_time(time_t &now); // blocking till start of next second
DCF77_Clock::void read_current_time(time_t &now); // non-blocking, reads current second.
This sounds slightly complicated but it is very simple in practice. In order to illustrate the concept I included some examples with the library. You can download everything from github.
The library includes some examples to get you started.
The DCF77 Scope does not rely on the library at all. It is an analysis and troubleshooting tool. If this tool does not give reasonable results the library will not work.
Swiss Army Debug Helper
The Swiss Army Debug Helber is the advanced version of the DCF77 Scope. It relies on the library and gives you lots of internal information. If the DCF77 Scope works correctly but the debug helper does not then you most probably have compiler issue. See the Software Incompatibilities below.
The Simple Clock implements a rudimentary DCF77 clock to exhibit the basic functionality of the library.
The Clock – Library Version
The Meinberg Emulator emulates a Meinberg DCF77 Clock. I implemented it because it was easy and because there are some NTPD Meinberg Drivers out there.
The Super Filter is intended as a drop in enhancement for existing DCF77 projects. Just put the Super Filter between the DCF77 module and your existing gadget and you will have all the benefits of my library without touching your code. It basically decodes the signal and re-synthesize it. Thus you will get a 100% clean signal.
You can download the library and the examples from github.
The key to the outstandind noise tolerance of this library is the phase detection algorithm. Of course I added a stack of complex algorithms on top of it. If you understand this algorithm (or at least the implications of my analysis) you know why my library is so much better than a naive straightforward implementation.
My library will not work with a “modern” Arduino Uno. The Uno utilizes a resonator instead of a crystal. Since a resonator has a less stable clock this in turn would decrease the noise tolerance significantly. It also would require different constants for the phase lock. Since my goal is to push the noise tolerance to the limits I will offer no support for this platform. You have several options:
- get hold of an “old style” Arduino like e.g. the Blinkenlighty (which would also help to fund my blog).
- replace the Uno’s resonator with a crystal or a crystal oscialltor (requires some soldering skills)
- dig into my library and modify the time constants (possible, but gives poorer results, I neither recommend nor support this approach)
The initilization code for this library will stop timer 0 interrupts. Thus the Arduino’s millis() function will not work properly anymore. This is usually not an issue since my library is already keeping time, thus there should be no need for a redundant time keeping mechanism. If you absolutely require the Arduino’s standard time keeping you may activate it by setting TIMSK0 accordingly. However you need to understand that I never tested this setup, thus it might decrease the library decoder performance. Only reactivate this if you understand the implications of race conditions.
The library also reprograms timer2. Thus anything that relies on control of timer2 (e.g. msTimer2) will interfere with my library.
The library works fine with my Arduino 1.0 installations on Ubuntu. However for higher versions of Arduino it compiles but fails to acquire a signal lock. The nasty reason for this is that the Arduino guys bundle an outdated version of avr-gcc with the newer versions of Arduino. I have no clue why this is the case. The newer versions of gcc create significantly faster code. Since my library requires lots of computations and has only a tight time budget this matters a log. So if you consistently fail to get a lock with this library please check your compiler version. If it is before 2010 then your compiler is definitely to old.
If you are using windows then you are lucky. As Werner Gaulke pointed out there exist detailed instructions by Andy Brown on how to install a newer avr-gcc.
Some readers wondered to which pins my library maps. The answer is: none. This library was designed to be completely pin agnostic. This of course rises the question how this library learns about the signal. The answer is that YOU have to provide the code for sampling the desired input pin. An example of how to do this is in the simple clock implementation. Watch out for the function “sample_input_pin()”.
Some readers also wondered what the meanings of the constants in my examples are. Of course I described them somehwere in my blog. But if you do not want to go through all of my articles here is the list of some standard constants that I use.
If these constants do not suit your needs, just change them. The library has no implicit dependency to any pin at all. So you can use any pin as long as it is not in use for something different.
|dcf77_analog_sample_pin||5||input pin for analog sampling|
|dcf77_sample_pin||19||input pin for digital sampling, notice D19 == A5|
|dcf77_inverted_samples||1||0 or 1 depending on your DCF77 module|
|dcf77_analog_samples||1||1 => analog sampling, 0 => digital sampling, Blinkenlighty needs analog samples see comment below|
|dcf77_monitor_pin||18||displays the signal the software received, common Arduinos might want to use 13 because this is connected to the only LED|
With regard to the analog vs. digital sampling. Analog mode will work with any Arduino I know. However it consumes slightly more resources. For the Blinkenlighty it is the only admissible mode if you do not want to disconnect the LED bar. So set this to 1 for the Blinkenlighty. For all others you may want to set this to 0.
Depending on this mode either the sample pin or the analog sample pin will be evaluated. Of course these constants may point to the SAME pin. In digital mode the digial sampling pin is used in analog mode the analog sampling pin is used. The choice of the mode has absolutely no effect on the noise tolerance. In the end it only controls the threshhold between a “low” and a “high” signal.
If you are unsure about the pin mappings use the DCF77 scope with your pin mappings to verify if you got them right.