1# awk program to scan clockstat files and report errors/statistics
2#
3# usage: awk -f check.awk clockstats
4#
5# This program works for the following radios:
6# PST/Traconex 1020 WWV reciever
7# Arbiter 1088 GPS receiver
8# Spectracom 8170/Netclock-2 WWVB receiver
9# IRIG audio decoder
10# Austron 2200A/2201A GPS receiver (see README.austron file)
11#
12BEGIN {
13          etf_min = osc_vmin = osc_tmin = 1e9
14          etf_max = osc_vmax = osc_tmax = -1e9
15}
16#
17# scan all records in file
18#
19{
20          #
21          # select PST/Traconex WWV records
22          # 00:00:37.234  96/07/08/190 O6@0:5281825C07510394
23          #
24          if (NF >= 4 && $3 == "127.127.3.1") {
25                    if (substr($6, 14, 4) > "0010")
26                              wwv_sync++
27                    if (substr($6, 13, 1) == "C")
28                              wwv_wwv++
29                    if (substr($6, 13, 1) == "H")
30                              wwv_wwvh++
31                    x = substr($6, 12, 1)
32                    if (x == "1")
33                              wwv_2.5++
34                    else if (x == "2")
35                              wwv_5++
36                    else if (x == "3")
37                              wwv_10++
38                    else if (x == "4")
39                              wwv_15++
40                    else if (x == "5")
41                              wwv_20++
42                    continue
43          }
44          #
45          # select Arbiter GPS records
46          # 96 190 00:00:37.000 0 V=08 S=44 T=3 P=10.6 E=00
47          # N39:42:00.951 W075:46:54.880 210.55      2.50 0.00
48          #
49          if (NF >= 4 && $3 == "127.127.11.1") {
50                    if (NF > 8) {
51                              arb_count++
52                              if ($7 != 0)
53                                        arb_sync++
54                              x = substr($10, 3, 1)
55                              if (x == "0")
56                                        arb_0++
57                              else if (x == "1")
58                                        arb_1++
59                              else if (x == "2")
60                                        arb_2++
61                              else if (x == "3")
62                                        arb_3++
63                              else if (x == "4")
64                                        arb_4++
65                              else if (x == "5")
66                                        arb_5++
67                              else if (x == "6")
68                              arb_6++
69                    } else if (NF == 8) {
70                              arbn++
71                              arb_mean += $7
72                              arb_rms += $7 * $7
73                              if (arbn > 0) {
74                                        x = $7 - arb_val
75                                        arb_var += x * x
76                              }
77                              arb_val = $7
78                    }
79                    continue
80          }
81          #
82          # select Spectracom WWVB records
83          # see summary for decode
84          #   96 189 23:59:32.248  D
85          #
86          if (NF >= 4 && $3 == "127.127.4.1") {
87                    if ($4 == "SIGNAL" || NF > 7)
88                              printf "%s\n", $0
89                    else {
90                              wwvb_count++
91                              if ($4 ~ /\?/)
92                                        wwvb_x++
93                              else if ($4 ~ /A/)
94                                        wwvb_a++
95                              else if ($4 ~ /B/)
96                                        wwvb_b++
97                              else if ($4 ~ /C/)
98                                        wwvb_c++
99                              else if ($4 ~ /D/)
100                                        wwvb_d++
101                    }
102                    continue
103          }
104          #
105          # select IRIG audio decoder records
106          # see summary for decode
107          #
108          if (NF >= 4 && $3 == "127.127.6.0") {
109                    irig_count++
110                    if ($5 ~ /\?/)
111                              irig_error++
112                    continue
113          }
114          #
115          # select Austron GPS LORAN ENSEMBLE records
116          # see summary for decode
117          #
118          else if (NF >= 13 && $6 == "ENSEMBLE") {
119                    ensemble_count++
120                    if ($9 <= 0)
121                              ensemble_badgps++
122                    else if ($12 <= 0)
123                              ensemble_badloran++
124                    else {
125                              if ($13 > 200e-9 || $13 < -200e-9)
126                                        ensemble_200++
127                              else if ($13 > 100e-9 || $13 < -100e-9)
128                                        ensemble_100++
129                              ensemble_mean += $13
130                              ensemble_rms += $13 * $13
131                    }
132                    continue
133          }
134          #
135          # select Austron LORAN TDATA records
136          # see summary for decode; note that signal quality log is simply
137          # copied to output
138          #
139          else if (NF >= 7 && $6 == "TDATA") {
140                tdata_count++
141                for (i = 7; i < NF; i++) {
142                        if ($i == "M" && $(i+1) == "OK") {
143                                i += 5
144                                m += $i
145                              tdata_m++
146                            }
147                        else if ($i == "W" && $(i+1) == "OK") {
148                                i += 5
149                                w += $i
150                              tdata_w++
151                              }
152                        else if ($i == "X" && $(i+1) == "OK") {
153                                i += 5
154                                x += $i
155                              tdata_x++
156                              }
157                        else if ($i == "Y" && $(i+1) == "OK") {
158                                i += 5
159                                y += $i
160                              tdata_y++
161                              }
162                        else if ($i == "Z" && $(i+1) == "OK") {
163                                i += 5
164                                z += $i
165                              tdata_z++
166                              }
167                    }
168                    continue
169          }
170          #
171          # select Austron ITF records
172          # see summary for decode
173          #
174          else if (NF >= 13 && $5 == "ITF" && $12 >= 100) {
175                    itf_count++
176                    if ($9 > 200e-9 || $9 < -200e-9)
177                              itf_200++
178                    else if ($9 > 100e-9 || $9 < -100e-9)
179                              itf_100++
180                    itf_mean += $9
181                    itf_rms += $9 * $9
182                    itf_var += $10 * $10
183                    continue
184          }
185          #
186          # select Austron ETF records
187          # see summary for decode
188          #
189          else if (NF >= 13 && $5 == "ETF" && $13 >= 100) {
190                    etf_count++
191                    if ($6 > etf_max)
192                              etf_max = $6
193                    else if ($6 < etf_min)
194                              etf_min = $6
195                    etf_mean += $6
196                    etf_rms += $6 * $6
197                    etf_var += $9 * $9
198                    continue
199          }
200          #
201          # select Austron TRSTAT records
202          # see summary for decode
203          #
204          else if (NF >= 5 && $5 == "TRSTAT") {
205                    trstat_count++
206                    j = 0
207                    for (i = 6; i <= NF; i++)
208                              if ($i == "T")
209                                        j++
210                    trstat_sat[j]++
211                    continue
212          }
213          #
214          # select Austron ID;OPT;VER records
215          #
216          # config GPS 2201A TTY1 TC1 LORAN IN OUT1 B.00 B.00 28-Apr-93
217          #
218          # GPS 2201A         receiver model
219          # TTY1              rs232 moduel
220          # TC1               IRIG module
221          # LORAN             LORAN assist module
222          # IN                input module
223          # OUT1              output module
224          # B.00 B.00         firmware revision
225          # 28-Apr-9          firmware date3
226        #
227          else if (NF >= 5 && $5 == "ID;OPT;VER") {
228                    id_count++
229                    id_temp = ""
230                    for (i = 6; i <= NF; i++)
231                              id_temp = id_temp " " $i
232                    if (id_string != id_temp)
233                              printf "config%s\n", id_temp
234                    id_string = id_temp
235                    continue
236          }
237          #
238          # select Austron POS;PPS;PPSOFF records
239          #
240          # position +39:40:48.425 -075:45:02.392 +74.09 Stored UTC 0 200 0
241          #
242          # +39:40:48.425     position north latitude
243          # -075:45:02.392 position east longitude
244          # +74.09  elevation (meters)
245          # Stored  position is stored
246          # UTC               time is relative to UTC
247          # 0 200 0 PPS offsets
248          #
249          else if (NF >= 5 && $5 == "POS;PPS;PPSOFF") {
250                    pos_count++
251                    pos_temp = ""
252                    for (i = 6; i <= NF; i++)
253                              pos_temp = pos_temp " " $i
254                    if (pos_string != pos_temp)
255                              printf "position%s\n", pos_temp
256                    pos_string = pos_temp
257          continue
258          }
259          #
260          # select Austron OSC;ET;TEMP records
261          #
262          # loop 1121 Software Control Locked
263          #
264          # 1121              oscillator type
265          # Software Control loop is under software control
266          # Locked  loop is locked
267          #
268          else if (NF >= 5 && $5 == "OSC;ET;TEMP") {
269                    osc_count++
270                    osc_temp = $6 " " $7 " " $8 " " $9
271                    if (osc_status != osc_temp)
272                              printf "loop %s\n", osc_temp
273                    osc_status = osc_temp
274                    if ($10 > osc_vmax)
275                              osc_vmax = $10
276                    if ($10 < osc_vmin)
277                              osc_vmin = $10
278                    if ($11 > osc_tmax)
279                              osc_tmax = $11
280                    if ($11 < osc_tmin)
281                              osc_tmin = $11
282          continue
283          }
284          #
285          # select Austron UTC records
286          # these ain't ready yet
287          #
288          else if (NF >= 5 && $5 == "UTC") {
289                    utc_count++
290                    utc_temp = ""
291                    for (i = 6; i <= NF; i++)
292                              utc_temp = utc_temp " " $i
293                    if (utc_string != utc_temp)
294#                             printf "utc%s\n", utc_temp
295                utc_string = utc_temp
296          continue
297          }
298} END {
299#
300# PST/Traconex WWV summary data
301#
302          if (wwv_wwv + wwv_wwvh > 0)
303                    printf "wwv %d, wwvh %d, err %d, MHz (2.5) %d, (5) %d, (10) %d, (15) %d, (20) %d\n", wwv_wwv, wwv_wwvh, wwv_sync, wwv_2.5, wwv_5, wwv_10, wwv_15, wwv_20
304#
305# Arbiter 1088 summary data
306#
307# gps               record count
308# err               error count
309# sats(0-6)         satellites tracked
310# mean              1 PPS mean (us)
311# rms               1 PPS rms error (us)
312# var               1 PPS Allan variance
313#
314          if (arb_count > 0) {
315                    printf "gps %d, err %d, sats(0-6) %d %d %d %d %d %d %d", arb_count, arb_sync, arb_0, arb_1, arb_2, arb_3, arb_4, arb_5, arb_6
316                    if (arbn > 1) {
317                              arb_mean /= arbn
318                              arb_rms = sqrt(arb_rms / arbn - arb_mean * arb_mean)
319                              arb_var = sqrt(arb_var / (2 * (arbn - 1)))
320                              printf ", mean %.2f, rms %.2f, var %.2e\n", arb_mean, arb_rms, arb_var * 1e-6
321                    } else {
322                              printf "\n"
323                    }
324          }
325#
326# ensemble summary data
327#
328# ensemble          record count
329# badgps  gps data unavailable
330# badloran          loran data unavailable
331# rms               ensemble rms error (ns)
332# >200              ensemble error >200 ns
333# >100              100 ns < ensemble error < 200 ns
334#
335          if (ensemble_count > 0) {
336                    ensemble_mean /= ensemble_count
337                    ensemble_rms = sqrt(ensemble_rms / ensemble_count - ensemble_mean * ensemble_mean) * 1e9
338                    printf "ensemble %d, badgps %d, badloran %d, rms %.1f, >200 %d, >100 %d\n", ensemble_count, ensemble_badgps, ensemble_badloran, ensemble_rms, ensemble_200, ensemble_100
339          }
340#
341# wwvb summary data
342#
343# wwvb              record count
344# ?                 unsynchronized
345# >1                error > 1 ms
346# >10               error > 10 ms
347# >100              error > 100 ms
348# >500              error > 500 ms
349#
350          if (wwvb_count > 0)
351                    printf "wwvb %d, ? %d, >1 %d, >10 %d, >100 %d, >500 %d\n", wwvb_count, wwvb_x, wwvb_a, wwvb_b, wwvb_c, wwvb_d
352#
353# irig summary data
354#
355# irig              record count
356# err               error count
357#
358          if (irig_count > 0)
359                    printf "irig %d, err %d\n", irig_count, irig_error
360#
361# tdata summary data
362#
363# tdata             record count
364# m                 M master OK-count, mean level (dB)
365# w                 W slave OK-count, mean level (dB)
366# x                 X slave OK-count, mean level (dB)
367# y                 Y slave OK-count, mean level (dB)
368# z                 Z slave OK-count, mean level (dB)
369#
370          if (tdata_count > 0 ) {
371                    if (tdata_m > 0)
372                              m /= tdata_count
373                    if (tdata_x > 0)
374                              w /= tdata_count
375                    if (tdata_x > 0)
376                              x /= tdata_count
377                    if (tdata_y > 0)
378                              y /= tdata_count
379                    if (tdata_z > 0)
380                              z /= tdata_count
381                    printf "tdata %d, m %d %.1f, w %d %.1f, x %d %.1f, y %d %.1f, z %d %.1f\n", tdata_count, tdata_m, m, tdata_w, w, tdata_x, x, tdata_y, y, tdata_z, z
382          }
383#
384# itf summary data
385#
386# itf               record count
387# rms               itf rms error (ns)
388# >200              itf error > 200 ns
389# >100              itf error > 100 ns
390# var               Allan variance
391#
392          if (itf_count > 1) {
393                    itf_mean /= itf_count
394                    itf_rms = sqrt(itf_rms / itf_count - itf_mean * itf_mean) * 1e9
395                    itf_var = sqrt(itf_var / (2 * (itf_count - 1)))
396                    printf "itf %d, rms %.1f, >200 %d, >100 %d, var %.2e\n", itf_count, itf_rms, itf_200, itf_100, itf_var
397          }
398#
399# etf summary data
400#
401# etf               record count
402# mean              etf mean (ns)
403# rms               etf rms error (ns)
404# max               etf maximum (ns)
405# min               etf minimum (ns)
406# var               Allan variance
407#
408          if (etf_count > 0) {
409                etf_mean /= etf_count
410                    etf_rms = sqrt(etf_rms / etf_count - etf_mean * etf_mean)
411                    etf_var = sqrt(etf_var / (2 * (etf_count - 1)))
412                    printf "etf %d, mean %.1f, rms %.1f, max %d, min %d, var %.2e\n", etf_count, etf_mean, etf_rms, etf_max, etf_min, etf_var
413          }
414#
415# trstat summary data
416#
417# trstat  record count
418# sat               histogram of tracked satellites (0 - 7)
419#
420          if (trstat_count > 0)
421                    printf "trstat %d, sat %d %d %d %d %d %d %d %d\n", trstat_count, trstat_sat[0], trstat_sat[1], trstat_sat[2], trstat_sat[2], trstat_sat[3], trstat_sat[4], trstat_sat[5], trstat_sat[6], trstat_sat[7]
422#
423# osc summary data
424#
425# osc               record count
426# control control midrange (V) +/- deviation (mV)
427# temp              oven temperature midrange +/- deviation (deg C)
428#
429          if (osc_count > 0)
430                    printf "osc %d, control %.3f+/-%.3f, temp %.1f+/-%.2f\n", osc_count, (osc_vmax + osc_vmin) / 2, (osc_vmax - osc_vmin) / 2 * 1e3, (osc_tmax + osc_tmin) / 2, (osc_tmax - osc_tmin) / 2
431}
432