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