#!/bin/sh # B7B board-side: validate QMI DMS time -> ONE system clock step 1970 -> DMS UTC (date -u -s @N, aligned to a whole second; NO RTC, NO eMMC) # -> post snapshot -> observe OBS s -> temporary /32 route to ONE VNIIFTRI server, ntpd -w (query only), route removed. trap '' HUP B=/tmp/b7/busybox; O=/tmp/b7b; mkdir -p $O; Q="qmicli -p -d /dev/wwan0qmi0"; D=/run/aurora-modem FLOOR=1790928541 # t4 initramfs build time 2026-10-02T08:09:01Z Y2026=1767225600; CEIL=$((FLOOR + 5*365*86400)); GPS=315964800; NTPIP=89.109.251.21; OBS=${1:-180} ts() { $B adjtimex | awk '/time.tv_sec/{s=$2} /time.tv_usec/{u=$2} END{printf "%d.%06d", s, u}'; } up() { cut -d' ' -f1 /proc/uptime; } A=$O/b7b-actions.txt; act() { echo "[up $(up) clk $(ts)] $*" >> $A; echo "<5>[b7b] $*" > /dev/kmsg; } mm() { mmcli "$@" 2>&1 | tr -d '\033' | sed 's/\[[0-9;]*m//g'; } snap() { { echo "=== uptime"; cat /proc/uptime; echo "=== date -u"; date -u; echo "=== adjtimex"; $B adjtimex echo "=== rtc (read only)"; cat /sys/class/rtc/rtc0/since_epoch /sys/class/rtc/rtc0/date /sys/class/rtc/rtc0/time echo "=== dms"; $Q --dms-get-time; echo "=== nitz"; $Q --nas-get-network-time echo "=== mmcli -m any"; mm -m any; echo "=== mmcli -b 1"; mm -b 1 echo "=== ip"; ip -4 addr show wwan0; ip -4 addr show usb0; ip route echo "=== pids"; echo "MM=$(pidof ModemManager) rmtfs=$(pidof rmtfs) qmi-proxy=$(pidof qmi-proxy) telnetd=$(pidof telnetd) udevd=$(pidof udevd) dbus=$(pidof dbus-daemon) polkitd=$(pidof polkitd)"; ps echo "=== state"; cat $D/state; echo "=== aurora-modem status"; /etc/init.d/aurora-modem status echo "=== lifecycle.log (lines $(wc -l < $D/lifecycle.log))"; tail -n 12 $D/lifecycle.log; echo "=== service.log"; tail -n 4 $D/service.log echo "=== mm-0.log tail"; tail -n 15 $D/mm-0.log echo "=== dmesg (lines $(dmesg | wc -l)) tail"; dmesg | tail -n 30; } > $1 2>&1; } dms_sample() { a=$(ts); r=$($Q --dms-get-time 2>&1); b=$(ts); echo "$r" >> $O/dms-raw.txt echo "$a $b $(echo "$r" | sed -n "s/.*System time: '\([0-9]*\).*/\1/p")"; } : > $A; act "B7B BEGIN obs=${OBS}s floor=$FLOOR ntp=$NTPIP" $B usleep 1 2>/dev/null && act "usleep ok" || act "usleep missing" # 1. baseline snap $O/b7b-pre.txt; act "pre snapshot done" # 2. time-source validation T=$O/b7b-time-source.txt; : > $T for k in 1 2 3; do echo "DMS$k $(dms_sample)" >> $T; done a=$(ts); r=$($Q --nas-get-network-time 2>&1); b=$(ts); echo "$r" > $O/nitz-raw.txt nd=$(echo "$r" | sed -n "s/.*3GPP Date: *'\(.*\)'/\1/p"); nt=$(echo "$r" | sed -n "s/.*3GPP Time: *'\(.*\)'/\1/p") ne=$(date -u -d "$nd $nt" +%s 2>/dev/null); echo "NITZ $a $b $nd $nt epoch=$ne" >> $T awk -v F=$FLOOR -v Y=$Y2026 -v C=$CEIL -v G=$GPS -v NE="$ne" ' /^DMS/ { n++; if ($4 !~ /^[0-9]+$/) bad="DMS"n" no value"; u=$4/1000+G; m=($2+$3)/2; o[n]=u-m; w[n]=$3-$2; uu[n]=u printf "DMS%d utc=%.3f window=%.1fms offset_vs_clock=%.3f\n", n, u, w[n]*1000, o[n] } /^NITZ/ { nm=($2+$3)/2; no=(NE+0)-nm; if (NE !~ /^[0-9]+$/) bad="NITZ no value"; printf "NITZ epoch=%s offset_vs_clock=%.3f\n", NE, no } END { b=1; for (i=2;i<=n;i++) if (w[i]mx) mx=o[i] } c1=(n==3 && bad==""); c2=(uu[b]>=F); c3=(uu[b]>=Y); c4=(uu[b]-2) printf "CHECK all values present: %s %s\n", c1?"PASS":"FAIL", bad printf "CHECK DMS >= image build 2026-10-02T08:09:01Z (%d): %s\n", F, c2?"PASS":"FAIL" printf "CHECK year >= 2026: %s\n", c3?"PASS":"FAIL" printf "CHECK not garbage (< build + 5 y; 1980/2017 excluded by floor): %s\n", c4?"PASS":"FAIL" printf "CHECK 3 DMS samples consistent (spread %.1f ms < 50): %s\n", (mx-mn)*1000, c5?"PASS":"FAIL" printf "CHECK |DMS - NITZ| = %.3f s < 2: %s\n", (d<0?-d:d), c6?"PASS":"FAIL" printf "BEST DMS%d offset=%.6f\n", b, o[b] print ((c1&&c2&&c3&&c4&&c5&&c6) ? "VERDICT=GO" : "VERDICT=STOP") }' $T > $T.eval cat $T.eval >> $T grep -q "VERDICT=GO" $T || { act "time source checks FAILED - STOP, clock not changed"; exit 2; } OFF=$(sed -n 's/^BEST DMS[0-9] offset=//p' $T); act "checks PASS, DMS offset vs clock $OFF s" [ "$2" = dry ] && { act "DRY RUN: stop before clock step"; exit 0; } # 3. one clock step, aligned so that the new clock lands on a whole UTC second DN0=$(dmesg | wc -l); RTC0=$(cat /sys/class/rtc/rtc0/since_epoch) L=$(ts); N=$(awk -v l=$L -v o=$OFF 'BEGIN{printf "%d", int(l+o)+2}'); W=$(awk -v n=$N -v o=$OFF -v l=$(ts) 'BEGIN{w=(n-o)-l-0.004; printf "%d", (w>0?w*1e6:0)}') act "step plan: N=$N ($(date -u -d @$N)) local_target=$(awk -v n=$N -v o=$OFF 'BEGIN{printf "%.6f", n-o}') wait_us=$W" $B usleep $W; c0=$(ts); date -u -s @$N > $O/date-s.out 2>&1; rc=$?; c1=$(ts) act "date -u -s @$N rc=$rc out='$(cat $O/date-s.out)' clk_before=$c0 clk_after=$c1 (no hwclock, RTC untouched)" # 4. immediate post snapshot + DMS re-check against the new clock snap $O/b7b-post.txt for k in 1 2 3; do s=$(dms_sample); echo "$s" | awk -v G=$GPS -v k=$k '{u=$3/1000+G; m=($1+$2)/2; printf "POST DMS%d utc=%.3f clock_mid=%.6f DMS-clock=%+.1f ms window=%.1f ms\n", k, u, m, (u-m)*1000, ($2-$1)*1000}' >> $O/b7b-post.txt; done act "post snapshot done; RTC since_epoch before step $RTC0, now $(cat /sys/class/rtc/rtc0/since_epoch)" # 5. observe V=$O/b7b-observe.txt; echo "# every 5 s: up clock pids state wwan0 lte_ping host_ping lifecycle_lines dmesg_lines rtc; every 15 s mm" > $V i=0; while [ $i -lt $OBS ]; do l="up=$(up) clk=$(date -u +%T) MM=$(pidof ModemManager) rmtfs=$(pidof rmtfs) qp=$(pidof qmi-proxy) tel=$(pidof telnetd) state=$(cat $D/state) wwan0=$(ip -4 -o addr show wwan0 | awk '{print $4}')" l="$l lte=$(ping -c1 -W2 -I wwan0 77.88.8.8 >/dev/null 2>&1 && echo ok || echo FAIL) host=$(ping -c1 -W1 172.16.42.2 >/dev/null 2>&1 && echo up || echo DOWN)" l="$l lc=$(wc -l < $D/lifecycle.log) dmesg=$(dmesg | wc -l) rtc=$(cat /sys/class/rtc/rtc0/since_epoch)" [ $((i % 15)) = 0 ] && l="$l mm=$(mm -m any | grep -E '\| +(state|registration): ' | awk -F': ' '{print $2}' | tr -d ' ' | tr '\n' '/') bearer1=$(mm -b 1 | grep -m1 'connected:' | awk -F': ' '{print $2}')" echo "$l" >> $V; sleep 5; i=$((i+5)); done echo "=== dmesg since step (from line $DN0)" >> $V; dmesg | tail -n +$((DN0+1)) >> $V echo "=== mm-0.log since step (grep time|clock|timeout|error|warn)" >> $V; grep -iE "time|clock|error|warn" $D/mm-0.log | tail -n 40 >> $V act "observe done" # 6-9. NTP check (query only) C=$O/b7b-ntp-check.txt; SRC=$(ip -4 -o addr show wwan0 | awk '{print $4}' | cut -d/ -f1) { echo "route add $NTPIP/32 dev wwan0 src $SRC"; ip route add $NTPIP/32 dev wwan0 src $SRC && echo ROUTE-ADD-OK; ip route echo "clock before: $(ts) $(date -u)"; timeout 14 $B ntpd -w -d -p $NTPIP 2>&1; echo "clock after: $(ts) $(date -u)" for k in 1 2 3; do dms_sample | awk -v G=$GPS -v k=$k '{u=$3/1000+G; m=($1+$2)/2; printf "DMS%d DMS-clock=%+.1f ms\n", k, (u-m)*1000}'; done ip route del $NTPIP/32 dev wwan0 && echo ROUTE-DEL-OK; ip route; } > $C 2>&1 act "ntp check done: $(grep 'reply from' $C | awk '{print $5, $6}' | tr '\n' ';')" act "B7B END"