aboutsummaryrefslogtreecommitdiffstats
path: root/dcf77_gps.lua
blob: dad17f137e91cb616688edc6f43616ca728e0387 (plain)
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
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
dcf77_pin=6
led_pin=4

min_ambient_light=25

last_valid_rise=0

valid_rise_min=990000
valid_rise_max=1010000
valid_timeout=5000000

valid_newmin_min=1990000
valid_newmin_max=2100000

valid_0_min=100000
valid_0_max=140000
valid_1_min=200000
valid_1_max=250000

dcfdata=""

valid_times=0
total_times=0

bcd={}
bcd[1]=1
bcd[2]=2
bcd[3]=4
bcd[4]=8
bcd[5]=10
bcd[6]=20
bcd[7]=40
bcd[8]=80

dow={}
dow[0]="Invalid"
dow[1]="Mon"
dow[2]="Tue"
dow[3]="Wed"
dow[4]="Thu"
dow[5]="Fri"
dow[6]="Sat"
dow[7]="Sun"

mon_days={}
mon_days[1]=31
mon_days[2]=28
mon_days[3]=31
mon_days[4]=30
mon_days[5]=31
mon_days[6]=30
mon_days[7]=31
mon_days[8]=31
mon_days[9]=30
mon_days[10]=31
mon_days[11]=30
mon_days[12]=31

function bcd2dec(data)
 local value=0
 for count=1,string.len(data),1
 do
  if string.sub(data,count,count)=="1" and bcd[count]~=nil then
   value=value+bcd[count]
  end
 end
 return value
end

function check_parity(data)
 local onecount=0
 for count=1,string.len(data),1
 do
  if string.sub(data,count,count)=="1" then
   onecount=onecount+1
  end
 end
 if onecount%2==0 then
  return true
 end
 return false
end

gps_sec=0
gps_valid=false
gps_part1=""
gps_part2=""
gps_part_checksum=0

hex={}
hex[0]="0"
hex[1]="1"
hex[2]="2"
hex[3]="3"
hex[4]="4"
hex[5]="5"
hex[6]="6"
hex[7]="7"
hex[8]="8"
hex[9]="9"
hex[10]="A"
hex[11]="B"
hex[12]="C"
hex[13]="D"
hex[14]="E"
hex[15]="F"

function send_gpsstring()
 local gps_data
 local gps_checksum=gps_part_checksum
 local part1len=string.len(gps_part1)

 if gps_sec<10 then
  gps_data=gps_part1.."0"..gps_sec..gps_part2
 else
  gps_data=gps_part1..gps_sec..gps_part2
 end

 gps_checksum=bit.bxor(gps_checksum,string.byte(string.sub(gps_data,part1len+1,part1len+1)))
 gps_checksum=bit.bxor(gps_checksum,string.byte(string.sub(gps_data,part1len+2,part1len+2)))

 local digit1=bit.rshift(gps_checksum,4)
 local digit2=bit.band(gps_checksum,15)

 print("$"..gps_data.."*"..hex[digit1]..hex[digit2].."\r")
end

function parse_data(data)
 total_times=total_times+1
 --print("-------------------------------------------------------------")
 --print(data)
 --print("Resrv: ",string.sub(data,1,15))
 --print("Info: ",string.sub(data,16,21))
 --print("Min: ",string.sub(data,22,29),bcd2dec(string.sub(data,22,28)),check_parity(string.sub(data,22,29)))
 --print("Hour: ",string.sub(data,30,36).." ",bcd2dec(string.sub(data,30,35)),check_parity(string.sub(data,30,36)))
 --print("Day: ",string.sub(data,37,42).."  ",bcd2dec(string.sub(data,37,42)))
 --print("DOW: ",string.sub(data,43,45).."     ",bcd2dec(string.sub(data,43,45)))
 --print("Month: ",string.sub(data,46,50).."    ",bcd2dec(string.sub(data,46,50)))
 --print("Year: ",string.sub(data,51,59),bcd2dec(string.sub(data,51,59)),check_parity(string.sub(data,37,59)))
 --print("Rest: ",string.sub(data,60))
 --if string.len(data)==59 then
 -- print("Time/date : "..bcd2dec(string.sub(data,30,35))..":"..bcd2dec(string.sub(data,22,28)).." "..dow[bcd2dec(string.sub(data,43,45))].." 20"..bcd2dec(string.sub(data,51,59)).."-"..bcd2dec(string.sub(data,46,50)).."-"..bcd2dec(string.sub(data,37,42)))
 --end

 gps_valid=false

 local min
 local hour
 local day
 local mon
 local year

 if data==nil then
  --print("No data received")
  return
 end

 if string.len(data)~=59 then
  --print("Data length invalid")
  return
 end
 --print("Data length valid")

 if string.sub(data,18,18)==string.sub(data,19,19) then
  --print("Timezone data is invalid")
  return
 end
 --print("Timezone data is valid")

 if string.sub(data,21,21)=="0" then
  --print("Start time bit is invalid")
  return
 end
 --print("Start time bit is valid")

 if not check_parity(string.sub(data,22,29)) then
  --print("Minute parity invalid")
  return
 end
 --print("Minute parity valid")
 min=bcd2dec(string.sub(data,22,28))
 if min>59 then
  --print("Minute value invalid")
  return
 end
 --print("Minute value valid")

 if not check_parity(string.sub(data,30,36)) then
  --print("Hour parity invalid")
  return
 end
 --print("Hour parity valid")
 hour=bcd2dec(string.sub(data,30,35))
 if hour>23 then
  --print("Hour valid invalid")
  return
 end
 --print("Hour valid valid")

 if not check_parity(string.sub(data,37,59)) then
  --print("Date parity invalid")
  return
 end
 --print("Date parity valid")

 day=bcd2dec(string.sub(data,37,42))
 if day<1 or day>31 then
  --print("Day value invalid")
  return
 end
 --print("Day valud valid")

 mon=bcd2dec(string.sub(data,46,50))
 if mon<1 or mon>12 then
  --print("Month value invalid")
  return
 end
 --print("Month value valid")

 year=bcd2dec(string.sub(data,51,59))
 if year<22 or year>30 then
  --print("Year value invalid")
  return
 end
 --print("Year value valid")

 valid_times=valid_times+1
 --print("Total "..valid_times.." valid times and "..total_times-valid_times.." invalid times")

 if string.sub(data,19,19)=="1" then
  --print("Subtracting 1 hour for CET timezone")
  hour=hour-1
 else
  --print("Subtracting 2 hours for CEST timezone")
  hour=hour-2
 end
 if hour<0 then
  hour=hour+24
  day=day-1
  if day<1 then
   mon=mon-1
   if mon<1 then
    mon=12
    year=year-1
   end
   if mon==2 and year%4==0 then
    day=day+1
   end
   day=mon_days[mon]
  end
 end
 gps_part1=string.format("GPZDA,%02d%02d",hour,min)
 gps_part2=string.format(".000,%02d,%02d,20%02d,,",day,mon,year)

 local gps_data=gps_part1..gps_part2
 gps_part_checksum=0

 for count=1,string.len(gps_data)
 do
  gps_part_checksum=bit.bxor(gps_part_checksum,string.byte(string.sub(gps_data,count,count)))
 end

 gps_sec=0
 gps_valid=true
 send_gpsstring()
