% Project Drogheda — GNSS / Log Analysis findings (Helgen Beamer / Metropolis) % Built from the helgen-beamer template. Compile: latexmk -xelatex .tex \documentclass[10pt,aspectratio=169]{beamer} \usetheme[progressbar=frametitle, numbering=fraction, block=fill]{metropolis} % ---------- Helgen official palette (Guia de Identidad v1.5, 2026) ---------- \definecolor{HelgenIndigo}{HTML}{2C3C5B} \definecolor{HelgenCyan}{HTML}{51F3DA} \definecolor{HelgenTeal}{HTML}{17A08C} \definecolor{HelgenMist}{HTML}{EFF1F5} \setbeamercolor{normal text}{fg=HelgenIndigo, bg=white} \setbeamercolor{alerted text}{fg=HelgenTeal} \setbeamercolor{frametitle}{bg=HelgenIndigo, fg=white} \setbeamercolor{progress bar}{fg=HelgenCyan, bg=HelgenMist} \setbeamercolor{title separator}{fg=HelgenCyan} \setbeamercolor{block title}{bg=HelgenMist, fg=HelgenIndigo} % ---------- Fonts: XeLaTeX + bundled brand fonts, clean fallback otherwise ---------- \usepackage{iftex} \ifXeTeX \usepackage{fontspec} \IfFontExistsTF{League Spartan}{% \newfontfamily\helgentitle{League Spartan}[BoldFont={* Bold}] \setbeamerfont{title}{family=\helgentitle, series=\bfseries} \setbeamerfont{frametitle}{family=\helgentitle, series=\bfseries} \setbeamerfont{section title}{family=\helgentitle, series=\bfseries} \setbeamerfont{standout}{family=\helgentitle, series=\bfseries} }{} \IfFontExistsTF{MADE Evolve Sans}{\setsansfont{MADE Evolve Sans}}{} \else \usepackage[T1]{fontenc} \fi \usepackage{booktabs} \usepackage{tikz} \usetikzlibrary{arrows.meta, positioning, shapes.misc, fit} \usepackage{listings} \lstset{ basicstyle=\ttfamily\scriptsize, keywordstyle=\color{HelgenIndigo}\bfseries, commentstyle=\color{gray}, stringstyle=\color{HelgenTeal}, showstringspaces=false, breaklines=true, frame=single, framerule=0.2pt, rulecolor=\color{HelgenMist} } % Reusable block / arrow styles (Helgen palette) \tikzset{ blk/.style={rounded rectangle, draw=HelgenIndigo, fill=HelgenMist, font=\scriptsize, minimum height=8mm, inner xsep=2.4mm, align=center}, acc/.style={rounded rectangle, draw=HelgenTeal, fill=white, font=\scriptsize, minimum height=8mm, inner xsep=2.4mm, align=center}, arr/.style={-{Stealth}, thick, HelgenTeal}, arrx/.style={-{Stealth}, thick, red!70!black, dashed}, } % ---------- Footer ---------- \setbeamertemplate{frame footer}{% \scriptsize\color{gray}\insertshortauthor\ · \insertshorttitle\ · Confidencial — Helgen Servicios Especializados} % ---------- Metadata ---------- \title[Drogheda · GNSS/Logs]{Project Drogheda — GNSS \& Log Analysis} \subtitle{HBOX RTK fleet: bench triage, healthy baseline, and observability} \author[Helgen]{Helgen Technologies — CTO/CSO} \date{20 June 2026} \titlegraphic{} \begin{document} \maketitle \begin{frame}{Agenda} \tableofcontents \end{frame} % ===================================================================== \section{Context} \begin{frame}{Where we are} \begin{columns}[T] \column{0.58\textwidth} \begin{itemize} \item HBOX RTK trackers on Hutchison Ports heavy equipment (ICAVE, Veracruz) — worked, then failed. \item Three independent failure fronts, fixed in parallel: \begin{itemize}\footnotesize \item \textbf{Backend} (retranslator) — could drop the fleet / black-hole a unit \item \textbf{Thermal} — PLA box in a sealed cabinet (heat trap) \item \textbf{Prototype HW + observability} — hand-wired; logs untrustworthy \end{itemize} \item Field units pulled $\rightarrow$ bench triage in Querétaro (firmware v1, no thermal log). \end{itemize} \column{0.4\textwidth} \begin{alertblock}{Invariant} Corrections ride the \textbf{4G modem}, not the GNSS — recover the link by power-cycling the stack. \end{alertblock} \end{columns} \end{frame} \begin{frame}{The signal chain} \centering \begin{tikzpicture}[node distance=7mm and 13mm] \node[blk] (f9p) {ZED-F9P\\(GNSS)}; \node[blk, right=of f9p] (mas) {4G NTRIP\\Master}; \node[blk, right=of mas] (ret) {retranslator\\(htracker)}; \node[blk, right=of ret] (wial) {Wialon}; \node[acc, above=10mm of mas] (cas) {NTRIP caster\\(rtk2go)}; \draw[arr] (f9p) -- node[above, font=\tiny]{UART2: GGA} (mas); \draw[arr] (mas) -- node[above, font=\tiny]{UDP} (ret); \draw[arr] (ret) -- (wial); \draw[arr] (cas) -- node[right, font=\tiny]{RTCM in} (mas); \end{tikzpicture} \vfill \footnotesize Corrections come \emph{down} from the caster over 4G; position goes \emph{out} by UDP to the retranslator. The GNSS receiver never talks to the network directly. \end{frame} % ===================================================================== \section{Backend (retranslator)} \begin{frame}{Backend — hardened \& deployed to prod} \begin{itemize} \item \textbf{Head-of-line blocking removed:} a \texttt{WialonForwarder} thread pool does all Wialon TCP I/O \emph{off} the UDP receive loop — one slow upstream no longer drops the fleet. \item \textbf{Anti-jump now time-aware + self-healing:} re-anchors after rejects, clears on disconnect — a unit is never permanently black-holed. \item \textbf{Identity keyed by \texttt{cell\_id}} (CGNAT-safe), not \texttt{(ip,port)}. \item Monitor gained a \textbf{backend-victim} KPI; logs ship to Grafana Loki. \alert{Live in production.} \end{itemize} \begin{alertblock}{Free triage} ONLINE in htracker + OFFLINE in Wialon = \textbf{backend victim}, not a dead radio. \end{alertblock} \end{frame} % ===================================================================== \section{GNSS log findings} \begin{frame}{What we analyzed} \begin{itemize} \item \textbf{7 swap-matrix bench captures} (RTK board $\times$ NTRIP master) + one healthy baseline. \item \textbf{Three views per unit:} u-blox \texttt{.ubx} (receiver), 4G-NTRIP console \texttt{.log}, server retranslator \texttt{tcpdump}. \item Decoded the NTRIP-caster and ZED-F9P configuration files. \item Built local tooling so this is repeatable (explorer + analyzer + dashboard). \end{itemize} \end{frame} \begin{frame}{Headline: receivers are fine — RTK is not being achieved} \begin{itemize} \item Every board emits \textbf{valid NMEA} (6–12 sats, good HDOP). The ZED-F9P is \emph{not} the failure. \item But \textbf{every epoch is SPS (quality 1)} — zero RTK (4/5). Centimetre position never delivered. \item \textbf{Why:} free public \textbf{rtk2go} caster (no SLA) + F9P config saved to \textbf{RAM only} + indoor/bench sky. \item The F9P message config itself is \textbf{correct} — GGA on UART2 @ 1\,Hz. \end{itemize} \end{frame} \begin{frame}{Every unit is SPS — none reached RTK} \centering \includegraphics[width=0.88\textwidth]{fig/fig_fix_compare.pdf}\\[2mm] \footnotesize Max GGA fix quality per capture (7 bench + baseline) — all flat at 1 (SPS); the RTK line (4) is never reached. \end{frame} \begin{frame}{Why: the corrections never settle} \centering \includegraphics[width=0.82\textwidth]{fig/fig_ntrip_delay.pdf}\\[2mm] \footnotesize Even on a healthy unit the NTRIP correction delay sawtooths up to the 300\,s restart watchdog — the free rtk2go feed is intermittent. \end{frame} \begin{frame}{Healthy baseline — \texttt{RTK14\_440} (three views agree)} \begin{columns}[T] \column{0.56\textwidth} \begin{itemize}\small \item \textbf{Receiver:} 341 epochs, 100\% valid, 12 sats, HDOP 0.75 — SPS. \item \textbf{Server:} 984 packets, steady 1\,Hz, \textbf{zero dropouts}, live in Wialon. \item \textbf{Console:} ``registered'' only — no forwarding lines. \end{itemize} \column{0.4\textwidth} \begin{alertblock}{The trap} Healthy \textbf{delivery} $\neq$ \textbf{RTK}. Baseline = healthy SPS + reliable delivery. \end{alertblock} \end{columns} \end{frame} \begin{frame}{Baseline receiver — healthy geometry, stuck at SPS} \centering \includegraphics[width=0.93\textwidth]{fig/fig_baseline_gnss.pdf}\\[1mm] \footnotesize \texttt{RTK14\_440} u-blox capture: 12 sats, HDOP 0.75, C/N0 $\sim$20 dB-Hz — but the fix never leaves 1 (SPS). \end{frame} \begin{frame}{Baseline delivery — healthy end-to-end} \centering \includegraphics[width=0.93\textwidth]{fig/fig_delivery.pdf}\\[1mm] \footnotesize Server-side tcpdump: 984 datagrams, steady $\sim$1\,Hz, max gap 0.86\,s — the unit is genuinely reporting (live in Wialon). \end{frame} \begin{frame}{The console blind spot — and a retraction} \begin{itemize} \item The \emph{provably-healthy} unit logs the \textbf{same sparse console} as units that looked ``failed''. \item $\Rightarrow$ the 4G-NTRIP console \textbf{cannot prove forwarding}. Absence of lines $\neq$ failure. \item \alert{Retraction:} the earlier ``master-14 ingest fault'' is \textbf{withdrawn}. \item Only \textbf{master 21} is a confirmed fault: modem won't register (\texttt{0xFFFFF} + 511 AT-parser errors). \end{itemize} \end{frame} \begin{frame}{Per-master verdict} \centering \begin{tabular}{ll} \toprule \textbf{NTRIP master} & \textbf{Result} \\ \midrule \#04 · \#17 · \#24 · \#25 & Healthy — registered + forwarding \\ \#14 & \alert{Indeterminate} — console can't confirm; baseline proves it forwards \\ \#21 & \alert{Failed} — modem won't register (\texttt{0xFFFFF} + 511 AT errors) \\ \bottomrule \end{tabular} \vfill \footnotesize Confirm a unit only from a \texttt{.ubx} (fix), the Blackpill, or a server delivery capture — never the console alone. \end{frame} \begin{frame}{Heat — not implicated (by these logs)} \begin{itemize} \item Room-temperature bench captures, \textbf{no thermal channel} (firmware v1). \item The hard faults reproduce \textbf{cold} $\Rightarrow$ heat is \emph{not} the cause of what we see here. \item The \textbf{field} thermal hypothesis stays open — needs a thermocouple cabinet soak and/or v2 MCU-temp logging. \end{itemize} \end{frame} % ===================================================================== \section{Changes \& tooling} \begin{frame}{Observability — the real fix} \begin{itemize} \item The Blackpill watchdog already computes fix, packets, state, battery, MCU temp. \item \textbf{v2.1 logging spec:} replace the self-overwriting ANSI dashboard with a \textbf{structured, timestamped, parseable} stream (+ decoded reset cause). \item \textbf{\texttt{hbox-log-tools}} repo: in-browser Log Explorer (parse + plot + compare to baseline, offline), batch analyzer, dashboard, manual. \end{itemize} \end{frame} \begin{frame}[fragile]{The new per-unit log line (v2.1)} \begin{lstlisting} BOOT,32,fw=2.1,reset=POR COLS,ms,state,link,fix,sat,rx,rate,vbat,v5v,veh,ign,tc,tmax,der,rec,limp,nmea_age TLM,190000,RUN,OK,4,12,340,1HZ,5.01,1,1,48,49,0,0,0,310 EVT,490000,RUN,DEGR,RTK lost -> recycle 4G/NTRIP \end{lstlisting} \footnotesize Fix + packets + state + reset cause $\Rightarrow$ per-unit health is unambiguous from the unit's own log — no server tcpdump needed. \end{frame} % ===================================================================== \section{Next steps} \begin{frame}{Next steps} \begin{enumerate} \item Save the F9P config to \textbf{FLASH} + verify (RAM-only is a power-cycle risk); check the simpleRTK2B Pro \texttt{XBEE$\leftrightarrow$UART2} switch. \item Move corrections off rtk2go $\rightarrow$ a \textbf{dedicated caster} (HelgenSRV) with uptime. \item \textbf{Open-sky RTK retest} with a verified caster $\rightarrow$ confirm fix $\geq 4$. \item Connect the \textbf{Blackpill} with v2.1 logging; modem-triage master 21; thermal soak. \end{enumerate} \end{frame} \begin{frame}[standout] The radios work.\\ We deliver \textbf{SPS} reliably — \textbf{not RTK}.\\[2mm] {\normalsize The blockers are \textbf{corrections} and \textbf{observability} — both now addressed.} \end{frame} \end{document}