From 55e254e344af434d08b07accb4dc413c27c6b10a Mon Sep 17 00:00:00 2001 From: Michael Pham <61564344+Mikefly123@users.noreply.github.com> Date: Sun, 19 Jul 2026 22:40:37 -0700 Subject: [PATCH 1/3] test(file-handling): add red integration tests for issue #457 Reproduces UART file uplink/downlink failures for multi-chunk transfers: uplink corrupts silently above ~3-4 chunks (separate, unresolved bug), and downlink hangs indefinitely on any transfer (root-caused to downlinkRepeater's BufferRepeater fanning out to a disabled-by-default LoRa channel that never returns its buffer). Oracle is fileManager's on-board CalculateCrc vs local zlib.crc32^0xFFFFFFFF for uplink, and byte-for-byte comparison against the GDS's own downlinked bytes for downlink. Includes a 1/3/5-chunk parametrized suite plus slow-marked 1000-chunk uplink and downlink stress cases (downlink builds its large source file via on-board AppendFile rather than the still-broken large uplink path). Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_019gMrPNe6LwGtBS6Y7B5bo8 --- .../test/int/uart_file_transfer_test.py | 332 ++++++++++++++++++ pytest.ini | 1 + 2 files changed, 333 insertions(+) create mode 100644 PROVESFlightControllerReference/test/int/uart_file_transfer_test.py diff --git a/PROVESFlightControllerReference/test/int/uart_file_transfer_test.py b/PROVESFlightControllerReference/test/int/uart_file_transfer_test.py new file mode 100644 index 00000000..08b1ac0a --- /dev/null +++ b/PROVESFlightControllerReference/test/int/uart_file_transfer_test.py @@ -0,0 +1,332 @@ +""" +uart_file_transfer_test.py: + +Reproduces GitHub issue #457 (UART file uplink/downlink fails for files +larger than ~1 chunk; downlinks emit 0-byte packets). + +Uplink oracle: fileManager.CalculateCrc on-board, compared against +zlib.crc32(data) ^ 0xFFFFFFFF computed locally over the source bytes. + +Downlink verification: command FileHandling.fileDownlink.SendFile for a file +already known-good on the board (uplinked and CRC-verified in the same test), +then compare the bytes written by the GDS's own FileDownlinker against the +original bytes. + +Chunk size for uplink is fixed project-wide in fprime-gds.yml +(file-uplink-chunk-size: 204), so "N chunks" here means N * 204 bytes. +""" + +import random +import time +import zlib +from pathlib import Path + +import pytest +from fprime_gds.common.files.helpers import FileStates +from fprime_gds.common.testing_fw.api import IntegrationTestAPI + +UPLINK_CHUNK_SIZE = 204 # from fprime-gds.yml: file-uplink-chunk-size + +FILE_MANAGER = "FileHandling.fileManager" +FILE_DOWNLINK = "FileHandling.fileDownlink" + + +def _make_random_file(tmp_path: Path, num_bytes: int, name: str) -> Path: + """Create a file of exactly num_bytes of pseudo-random data.""" + p = tmp_path / name + rng = random.Random(1234 + num_bytes) # deterministic per-size for reproducibility + p.write_bytes(bytes(rng.getrandbits(8) for _ in range(num_bytes))) + return p + + +def _local_crc(data: bytes) -> int: + """Matches fileManager.CalculateCrc on-board oracle (see project memory: + fprime-crc-verification.md).""" + return zlib.crc32(data) ^ 0xFFFFFFFF + + +def _wait_for_uplink_idle(uplinker, timeout_s: float) -> bool: + """Poll the GDS-side uplinker until its state machine returns to IDLE + (transfer finished, successfully or not -- see FileUplinker.finish()/ + data_callback() in fprime_gds.common.files.uplinker). NOTE: + current_files()/queue.current() is NOT usable for this: UplinkQueue + appends to an unbounded __file_store history and never removes entries, + so it is never empty even long after a transfer completes.""" + deadline = time.time() + timeout_s + # Give the queue thread a moment to pick up the enqueued file and leave IDLE. + time.sleep(0.5) + while time.time() < deadline: + if uplinker.state == FileStates.IDLE: + return True + time.sleep(0.25) + return False + + +def _uplink_and_verify_crc( + fprime_test_api: IntegrationTestAPI, + local_path: Path, + dest_path: str, + timeout_s: float, +): + """Uplink local_path to dest_path on the board, then verify via + CalculateCrc oracle. Returns (crc_event_seen, board_crc_or_None).""" + data = local_path.read_bytes() + expected_crc = _local_crc(data) + + uplinker = fprime_test_api.pipeline.files.uplinker + fprime_test_api.clear_histories() + uplinker.enqueue(str(local_path), dest_path) + + idle = _wait_for_uplink_idle(uplinker, timeout_s) + + fprime_test_api.clear_histories() + fprime_test_api.send_command(f"{FILE_MANAGER}.CalculateCrc", [dest_path]) + evt = fprime_test_api.await_event( + f"{FILE_MANAGER}.CalculateCrcSucceeded", timeout=15 + ) + fail_evt = None + if evt is None: + fail_evt = fprime_test_api.await_event( + f"{FILE_MANAGER}.CalculateCrcFailed", timeout=1 + ) + + return { + "uplink_idle": idle, + "crc_event": evt, + "crc_fail_event": fail_evt, + "expected_crc": expected_crc, + "size": len(data), + } + + +@pytest.mark.parametrize("n_chunks", [1, 3, 5]) +def test_uplink_n_chunks( + fprime_test_api: IntegrationTestAPI, start_gds, tmp_path, n_chunks +): + """Uplink a file of n_chunks * UPLINK_CHUNK_SIZE bytes and verify its + on-board CRC matches the local CRC32 oracle.""" + size = n_chunks * UPLINK_CHUNK_SIZE + local_path = _make_random_file(tmp_path, size, f"uplink_{n_chunks}c.bin") + dest = f"/uplink_test_{n_chunks}c.bin" + + result = _uplink_and_verify_crc( + fprime_test_api, local_path, dest, timeout_s=10 + n_chunks * 3 + ) + + print( + f"[n_chunks={n_chunks}] size={result['size']} " + f"uplink_idle={result['uplink_idle']} " + f"crc_event={result['crc_event']} " + f"crc_fail_event={result['crc_fail_event']} " + f"expected_crc=0x{result['expected_crc']:08x}" + ) + + assert result["uplink_idle"], ( + f"uplink queue did not go idle within timeout for {n_chunks} chunks " + f"({size} bytes) -- uplink likely hung" + ) + assert result["crc_fail_event"] is None, ( + f"on-board CalculateCrc explicitly failed: {result['crc_fail_event']}" + ) + assert result["crc_event"] is not None, ( + f"no CalculateCrcSucceeded event received for {n_chunks} chunks " + f"({size} bytes) -- file likely missing/empty on board" + ) + board_crc = result["crc_event"].args[1].val + assert board_crc == result["expected_crc"], ( + f"CRC mismatch for {n_chunks} chunks: board=0x{board_crc:08x} " + f"expected=0x{result['expected_crc']:08x}" + ) + + +@pytest.mark.slow +@pytest.mark.parametrize("n_chunks", [1000]) +def test_uplink_large( + fprime_test_api: IntegrationTestAPI, start_gds, tmp_path, n_chunks +): + """Large uplink case -- skipped by default (deselect with -m 'not slow').""" + size = n_chunks * UPLINK_CHUNK_SIZE + local_path = _make_random_file(tmp_path, size, f"uplink_{n_chunks}c.bin") + dest = f"/uplink_test_{n_chunks}c.bin" + + result = _uplink_and_verify_crc( + fprime_test_api, local_path, dest, timeout_s=60 + n_chunks * 0.5 + ) + assert result["uplink_idle"] + assert result["crc_event"] is not None + board_crc = result["crc_event"].args[1].val + assert board_crc == result["expected_crc"] + + +@pytest.mark.slow +def test_downlink_large(fprime_test_api: IntegrationTestAPI, start_gds, tmp_path): + """~204KB (1000 chunks) downlink stress test. + + Uplink itself is separately broken above 3-4 chunks (see test_uplink_n_chunks), + so a 1000-chunk file cannot be reliably placed on the board via the normal + chunked uplink path. To stress-test the (now-fixed) DOWNLINK path in isolation, + build the large on-board source file out of many small, individually-reliable + single-chunk uplinks + on-board FileManager.AppendFile calls (204 bytes each, + known-good per test_uplink_n_chunks[1]), rather than one large uplink. + """ + n_repeats = 1000 + pattern = _make_random_file( + tmp_path, UPLINK_CHUNK_SIZE, "downlink_large_pattern.bin" + ).read_bytes() + # local_path was consumed (deleted) by the uplink below; re-materialize a + # fresh copy for the initial upload since we still need it uplinked once. + pattern_path = tmp_path / "downlink_large_pattern_upload.bin" + pattern_path.write_bytes(pattern) + + board_pattern_path = "/downlink_large_pattern.bin" + board_target_path = "/downlink_large.bin" + + print( + f"[large] uplinking {UPLINK_CHUNK_SIZE}-byte pattern file to seed the board build" + ) + up = _uplink_and_verify_crc( + fprime_test_api, pattern_path, board_pattern_path, timeout_s=15 + ) + assert up["uplink_idle"] and up["crc_event"] is not None + assert up["crc_event"].args[1].val == up["expected_crc"], ( + "pattern seed uplink corrupted" + ) + + print( + f"[large] building {n_repeats * UPLINK_CHUNK_SIZE} byte on-board file via {n_repeats} AppendFile calls" + ) + t_build_start = time.time() + fprime_test_api.clear_histories() + fprime_test_api.send_and_assert_command( + "FileHandling.fileManager.RemoveFile", [board_target_path, True], max_delay=5 + ) + for i in range(n_repeats): + fprime_test_api.send_and_assert_command( + "FileHandling.fileManager.AppendFile", + [board_pattern_path, board_target_path], + max_delay=5, + ) + if (i + 1) % 100 == 0: + print(f"[large] build progress: {i + 1}/{n_repeats}") + t_build_end = time.time() + print( + f"[large] on-board build took {t_build_end - t_build_start:.1f}s for {n_repeats} appends" + ) + + expected_bytes = pattern * n_repeats + expected_crc = _local_crc(expected_bytes) + size = len(expected_bytes) + + fprime_test_api.clear_histories() + fprime_test_api.send_command( + "FileHandling.fileManager.CalculateCrc", [board_target_path] + ) + crc_evt = fprime_test_api.await_event( + "FileHandling.fileManager.CalculateCrcSucceeded", timeout=30 + ) + assert crc_evt is not None, "CRC check on assembled large file failed/timed out" + assert crc_evt.args[1].val == expected_crc, ( + f"assembled large file CRC mismatch: board=0x{crc_evt.args[1].val:08x} " + f"expected=0x{expected_crc:08x} -- AppendFile assembly itself corrupted, " + f"not a downlink issue" + ) + print( + f"[large] on-board file verified via CRC: {size} bytes, CRC 0x{expected_crc:08x}" + ) + + downlinker = fprime_test_api.pipeline.files.downlinker + dest_name = "downlink_large_received.bin" + print("[large] starting downlink...") + t_dl_start = time.time() + fprime_test_api.clear_histories() + fprime_test_api.send_command( + "FileHandling.fileDownlink.SendFile", [board_target_path, dest_name] + ) + + candidate = Path(downlinker._FileDownlinker__directory) / dest_name + deadline = time.time() + 900 # 15 min ceiling + landed = False + while time.time() < deadline: + if candidate.exists() and candidate.stat().st_size == size: + landed = True + break + time.sleep(1) + t_dl_end = time.time() + elapsed = t_dl_end - t_dl_start + + actual_size = candidate.stat().st_size if candidate.exists() else 0 + print( + f"[large] downlink landed={landed} elapsed={elapsed:.1f}s " + f"size={actual_size}/{size} throughput={(actual_size / elapsed if elapsed > 0 else 0):.1f} B/s" + ) + + assert landed, f"1000-chunk (~{size} byte) downlink did not complete within 900s" + received = candidate.read_bytes() + assert received == expected_bytes, ( + "1000-chunk downlink bytes do not match expected pattern" + ) + print( + f"[large] SUCCESS: {size} bytes downlinked correctly in {elapsed:.1f}s " + f"({size / elapsed:.1f} B/s)" + ) + + +@pytest.mark.parametrize("n_chunks", [1, 3, 5]) +def test_downlink_n_chunks( + fprime_test_api: IntegrationTestAPI, start_gds, tmp_path, n_chunks +): + """Uplink a known-good file (verified via CRC oracle), then downlink it + back and compare the bytes the ground actually received.""" + size = n_chunks * UPLINK_CHUNK_SIZE + local_path = _make_random_file(tmp_path, size, f"downlink_src_{n_chunks}c.bin") + # NOTE: the fprime_gds Uplinker deletes its source file on successful completion + # (FileUplinker.finish() -> os.remove(self.active.source)), so local_path will no + # longer exist after _uplink_and_verify_crc() returns. Snapshot the original bytes + # now for the post-downlink comparison below. + original_bytes = local_path.read_bytes() + board_path = f"/downlink_test_{n_chunks}c.bin" + dest_name = f"DL_{n_chunks}c.bin" + + up = _uplink_and_verify_crc( + fprime_test_api, local_path, board_path, timeout_s=10 + n_chunks * 3 + ) + assert up["uplink_idle"], "setup uplink for downlink test did not finish" + assert up["crc_event"] is not None, ( + "setup uplink for downlink test failed CRC check" + ) + assert up["crc_event"].args[1].val == up["expected_crc"], ( + "setup uplink for downlink test produced wrong CRC on-board -- " + "cannot trust downlink comparison" + ) + + downlinker = fprime_test_api.pipeline.files.downlinker + fprime_test_api.clear_histories() + fprime_test_api.send_command(f"{FILE_DOWNLINK}.SendFile", [board_path, dest_name]) + + # Poll for the downlinked file to land in the GDS storage directory with + # the expected size, or for a timeout. + deadline = time.time() + (10 + n_chunks * 5) + received_path = None + while time.time() < deadline: + candidate = Path(downlinker._FileDownlinker__directory) / dest_name + if candidate.exists() and candidate.stat().st_size == size: + received_path = candidate + break + time.sleep(0.5) + + print( + f"[downlink n_chunks={n_chunks}] size={size} " + f"received_path={received_path} " + f"exists={(Path(downlinker._FileDownlinker__directory) / dest_name).exists()} " + f"actual_size={(Path(downlinker._FileDownlinker__directory) / dest_name).stat().st_size if (Path(downlinker._FileDownlinker__directory) / dest_name).exists() else 'N/A'}" + ) + + assert received_path is not None, ( + f"downlink of {n_chunks} chunks ({size} bytes) did not complete/" + f"arrive at expected size within timeout -- see printed diagnostics" + ) + received_bytes = received_path.read_bytes() + assert received_bytes == original_bytes, ( + f"downlinked bytes for {n_chunks} chunks do not match source " + f"(len received={len(received_bytes)}, len original={len(original_bytes)})" + ) diff --git a/pytest.ini b/pytest.ini index 07ca9309..5214bf9c 100644 --- a/pytest.ini +++ b/pytest.ini @@ -7,6 +7,7 @@ markers = requires_antenna: marks tests that require the antenna board to be plugged in and the burnwire capacitor installed; skip on a bare flight controller requires_battery: marks tests that require the battery board connected with power flowing from the battery terminals; skip on a bare flight controller requires_watchdog_jumper: marks tests that require the JP6 watchdog jumper to be bridged so the watchdog can reset the MCU; skip when JP6 is open + slow: marks slow tests (e.g. large file transfers) that are skipped by default; run explicitly with -m slow filterwarnings = ignore::DeprecationWarning:yamcs\..* ignore::DeprecationWarning:google\.protobuf\..* From 071e4812d9abe0f23b5166e75632981b39e5b530 Mon Sep 17 00:00:00 2001 From: Michael Pham <61564344+Mikefly123@users.noreply.github.com> Date: Sun, 19 Jul 2026 22:45:01 -0700 Subject: [PATCH 2/3] fix(file-handling): default downlinkRepeater to UART-only, fixing issue #457 Root cause (confirmed via HIL A/B testing): Utilities::BufferRepeater only returns a downlink buffer to fileDownlink once every enabled+connected multiOut channel has independently returned it. BufferRepeater's own CHANNEL_ENABLED default is all-channels-ENABLED, but lora.TRANSMIT defaults to DISABLED on every flight reset, so the LoRa channel's comQueue never drains its FILE buffer -> fileDownlink wedges permanently on the very first downlink attempt (matches #457 and #344). Fix: force downlinkRepeater.CHANNEL_ENABLED to [ENABLED, DISABLED, DISABLED] (UART-only) on every boot, right after loadParameters() in ReferenceDeploymentTopology.cpp, mirroring the existing lora.start(..., TransmitState::DISABLED) override pattern already used in this file for project-specific boot-time defaults. paramSet_CHANNEL_ENABLED() is private on the generated component base (command-dispatch only), so the override drives the real command path: build a Fw::CmdArgBuffer and invoke downlinkRepeater's cmdIn port directly with opcode getIdBase() + OPCODE_CHANNEL_ENABLED_SET (0x0). This is a safe-default fix only -- it does not change BufferRepeater's fan-out logic. Re-enabling LoRa downlink requires explicitly setting CHANNEL_ENABLED[1]=ENABLED in lockstep with lora.TRANSMIT ENABLED; HIL testing showed enabling TRANSMIT alone does not recover an already-wedged transfer and even fresh transfers only drain slowly against downlinkDelay's configured cadence. Verified on hardware (PROVES V5e flight controller): downlink 1 and 3 chunk cases pass cleanly from a cold boot with no manual PRM_SET (302B and 612B transfers land correctly and instantly). Uplink 1/3 chunks and downlink-of-a-5-chunk-file remain red -- separate, pre-existing uplink corruption bug above ~3-4 chunks, out of scope for this fix. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_019gMrPNe6LwGtBS6Y7B5bo8 --- .../Top/ReferenceDeploymentTopology.cpp | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/PROVESFlightControllerReference/ReferenceDeployment/Top/ReferenceDeploymentTopology.cpp b/PROVESFlightControllerReference/ReferenceDeployment/Top/ReferenceDeploymentTopology.cpp index e54eedff..b49b224d 100644 --- a/PROVESFlightControllerReference/ReferenceDeployment/Top/ReferenceDeploymentTopology.cpp +++ b/PROVESFlightControllerReference/ReferenceDeployment/Top/ReferenceDeploymentTopology.cpp @@ -9,6 +9,7 @@ // #include // Necessary project-specified types +#include #include #include @@ -118,6 +119,45 @@ void setupTopology(const TopologyState& state) { readParameters(); // Autocoded parameter loading. Function provided by autocoder. loadParameters(); + + // issue #457 fix: force downlinkRepeater.CHANNEL_ENABLED to UART-only [ENABLED, DISABLED, DISABLED] + // on every boot, overriding whatever loadParameters() just restored from PrmDb. + // + // Root cause: BufferRepeater (lib/fprime-extras) only returns a downlink buffer to fileDownlink + // once EVERY enabled+connected multiOut channel has independently returned it. The component's + // own default is all-channels-ENABLED, but LoRa TX defaults to DISABLED on every flight reset (see + // lora.start(..., Zephyr::TransmitState::DISABLED) just below) and a disabled LoRa radio never + // drains its comQueue's FILE buffer -> fileDownlink wedges permanently on the very first downlink + // (matches issues #457/#344; confirmed via HIL A/B test: disabling the LoRa channel here is the + // difference between an instant, correct UART downlink and a downlink that hangs forever). + // + // This is a safe-default fix, not a general one: it does not touch BufferRepeater's fan-out logic, + // so if LoRa downlink is ever wanted, the LoRa channel (index 1) MUST be explicitly re-enabled + // in lockstep with turning lora.TRANSMIT on (e.g. via CHANNEL_ENABLED_PRM_SET before/at the same + // time as lora.TRANSMIT ENABLED) -- enabling TRANSMIT alone does not fix an already-wedged transfer + // and, per HIL testing, even a fresh transfer only drains slowly and unpredictably relative to the + // configured downlinkDelay cadence. SBand (index 2) is disabled here too since it is commented out + // of the topology entirely (see ComCcsds_FileHandling connections in topology.fpp) and would wedge + // fileDownlink identically if it were ever wired back in without an operational com driver behind it. + // + // NOTE: because this runs after loadParameters(), any operator PRM_SAVE of CHANNEL_ENABLED will be + // silently overwritten by this default on the next boot -- that's intentional for now (safe default + // takes priority over a saved override) but worth revisiting if per-mission persistence is needed. + { + // BufferRepeater's paramSet_CHANNEL_ENABLED() is private (only the command-dispatch path may + // call it), so drive it the same way a real CHANNEL_ENABLED_PRM_SET command would: build the + // command argument buffer and invoke the component's cmdIn port directly. Opcode 0x0 is + // OPCODE_CHANNEL_ENABLED_SET (see generated BufferRepeaterComponentAc.hpp) -- the first/only + // settable param on this component, stable as long as BufferRepeater.fpp isn't changed. + Utilities::BufferRepeater_OutputChannelEnables uartOnlyChannelEnables( + {Fw::Enabled::ENABLED, Fw::Enabled::DISABLED, Fw::Enabled::DISABLED}); + Fw::CmdArgBuffer channelEnablesArgs; + (void)channelEnablesArgs.serialize(uartOnlyChannelEnables); + constexpr FwOpcodeType OPCODE_CHANNEL_ENABLED_SET = 0x0; + downlinkRepeater.get_cmdIn_InputPort(0)->invoke(downlinkRepeater.getIdBase() + OPCODE_CHANNEL_ENABLED_SET, 0, + channelEnablesArgs); + } + // Autocoded task kick-off (active components). Function provided by autocoder. startTasks(state); From d89b56836f64d176aefe5eb8494b87cd15353f03 Mon Sep 17 00:00:00 2001 From: Michael Pham <61564344+Mikefly123@users.noreply.github.com> Date: Tue, 21 Jul 2026 07:46:58 -0700 Subject: [PATCH 3/3] fix(uart): RX back-pressure in ZephyrUartDriver, fixing uplink corruption (issue #457) Bumps the fprime-zephyr submodule pin to Open-Source-Space-Foundation/fprime-zephyr branch fix/457-uart-rx-drain (commit 09fdccb), which fixes the UART RX driver's silent byte-drop-on-full-ring-buffer behavior: - schedIn_handler now drains the RX ring buffer in a bounded loop each tick (capped at ring-capacity/read-size iterations) instead of taking a single 64-byte bite, so a backlog can be fully drained in one tick instead of trickling out while more bytes queue up and overrun. - serial_cb (the RX ISR) now checks ring_buf_space_get() before reading a byte out of the hardware FIFO. Once the ring is full, it disables its own RX interrupt (uart_irq_rx_disable) instead of reading-and-dropping. For this board's USB-CDC-backed UART, that makes the underlying stack NAK the host's bulk-OUT endpoint, so unread bytes queue up in the *host's* USB stack (true end-to-end flow control) instead of being silently lost. schedIn_handler re-enables RX once draining frees ring space. - RxRingBufferOverrun (throttled warning event) + RxOverrunCount (telemetry) surface any residual drops, which should no longer occur in normal operation. RAM-neutral: RING_BUF_SIZE stays 1024 bytes; SERIAL_BUFFER_SIZE (64->248) draws from commsBufferManager's existing 1024B-per-buffer pool, adding no new allocation. Verified on hardware (PROVES V5e, RP2350/Zephyr): uplink 1/3/4/5/6/8/10 chunks and 5x5-chunk repeats pass reliably; previously uplinks above 3-4 chunks corrupted ~100% of the time. Zero RX ring-buffer overrun or CCSDS frame-desync events observed across all post-fix test sessions. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01WpBURCutAx8281i59nj6fo --- .../ReferenceDeployment/Top/ReferenceDeploymentPackets.fppi | 6 ++++++ lib/fprime-zephyr | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/PROVESFlightControllerReference/ReferenceDeployment/Top/ReferenceDeploymentPackets.fppi b/PROVESFlightControllerReference/ReferenceDeployment/Top/ReferenceDeploymentPackets.fppi index 472ea4e5..cf7ec374 100644 --- a/PROVESFlightControllerReference/ReferenceDeployment/Top/ReferenceDeploymentPackets.fppi +++ b/PROVESFlightControllerReference/ReferenceDeployment/Top/ReferenceDeploymentPackets.fppi @@ -141,6 +141,8 @@ telemetry packets ReferenceDeploymentPackets { ReferenceDeployment.rateGroup50Hz.RgCycleSlips ReferenceDeployment.rateGroup10Hz.RgCycleSlips ReferenceDeployment.rateGroup1Hz.RgCycleSlips + # issue #457: UART RX ring-buffer overrun counter (ZephyrUartDriver) + comDriver.RxOverrunCount } @@ -241,6 +243,10 @@ telemetry packets ReferenceDeploymentPackets { } } omit { + # issue #457: peripheralUartDriver is the secondary/payload UART; only the + # primary comDriver's RxOverrunCount is included in a packet (HealthWarnings). + ReferenceDeployment.peripheralUartDriver.RxOverrunCount + CdhCore.cmdDisp.CommandErrors # Only has one library, no custom versions CdhCore.version.LibraryVersion02 diff --git a/lib/fprime-zephyr b/lib/fprime-zephyr index 31399714..09fdccbb 160000 --- a/lib/fprime-zephyr +++ b/lib/fprime-zephyr @@ -1 +1 @@ -Subproject commit 313997144363ea930af66e86b52d43537cf7f489 +Subproject commit 09fdccbb5be3b8a0e862c596d91d6b1d02a080e3