154359Sroberto# awk program to scan clockstat files and report errors/statistics
254359Sroberto#
354359Sroberto# usage: awk -f check.awk clockstats
454359Sroberto#
554359Sroberto# This program works for the following radios:
654359Sroberto# PST/Traconex 1020 WWV reciever
754359Sroberto# Arbiter 1088 GPS receiver
854359Sroberto# Spectracom 8170/Netclock-2 WWVB receiver
954359Sroberto# IRIG audio decoder
1054359Sroberto# Austron 2200A/2201A GPS receiver (see README.austron file)
1154359Sroberto#
1254359SrobertoBEGIN {
1354359Sroberto	etf_min = osc_vmin = osc_tmin = 1e9
1454359Sroberto	etf_max = osc_vmax = osc_tmax = -1e9
1554359Sroberto}
1654359Sroberto#
1754359Sroberto# scan all records in file
1854359Sroberto#
1954359Sroberto{
2054359Sroberto	#
2154359Sroberto	# select PST/Traconex WWV records
2254359Sroberto	# 00:00:37.234  96/07/08/190 O6@0:5281825C07510394
2354359Sroberto	#
2454359Sroberto	if (NF >= 4 && $3 == "127.127.3.1") {
2554359Sroberto		if (substr($6, 14, 4) > "0010")
2654359Sroberto			wwv_sync++
2754359Sroberto		if (substr($6, 13, 1) == "C")
2854359Sroberto			wwv_wwv++
2954359Sroberto		if (substr($6, 13, 1) == "H")
3054359Sroberto			wwv_wwvh++
3154359Sroberto		x = substr($6, 12, 1)
3254359Sroberto		if (x == "1")
3354359Sroberto			wwv_2.5++
3454359Sroberto		else if (x == "2")
3554359Sroberto			wwv_5++
3654359Sroberto		else if (x == "3")
3754359Sroberto			wwv_10++
3854359Sroberto		else if (x == "4")
3954359Sroberto			wwv_15++
4054359Sroberto		else if (x == "5")
4154359Sroberto			wwv_20++
4254359Sroberto		continue
4354359Sroberto	}
4454359Sroberto	#
4554359Sroberto	# select Arbiter GPS records
4654359Sroberto	# 96 190 00:00:37.000 0 V=08 S=44 T=3 P=10.6 E=00
4754359Sroberto	# N39:42:00.951 W075:46:54.880 210.55      2.50 0.00
4854359Sroberto	#
4954359Sroberto	if (NF >= 4 && $3 == "127.127.11.1") {
5054359Sroberto		if (NF > 8) {
5154359Sroberto			arb_count++
5254359Sroberto			if ($7 != 0)
5354359Sroberto				arb_sync++
5454359Sroberto			x = substr($10, 3, 1)
5554359Sroberto			if (x == "0")
5654359Sroberto				arb_0++
5754359Sroberto			else if (x == "1")
5854359Sroberto				arb_1++
5954359Sroberto			else if (x == "2")
6054359Sroberto				arb_2++
6154359Sroberto			else if (x == "3")
6254359Sroberto				arb_3++
6354359Sroberto			else if (x == "4")
6454359Sroberto				arb_4++
6554359Sroberto			else if (x == "5")
6654359Sroberto				arb_5++
6754359Sroberto			else if (x == "6")
6854359Sroberto			arb_6++
6954359Sroberto		} else if (NF == 8) {
7054359Sroberto			arbn++
7154359Sroberto			arb_mean += $7
7254359Sroberto			arb_rms += $7 * $7
7354359Sroberto			if (arbn > 0) {
7454359Sroberto				x = $7 - arb_val
7554359Sroberto				arb_var += x * x
7654359Sroberto			}
7754359Sroberto			arb_val = $7
7854359Sroberto		}
7954359Sroberto		continue
8054359Sroberto	}
8154359Sroberto	#
8254359Sroberto	# select Spectracom WWVB records
8354359Sroberto	# see summary for decode
8454359Sroberto	#   96 189 23:59:32.248  D
8554359Sroberto	#
8654359Sroberto	if (NF >= 4 && $3 == "127.127.4.1") {
8754359Sroberto		if ($4 == "SIGNAL" || NF > 7)
8854359Sroberto			printf "%s\n", $0
8954359Sroberto		else {
9054359Sroberto			wwvb_count++
9154359Sroberto			if ($4 ~ /\?/)
9254359Sroberto				wwvb_x++
9354359Sroberto			else if ($4 ~ /A/)
9454359Sroberto				wwvb_a++
9554359Sroberto			else if ($4 ~ /B/)
9654359Sroberto				wwvb_b++
9754359Sroberto			else if ($4 ~ /C/)
9854359Sroberto				wwvb_c++
9954359Sroberto			else if ($4 ~ /D/)
10054359Sroberto				wwvb_d++
10154359Sroberto		}
10254359Sroberto		continue
10354359Sroberto	}
10454359Sroberto	#
10554359Sroberto	# select IRIG audio decoder records
10654359Sroberto	# see summary for decode
10754359Sroberto	#
10854359Sroberto	if (NF >= 4 && $3 == "127.127.6.0") {
10954359Sroberto		irig_count++
11054359Sroberto		if ($5 ~ /\?/)
11154359Sroberto			irig_error++
11254359Sroberto		continue
11354359Sroberto	}
11454359Sroberto	#
11554359Sroberto	# select Austron GPS LORAN ENSEMBLE records
11654359Sroberto	# see summary for decode
11754359Sroberto	#
11854359Sroberto	else if (NF >= 13 && $6 == "ENSEMBLE") {
11954359Sroberto		ensemble_count++
12054359Sroberto		if ($9 <= 0)
12154359Sroberto			ensemble_badgps++
12254359Sroberto		else if ($12 <= 0)
12354359Sroberto			ensemble_badloran++
12454359Sroberto		else {
12554359Sroberto			if ($13 > 200e-9 || $13 < -200e-9)
12654359Sroberto				ensemble_200++
12754359Sroberto			else if ($13 > 100e-9 || $13 < -100e-9)
12854359Sroberto				ensemble_100++
12954359Sroberto			ensemble_mean += $13
13054359Sroberto			ensemble_rms += $13 * $13
13154359Sroberto		}
13254359Sroberto		continue
13354359Sroberto	}
13454359Sroberto	#
13554359Sroberto	# select Austron LORAN TDATA records
13654359Sroberto	# see summary for decode; note that signal quality log is simply
13754359Sroberto	# copied to output
13854359Sroberto	#
13954359Sroberto	else if (NF >= 7 && $6 == "TDATA") {
14054359Sroberto                tdata_count++
14154359Sroberto                for (i = 7; i < NF; i++) {
14254359Sroberto                        if ($i == "M" && $(i+1) == "OK") {
14354359Sroberto                                i += 5
14454359Sroberto                                m += $i
14554359Sroberto                		tdata_m++
14654359Sroberto		        }
14754359Sroberto                        else if ($i == "W" && $(i+1) == "OK") {
14854359Sroberto                                i += 5
14954359Sroberto                                w += $i
15054359Sroberto                        	tdata_w++
15154359Sroberto			}
15254359Sroberto                        else if ($i == "X" && $(i+1) == "OK") {
15354359Sroberto                                i += 5
15454359Sroberto                                x += $i
15554359Sroberto                        	tdata_x++
15654359Sroberto			}
15754359Sroberto                        else if ($i == "Y" && $(i+1) == "OK") {
15854359Sroberto                                i += 5
15954359Sroberto                                y += $i
16054359Sroberto                        	tdata_y++
16154359Sroberto			}
16254359Sroberto                        else if ($i == "Z" && $(i+1) == "OK") {
16354359Sroberto                                i += 5
16454359Sroberto                                z += $i
16554359Sroberto                        	tdata_z++
16654359Sroberto			}
16754359Sroberto		}	
16854359Sroberto		continue
16954359Sroberto	}
17054359Sroberto	#
17154359Sroberto	# select Austron ITF records
17254359Sroberto	# see summary for decode
17354359Sroberto	#
17454359Sroberto	else if (NF >= 13 && $5 == "ITF" && $12 >= 100) {
17554359Sroberto		itf_count++
17654359Sroberto		if ($9 > 200e-9 || $9 < -200e-9)
17754359Sroberto			itf_200++
17854359Sroberto		else if ($9 > 100e-9 || $9 < -100e-9)
17954359Sroberto			itf_100++
18054359Sroberto		itf_mean += $9
18154359Sroberto		itf_rms += $9 * $9
18254359Sroberto		itf_var += $10 * $10
18354359Sroberto		continue
18454359Sroberto	}
18554359Sroberto	#
18654359Sroberto	# select Austron ETF records
18754359Sroberto	# see summary for decode
18854359Sroberto	#
18954359Sroberto	else if (NF >= 13 && $5 == "ETF" && $13 >= 100) {
19054359Sroberto		etf_count++
19154359Sroberto		if ($6 > etf_max)
19254359Sroberto			etf_max = $6
19354359Sroberto		else if ($6 < etf_min)
19454359Sroberto			etf_min = $6
19554359Sroberto		etf_mean += $6
19654359Sroberto		etf_rms += $6 * $6
19754359Sroberto		etf_var += $9 * $9
19854359Sroberto		continue
19954359Sroberto	}
20054359Sroberto	#
20154359Sroberto	# select Austron TRSTAT records
20254359Sroberto	# see summary for decode
20354359Sroberto	#
20454359Sroberto	else if (NF >= 5 && $5 == "TRSTAT") {
20554359Sroberto		trstat_count++
20654359Sroberto		j = 0
20754359Sroberto		for (i = 6; i <= NF; i++)
20854359Sroberto			if ($i == "T")
20954359Sroberto				j++
21054359Sroberto		trstat_sat[j]++
21154359Sroberto		continue
21254359Sroberto	}
21354359Sroberto	#
21454359Sroberto	# select Austron ID;OPT;VER records
21554359Sroberto	#
21654359Sroberto	# config GPS 2201A TTY1 TC1 LORAN IN OUT1 B.00 B.00 28-Apr-93
21754359Sroberto	#
21854359Sroberto	# GPS 2201A	receiver model
21954359Sroberto	# TTY1		rs232 moduel
22054359Sroberto	# TC1		IRIG module
22154359Sroberto	# LORAN		LORAN assist module
22254359Sroberto	# IN		input module
22354359Sroberto	# OUT1		output module
22454359Sroberto	# B.00 B.00	firmware revision
22554359Sroberto	# 28-Apr-9	firmware date3
22654359Sroberto        #
22754359Sroberto	else if (NF >= 5 && $5 == "ID;OPT;VER") {
22854359Sroberto		id_count++
22954359Sroberto		id_temp = ""
23054359Sroberto		for (i = 6; i <= NF; i++)
23154359Sroberto			id_temp = id_temp " " $i
23254359Sroberto		if (id_string != id_temp)
23354359Sroberto			printf "config%s\n", id_temp
23454359Sroberto		id_string = id_temp
23554359Sroberto		continue	
23654359Sroberto	}
23754359Sroberto	#
23854359Sroberto	# select Austron POS;PPS;PPSOFF records
23954359Sroberto	#
24054359Sroberto	# position +39:40:48.425 -075:45:02.392 +74.09 Stored UTC 0 200 0
24154359Sroberto	#
24254359Sroberto	# +39:40:48.425	position north latitude
24354359Sroberto	# -075:45:02.392 position east longitude
24454359Sroberto	# +74.09	elevation (meters)
24554359Sroberto	# Stored	position is stored
24654359Sroberto	# UTC		time is relative to UTC
24754359Sroberto	# 0 200 0	PPS offsets
24854359Sroberto	#
24954359Sroberto	else if (NF >= 5 && $5 == "POS;PPS;PPSOFF") {
25054359Sroberto		pos_count++
25154359Sroberto		pos_temp = ""
25254359Sroberto		for (i = 6; i <= NF; i++)
25354359Sroberto			pos_temp = pos_temp " " $i
25454359Sroberto		if (pos_string != pos_temp)
25554359Sroberto			printf "position%s\n", pos_temp
25654359Sroberto		pos_string = pos_temp
25754359Sroberto	continue
25854359Sroberto	}
25954359Sroberto	#
26054359Sroberto	# select Austron OSC;ET;TEMP records
26154359Sroberto	#
26254359Sroberto	# loop 1121 Software Control Locked
26354359Sroberto	#
26454359Sroberto	# 1121		oscillator type
26554359Sroberto	# Software Control loop is under software control
26654359Sroberto	# Locked	loop is locked
26754359Sroberto	#
26854359Sroberto	else if (NF >= 5 && $5 == "OSC;ET;TEMP") {
26954359Sroberto		osc_count++
27054359Sroberto		osc_temp = $6 " " $7 " " $8 " " $9
27154359Sroberto		if (osc_status != osc_temp)
27254359Sroberto			printf "loop %s\n", osc_temp
27354359Sroberto		osc_status = osc_temp
27454359Sroberto		if ($10 > osc_vmax)
27554359Sroberto			osc_vmax = $10
27654359Sroberto		if ($10 < osc_vmin)
27754359Sroberto			osc_vmin = $10
27854359Sroberto		if ($11 > osc_tmax)
27954359Sroberto			osc_tmax = $11
28054359Sroberto		if ($11 < osc_tmin)
28154359Sroberto			osc_tmin = $11
28254359Sroberto	continue
28354359Sroberto	}
28454359Sroberto	#
28554359Sroberto	# select Austron UTC records
28654359Sroberto	# these ain't ready yet
28754359Sroberto	#
28854359Sroberto	else if (NF >= 5 && $5 == "UTC") {
28954359Sroberto		utc_count++
29054359Sroberto		utc_temp = ""
29154359Sroberto		for (i = 6; i <= NF; i++)
29254359Sroberto			utc_temp = utc_temp " " $i
29354359Sroberto		if (utc_string != utc_temp)
29454359Sroberto#			printf "utc%s\n", utc_temp
29554359Sroberto                utc_string = utc_temp
29654359Sroberto	continue
29754359Sroberto	}
29854359Sroberto} END {
29954359Sroberto#
30054359Sroberto# PST/Traconex WWV summary data
30154359Sroberto#
30254359Sroberto	if (wwv_wwv + wwv_wwvh > 0)
30354359Sroberto		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
30454359Sroberto#
30554359Sroberto# Arbiter 1088 summary data
30654359Sroberto#
30754359Sroberto# gps		record count
30854359Sroberto# err		error count
30954359Sroberto# sats(0-6)	satellites tracked
31054359Sroberto# mean		1 PPS mean (us)
31154359Sroberto# rms		1 PPS rms error (us)
31254359Sroberto# var		1 PPS Allan variance
31354359Sroberto#
31454359Sroberto	if (arb_count > 0) {
31554359Sroberto		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
31654359Sroberto		if (arbn > 1) {
31754359Sroberto			arb_mean /= arbn
31854359Sroberto			arb_rms = sqrt(arb_rms / arbn - arb_mean * arb_mean)
31954359Sroberto			arb_var = sqrt(arb_var / (2 * (arbn - 1)))
32054359Sroberto			printf ", mean %.2f, rms %.2f, var %.2e\n", arb_mean, arb_rms, arb_var * 1e-6
32154359Sroberto		} else {
32254359Sroberto			printf "\n"
32354359Sroberto		}
32454359Sroberto	}
32554359Sroberto#
32654359Sroberto# ensemble summary data
32754359Sroberto#
32854359Sroberto# ensemble	record count
32954359Sroberto# badgps	gps data unavailable
33054359Sroberto# badloran	loran data unavailable
33154359Sroberto# rms		ensemble rms error (ns)
33254359Sroberto# >200		ensemble error >200 ns
33354359Sroberto# >100		100 ns < ensemble error < 200 ns
33454359Sroberto#
33554359Sroberto	if (ensemble_count > 0) {
33654359Sroberto		ensemble_mean /= ensemble_count
33754359Sroberto		ensemble_rms = sqrt(ensemble_rms / ensemble_count - ensemble_mean * ensemble_mean) * 1e9 
33854359Sroberto		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
33954359Sroberto	}
34054359Sroberto#
34154359Sroberto# wwvb summary data
34254359Sroberto#
34354359Sroberto# wwvb		record count
34454359Sroberto# ?		unsynchronized
34554359Sroberto# >1		error > 1 ms
34654359Sroberto# >10		error > 10 ms
34754359Sroberto# >100		error > 100 ms
34854359Sroberto# >500		error > 500 ms
34954359Sroberto#
35054359Sroberto	if (wwvb_count > 0)
35154359Sroberto		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
35254359Sroberto#
35354359Sroberto# irig summary data
35454359Sroberto#
35554359Sroberto# irig		record count
35654359Sroberto# err		error count
35754359Sroberto#
35854359Sroberto	if (irig_count > 0)
35954359Sroberto		printf "irig %d, err %d\n", irig_count, irig_error
36054359Sroberto#
36154359Sroberto# tdata summary data
36254359Sroberto#
36354359Sroberto# tdata		record count
36454359Sroberto# m		M master OK-count, mean level (dB)
36554359Sroberto# w		W slave OK-count, mean level (dB)
36654359Sroberto# x		X slave OK-count, mean level (dB)
36754359Sroberto# y		Y slave OK-count, mean level (dB)
36854359Sroberto# z		Z slave OK-count, mean level (dB)
36954359Sroberto#
37054359Sroberto	if (tdata_count > 0 ) {
37154359Sroberto		if (tdata_m > 0)
37254359Sroberto			m /= tdata_count
37354359Sroberto		if (tdata_x > 0)
37454359Sroberto			w /= tdata_count
37554359Sroberto		if (tdata_x > 0)
37654359Sroberto			x /= tdata_count
37754359Sroberto		if (tdata_y > 0)
37854359Sroberto			y /= tdata_count
37954359Sroberto		if (tdata_z > 0)
38054359Sroberto			z /= tdata_count
38154359Sroberto		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
38254359Sroberto	}
38354359Sroberto#
38454359Sroberto# itf summary data
38554359Sroberto#
38654359Sroberto# itf		record count
38754359Sroberto# rms		itf rms error (ns)
38854359Sroberto# >200		itf error > 200 ns
38954359Sroberto# >100		itf error > 100 ns
39054359Sroberto# var		Allan variance
39154359Sroberto#
39254359Sroberto	if (itf_count > 1) { 
39354359Sroberto		itf_mean /= itf_count
39454359Sroberto		itf_rms = sqrt(itf_rms / itf_count - itf_mean * itf_mean) * 1e9
39554359Sroberto		itf_var = sqrt(itf_var / (2 * (itf_count - 1)))
39654359Sroberto		printf "itf %d, rms %.1f, >200 %d, >100 %d, var %.2e\n", itf_count, itf_rms, itf_200, itf_100, itf_var
39754359Sroberto	}
39854359Sroberto#
39954359Sroberto# etf summary data
40054359Sroberto#
40154359Sroberto# etf		record count
40254359Sroberto# mean		etf mean (ns)
40354359Sroberto# rms		etf rms error (ns)
40454359Sroberto# max		etf maximum (ns)
40554359Sroberto# min		etf minimum (ns)
40654359Sroberto# var		Allan variance
40754359Sroberto#
40854359Sroberto	if (etf_count > 0) {
40954359Sroberto                etf_mean /= etf_count
41054359Sroberto		etf_rms = sqrt(etf_rms / etf_count - etf_mean * etf_mean)
41154359Sroberto		etf_var = sqrt(etf_var / (2 * (etf_count - 1)))
41254359Sroberto		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
41354359Sroberto	}
41454359Sroberto#
41554359Sroberto# trstat summary data
41654359Sroberto#
41754359Sroberto# trstat	record count
41854359Sroberto# sat		histogram of tracked satellites (0 - 7)
41954359Sroberto#
42054359Sroberto	if (trstat_count > 0)
42154359Sroberto		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]
42254359Sroberto#
42354359Sroberto# osc summary data
42454359Sroberto#
42554359Sroberto# osc		record count
42654359Sroberto# control	control midrange (V) +/- deviation (mV)
42754359Sroberto# temp		oven temperature midrange +/- deviation (deg C)
42854359Sroberto#
42954359Sroberto	if (osc_count > 0)
43054359Sroberto		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
43154359Sroberto}
432