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