Newer
Older
TestStandRepository / Software / Arduino / libraries / Arduino-Libraries / DCF77 / DCF77.cpp
  1. /*
  2. DCF77.c - DCF77 library
  3. Copyright (c) Thijs Elenbaas 2012
  4.  
  5. This library is free software; you can redistribute it and/or
  6. modify it under the terms of the GNU Lesser General Public
  7. License as published by the Free Software Foundation; either
  8. version 2.1 of the License, or (at your option) any later version.
  9.  
  10. This library is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. Lesser General Public License for more details.
  14.  
  15. You should have received a copy of the GNU Lesser General Public
  16. License along with this library; if not, write to the Free Software
  17. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  18. 11 Apr 2012 - initial release
  19. 23 Apr 2012 - added UTC support
  20. 2 Jul 2012 - minor bugfix and additional noise rejection
  21. */
  22.  
  23. #include <DCF77.h> //https://github.com/thijse/Arduino-Libraries/downloads
  24. #include <Time.h> //http://www.arduino.cc/playground/Code/Time
  25. #include <Utils.h>
  26.  
  27. #define _DCF77_VERSION 0_9_7 // software version of this library
  28.  
  29. using namespace Utils;
  30.  
  31. /**
  32. * Constructor
  33. */
  34. DCF77::DCF77(int DCF77Pin, int DCFinterrupt, bool OnRisingFlank)
  35. {
  36. dCF77Pin = DCF77Pin;
  37. dCFinterrupt = DCFinterrupt;
  38. pulseStart = OnRisingFlank ? HIGH : LOW;
  39. if (!initialized) {
  40. pinMode(dCF77Pin, INPUT);
  41. initialize();
  42. }
  43. initialized = true;
  44. }
  45.  
  46. /**
  47. * Initialize parameters
  48. */
  49. void DCF77::initialize(void)
  50. {
  51. leadingEdge = 0;
  52. trailingEdge = 0;
  53. PreviousLeadingEdge = 0;
  54. Up = false;
  55. runningBuffer = 0;
  56. FilledBufferAvailable = false;
  57. bufferPosition = 0;
  58. flags.parityDate = 0;
  59. flags.parityFlag = 0;
  60. flags.parityHour = 0;
  61. flags.parityMin = 0;
  62. CEST = 0;
  63. }
  64.  
  65. /**
  66. * Start receiving DCF77 information
  67. */
  68. void DCF77::Start(void)
  69. {
  70. attachInterrupt(dCFinterrupt, int0handler, CHANGE);
  71. }
  72.  
  73. /**
  74. * Stop receiving DCF77 information
  75. */
  76. void DCF77::Stop(void)
  77. {
  78. detachInterrupt(dCFinterrupt);
  79. }
  80.  
  81. /**
  82. * Initialize buffer for next time update
  83. */
  84. inline void DCF77::bufferinit(void)
  85. {
  86. runningBuffer = 0;
  87. bufferPosition = 0;
  88. }
  89.  
  90. /**
  91. * Interrupt handler that processes up-down flanks into pulses and stores these in the buffer
  92. */
  93. void DCF77::int0handler() {
  94. int flankTime = millis();
  95. byte sensorValue = digitalRead(dCF77Pin);
  96.  
  97. // If flank is detected quickly after previous flank up
  98. // this will be an incorrect pulse that we shall reject
  99. if ((flankTime-PreviousLeadingEdge)<DCFRejectionTime) {
  100. LogLn("rCT");
  101. return;
  102. }
  103. // If the detected pulse is too short it will be an
  104. // incorrect pulse that we shall reject as well
  105. if ((flankTime-leadingEdge)<DCFRejectPulseWidth) {
  106. LogLn("rPW");
  107. return;
  108. }
  109. if(sensorValue==pulseStart) {
  110. if (!Up) {
  111. // Flank up
  112. leadingEdge=flankTime;
  113. Up = true;
  114. }
  115. } else {
  116. if (Up) {
  117. // Flank down
  118. trailingEdge=flankTime;
  119. int difference=trailingEdge - leadingEdge;
  120. if ((leadingEdge-PreviousLeadingEdge) > DCFSyncTime) {
  121. finalizeBuffer();
  122. }
  123. PreviousLeadingEdge = leadingEdge;
  124. // Distinguish between long and short pulses
  125. if (difference < DCFSplitTime) { appendSignal(0); } else { appendSignal(1); }
  126. Up = false;
  127. }
  128. }
  129. }
  130.  
  131. /**
  132. * Add new bit to buffer
  133. */
  134. inline void DCF77::appendSignal(unsigned char signal) {
  135. Log(signal, DEC);
  136. runningBuffer = runningBuffer | ((unsigned long long) signal << bufferPosition);
  137. bufferPosition++;
  138. if (bufferPosition > 59) {
  139. // Buffer is full before at end of time-sequence
  140. // this may be due to noise giving additional peaks
  141. LogLn("EoB");
  142. finalizeBuffer();
  143. }
  144. }
  145.  
  146. /**
  147. * Finalize filled buffer
  148. */
  149. inline void DCF77::finalizeBuffer(void) {
  150. if (bufferPosition == 59) {
  151. // Buffer is full
  152. LogLn("BF");
  153. // Prepare filled buffer and time stamp for main loop
  154. filledBuffer = runningBuffer;
  155. filledTimestamp = now();
  156. // Reset running buffer
  157. bufferinit();
  158. FilledBufferAvailable = true;
  159. } else {
  160. // Buffer is not yet full at end of time-sequence
  161. LogLn("EoM");
  162. // Reset running buffer
  163. bufferinit();
  164. }
  165. }
  166.  
  167. /**
  168. * Returns whether there is a new time update available
  169. * This functions should be called prior to getTime() function.
  170. */
  171. bool DCF77::receivedTimeUpdate(void) {
  172. // If buffer is not filled, there is no new time
  173. if(!FilledBufferAvailable) {
  174. return false;
  175. }
  176. // if buffer is filled, we will process it and see if this results in valid parity
  177. if (!processBuffer()) {
  178. LogLn("Invalid parity");
  179. return false;
  180. }
  181. // Since the received signal is error-prone, and the parity check is not very strong,
  182. // we will do some sanity checks on the time
  183. time_t processedTime = latestupdatedTime + (now() - processingTimestamp);
  184. if (processedTime<MIN_TIME || processedTime>MAX_TIME) {
  185. LogLn("Time outside of bounds");
  186. return false;
  187. }
  188.  
  189. // If received time is close to internal clock (2 min) we are satisfied
  190. time_t difference = abs(processedTime - now());
  191. if(difference < 2*SECS_PER_MIN) {
  192. LogLn("close to internal clock");
  193. storePreviousTime();
  194. return true;
  195. }
  196.  
  197. // Time can be further from internal clock for several reasons
  198. // We will check if lag from internal clock is consistent
  199. time_t shiftPrevious = (previousUpdatedTime - previousProcessingTimestamp);
  200. time_t shiftCurrent = (latestupdatedTime - processingTimestamp);
  201. time_t shiftDifference = abs(shiftCurrent-shiftPrevious);
  202. storePreviousTime();
  203. if(shiftDifference < 2*SECS_PER_MIN) {
  204. LogLn("time lag consistent");
  205. return true;
  206. } else {
  207. LogLn("time lag inconsistent");
  208. }
  209. // If lag is inconsistent, this may be because of no previous stored date
  210. // This would be resolved in a second run.
  211. return false;
  212. }
  213.  
  214. /**
  215. * Store previous time. Needed for consistency
  216. */
  217. void DCF77::storePreviousTime(void) {
  218. previousUpdatedTime = latestupdatedTime;
  219. previousProcessingTimestamp = processingTimestamp;
  220. }
  221.  
  222. /**
  223. * Calculate the parity of the time and date.
  224. */
  225. void DCF77::calculateBufferParities(void) {
  226. // Calculate Parity
  227. flags.parityFlag = 0;
  228. for(int pos=0;pos<59;pos++) {
  229. bool s = (processingBuffer >> pos) & 1;
  230. // Update the parity bits. First: Reset when minute, hour or date starts.
  231. if (pos == 21 || pos == 29 || pos == 36) {
  232. flags.parityFlag = 0;
  233. }
  234. // save the parity when the corresponding segment ends
  235. if (pos == 28) {flags.parityMin = flags.parityFlag;};
  236. if (pos == 35) {flags.parityHour = flags.parityFlag;};
  237. if (pos == 58) {flags.parityDate = flags.parityFlag;};
  238. // When we received a 1, toggle the parity flag
  239. if (s == 1) {
  240. flags.parityFlag = flags.parityFlag ^ 1;
  241. }
  242. }
  243. }
  244.  
  245. /**
  246. * Evaluates the information stored in the buffer. This is where the DCF77
  247. * signal is decoded
  248. */
  249. bool DCF77::processBuffer(void) {
  250. ///// Start interaction with interrupt driven loop /////
  251. // Copy filled buffer and timestamp from interrupt driven loop
  252. processingBuffer = filledBuffer;
  253. processingTimestamp = filledTimestamp;
  254. // Indicate that there is no filled, unprocessed buffer anymore
  255. FilledBufferAvailable = false;
  256. ///// End interaction with interrupt driven loop /////
  257.  
  258. // Calculate parities for checking buffer
  259. calculateBufferParities();
  260. tmElements_t time;
  261. bool proccessedSucces;
  262.  
  263. struct DCF77Buffer *rx_buffer;
  264. rx_buffer = (struct DCF77Buffer *)(unsigned long long)&processingBuffer;
  265.  
  266. // Check parities
  267. if (flags.parityMin == rx_buffer->P1 &&
  268. flags.parityHour == rx_buffer->P2 &&
  269. flags.parityDate == rx_buffer->P3 &&
  270. rx_buffer->CEST != rx_buffer->CET)
  271. {
  272. //convert the received buffer into time
  273. time.Second = 0;
  274. time.Minute = rx_buffer->Min-((rx_buffer->Min/16)*6);
  275. time.Hour = rx_buffer->Hour-((rx_buffer->Hour/16)*6);
  276. time.Day = rx_buffer->Day-((rx_buffer->Day/16)*6);
  277. time.Month = rx_buffer->Month-((rx_buffer->Month/16)*6);
  278. time.Year = 2000 + rx_buffer->Year-((rx_buffer->Year/16)*6) -1970;
  279. latestupdatedTime = makeTime(time);
  280. CEST = rx_buffer->CEST;
  281. //Parity correct
  282. return true;
  283. } else {
  284. //Parity incorrect
  285. return false;
  286. }
  287. }
  288.  
  289. /**
  290. * Get most recently received time
  291. * Note, this only returns an time once, until the next update
  292. */
  293. time_t DCF77::getTime(void)
  294. {
  295. if (!receivedTimeUpdate()) {
  296. return(0);
  297. } else {
  298. // Send out time, taking into account the difference between when the DCF time was received and the current time
  299. time_t currentTime =latestupdatedTime + (now() - processingTimestamp);
  300. return(currentTime);
  301. }
  302. }
  303.  
  304. /**
  305. * Get most recently received time in UTC
  306. * Note, this only returns an time once, until the next update
  307. */
  308. time_t DCF77::getUTCTime(void)
  309. {
  310. if (!receivedTimeUpdate()) {
  311. return(0);
  312. } else {
  313. // Send out time UTC time
  314. int UTCTimeDifference = (CEST ? 2 : 1)*SECS_PER_HOUR;
  315. time_t currentTime =latestupdatedTime - UTCTimeDifference + (now() - processingTimestamp);
  316. return(currentTime);
  317. }
  318. }
  319.  
  320. /**
  321. * Initialize parameters
  322. */
  323. int DCF77::dCF77Pin=0;
  324. int DCF77::dCFinterrupt=0;
  325. byte DCF77::pulseStart=HIGH;
  326.  
  327. // Parameters shared between interupt loop and main loop
  328.  
  329. volatile unsigned long long DCF77::filledBuffer = 0;
  330. volatile bool DCF77::FilledBufferAvailable= false;
  331. volatile time_t DCF77::filledTimestamp= 0;
  332.  
  333. // DCF Buffers and indicators
  334. int DCF77::bufferPosition = 0;
  335. unsigned long long DCF77::runningBuffer = 0;
  336. unsigned long long DCF77::processingBuffer = 0;
  337.  
  338. // Pulse flanks
  339. int DCF77::leadingEdge=0;
  340. int DCF77::trailingEdge=0;
  341. int DCF77::PreviousLeadingEdge=0;
  342. bool DCF77::Up= false;
  343.  
  344. // DCF77 and internal timestamps
  345. time_t DCF77::latestupdatedTime= 0;
  346. time_t DCF77::previousUpdatedTime= 0;
  347. time_t DCF77::processingTimestamp= 0;
  348. time_t DCF77::previousProcessingTimestamp=0;
  349. unsigned char DCF77::CEST=0;
  350. DCF77::ParityFlags DCF77::flags = {0,0,0,0};
  351.  
  352.