end

function irq_handler(level,when,count)
 if count>10 then
  --print("Waiting for stable signal, changing to polling")
  start_poll()
  return
 end
 if when<last_valid_rise then
  delta=(2147483647-last_valid_rise)+when
 else
  delta=when-last_valid_rise
 end
 if level==1 then
  --print("Rise delta: "..delta)
  if last_valid_rise==0 then
   last_valid_rise=when
   return
  end
  if delta>valid_newmin_min and delta<valid_newmin_max then
   parse_data(dcfdata)
   dcfdata=""
   last_valid_rise=when
   return
  end
  if delta>valid_rise_min and delta<valid_rise_max then
   --print("valid")
   last_valid_rise=when
   if gps_valid==true then
    gps_sec=gps_sec+1
    if gps_sec<60 then
     send_gpsstring()
     if adc.read(0)>min_ambient_light then
      gpio.write(led_pin,gpio.LOW)
     end
    else
     gps_valid=false
     gpio.write(led_pin,gpio.HIGH)
    end
   end
   return
  end
  if delta>valid_timeout then
   --print("Valid timeout")
   last_valid_rise=0
   return
  end
 end
 if level==0 then
  --print("Pulse length: "..delta)
  if delta>valid_0_min and delta<valid_0_max then
   --print("0")
   dcfdata=dcfdata.."0"
   gpio.write(led_pin,gpio.HIGH)
  end
  if delta>valid_1_min and delta<valid_1_max then
   --print("1")
   dcfdata=dcfdata.."1"
   gpio.write(led_pin,gpio.HIGH)
  end
  if string.len(dcfdata)>59 then
   dcfdata=string.sub(dcfdata,2)
  end
 end
end

function irq_dummy(level,when,count)
 --print("Dummy IRQ handler")
 return
end

poll_prev_value=0
poll_value_count={}
poll_value_count[0]=0
poll_value_count[1]=0
poll_clean=0

function poll_handler()
 local value
 value=gpio.read(dcf77_pin)
 if value==nil then
  poll_timer:start()
 end
 if value==1 and poll_prev_value==0 then
  --print(poll_value_count[0],poll_value_count[1])
  if poll_value_count[0]>70 and poll_value_count[0]<100 and poll_value_count[1]>6 and poll_value_count[1]<30 then
   poll_clean=poll_clean+1
   --print(poll_clean.." clean pulses")
   if poll_clean>10 then
    poll_clean=0
    --print("Clean signal, swithcing to IRQ handling")
    start_irq()
    return
   end
  else
   poll_clean=0
  end
  poll_value_count[0]=0
  poll_value_count[1]=0
 end
 poll_prev_value=value
 poll_value_count[value]=poll_value_count[value]+1
 poll_timer:start()
end

poll_timer=tmr.create()
poll_timer:register(10, tmr.ALARM_SEMI,poll_handler)

function start_poll()
 gpio.write(led_pin,gpio.HIGH)
 gpio.trig(dcf77_pin,"none",irq_dummy)
 gpio.mode(dcf77_pin,gpio.INPUT)
 poll_timer:start()
end

function start_irq()
 gpio.mode(led_pin,gpio.OUTPUT)
 gpio.write(led_pin,gpio.HIGH)
 gps_valid=false
 gpio.trig(dcf77_pin,"both",irq_handler)
 gpio.mode(dcf77_pin,gpio.INT)
end

function start_dcf77()
 start_irq()
end

function stop_dcf77()
 gpio.mode(dcf77_pin,gpio.INPUT)
 poll_timer:stop()
 gpio.write(led_pin,gpio.HIGH)
end