From e6d22bce14469d6e4ffc2335c34282718e9ea1b0 Mon Sep 17 00:00:00 2001 From: Michael Pham <61564344+Mikefly123@users.noreply.github.com> Date: Thu, 23 Jul 2026 23:34:54 -0700 Subject: [PATCH] perf(comdelay): pass comStatus straight through when DIVIDER == 0 Problem: file downlink over the USP LoRa radio is throughput-capped by ComDelay. comStatusIn_handler only latched the radio's comStatus, and run_handler (10 Hz via rateGroup10Hz -> downlinkDelay.run) released at most one status per tick even with DIVIDER=0, quantizing the whole downlink to ~10 frames/s (~2 kB/s at 248 B frames) regardless of air rate. Mechanism/fix: when DIVIDER == 0, comStatusIn_handler now forwards the status to comStatusOut immediately instead of latching it, so ComQueue is paced purely by radio TX-done. DIVIDER > 0 keeps the existing latched/tick-paced behavior unchanged. Coherence: in passthrough mode the valid flag is never set, so a status can't also be emitted by run_handler (no duplication); a status latched under DIVIDER > 0 before a runtime switch to 0 is still consumed by run_handler's compare_exchange (no loss). ComDelay is passive, so the forward runs on the radio-side caller thread; that is safe because comStatusOut feeds ComQueue's async input (enqueue only). A grep-able image-currency marker string is included. HWIL A/B on the v5e + GRC-USP bench (P4_GFSK_75K both ends, radio-only downlink path, 178,704 B FileDownlink SendFile, SendStarted->FileSent): - baseline (latched, DIVIDER=0): 90.19 s = 1981 B/s (matches the ~105 s / 200 kB manual measurement) - passthrough: 69.75 s = 2562 B/s and 71.47 s = 2500 B/s (~1.28x) - received files byte-identical to baseline (same cksum); no QueueOverflow storms, no panics/wedges; DIVIDER=9 still tick-paces (~0.8 frame/s observed); profile sweep + NO_OP commanding unaffected. Co-Authored-By: Claude Fable 5 --- .../Components/ComDelay/ComDelay.cpp | 33 +++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/PROVESFlightControllerReference/Components/ComDelay/ComDelay.cpp b/PROVESFlightControllerReference/Components/ComDelay/ComDelay.cpp index 687aac929..30244cb3f 100644 --- a/PROVESFlightControllerReference/Components/ComDelay/ComDelay.cpp +++ b/PROVESFlightControllerReference/Components/ComDelay/ComDelay.cpp @@ -38,9 +38,38 @@ void ComDelay ::parameterUpdated(FwPrmIdType id) { // Handler implementations for typed input ports // ---------------------------------------------------------------------- +// Image-currency marker: grep-able via `strings zephyr.elf` to verify the flashed +// binary actually contains the divider-0 passthrough change (never trust "Verified OK"). +static volatile const char COMDELAY_DIV0_PASSTHROUGH_MARKER[] = "comdelay-div0-passthrough-20260723"; + void ComDelay ::comStatusIn_handler(FwIndexType portNum, Fw::Success& condition) { - this->m_last_status = condition; - this->m_last_status_valid = true; + // Read the divider; on invalid/uninit fall back to the default (matches run_handler). + Fw::ParamValid is_valid; + U16 current_divisor = this->paramGet_DIVIDER(is_valid); + if ((is_valid == Fw::ParamValid::INVALID) || (is_valid == Fw::ParamValid::UNINIT)) { + current_divisor = Components::DEFAULT_DIVIDER; + } + + if (current_divisor == 0) { + // DIVIDER == 0 means "no delay": forward the status immediately instead of + // latching it for the next rate tick. This removes the rate-group quantization + // (one status per tick) so downlink is paced purely by radio TX-done. + // + // Threading note: ComDelay is passive, so this executes on the CALLER's thread + // (the radio-side comStatus source). That is safe because comStatusOut feeds an + // async input (ComQueue), which only enqueues a message here. + // + // Coherence with run_handler: in passthrough mode we never set + // m_last_status_valid, so this status cannot ALSO be emitted by run_handler + // (no duplication). A status latched earlier under DIVIDER > 0 is still + // consumed by run_handler's compare_exchange as before (no loss) if the + // divider is changed to 0 at runtime. + static_cast(COMDELAY_DIV0_PASSTHROUGH_MARKER[0]); // volatile read keeps the marker in the image + this->comStatusOut_out(0, condition); + } else { + this->m_last_status = condition; + this->m_last_status_valid = true; + } } void ComDelay ::run_handler(FwIndexType portNum, U32 context) {