Skip to content

Add --dvr-on-signal auto DVR, OSD MSP fixes, and misc VRX-side improvements - #149

Open
moro161 wants to merge 10 commits into
OpenIPC:masterfrom
moro161:dvr-on-signal-auto-record
Open

moro161 wants to merge 10 commits into
OpenIPC:masterfrom
moro161:dvr-on-signal-auto-record

Conversation

@moro161

@moro161 moro161 commented Sep 16, 2026

Copy link
Copy Markdown

Summary

  • --dvr-on-signal: auto-start DVR recording when the air unit's signal is acquired, auto-stop when it's lost (mirrors the existing --mavlink-dvr-on-arm behavior but doesn't require a MAVLink link). Live-toggleable from the GS menu (System → DVR → Auto Record) without a restart.
  • Fix MSP OSD UDP bind failures and truncated aggregated packets (msposd's multi-command datagrams were being silently truncated by an undersized receive buffer, and the socket bind could fail if the port was transiently held at boot).
  • Add air unit SoC temperature sensor readout over the wfb-ng tunnel.
  • Add a hand-tuned 1280x720@100Hz DRM modeline for the Skyzone O4X Pro HDMI-in pairing (stock CVT/CVT-RB/CEA timings corrupt the picture on this RK3566 + O4X Pro combination).
  • Bound the video appsink queues (drop=true max-buffers=1) so a decode/pull stall skips straight to live instead of buffering and fast-forwarding once it clears.

Test plan

  • Built and deployed to a real RunCam WiFiLink-RX (RK3566) ground station; confirmed --dvr-on-signal starts/stops recording on link acquire/loss and the live GS-menu toggle persists correctly.
  • Confirmed the 100Hz modeline renders correctly on a Skyzone O4X Pro.
  • Confirmed OSD telemetry renders without truncation/dropped glyphs after the MSP buffer fix.

🤖 Generated with Claude Code

moro161 and others added 10 commits September 10, 2026 20:16
out_appsink (live RTP pipeline and DVR file playback) set drop=true
with no max-buffers, which defaults to 0 (unlimited) in GStreamer, so
drop=true was a no-op: a decode/pull stall let frames pile up with no
bound, and the picture would catch up in fast-forward once the stall
cleared instead of skipping ahead to live. Add max-buffers=1.

Also trim the restream branch's queue from max-size-time=1s to 150ms
so a slow/stalled restream peer bounds out sooner, and bump the
socket-read packet-rate log from debug to an [FPS-TRACE] info line so
it lines up with the decode/display bottleneck counters.
Drop the once/sec [FPS-TRACE] socket packet-rate log added while
bottleneck-hunting; the decode/handoff/display counters it was meant
to line up with (main.cpp) were never committed and have also been
removed. Back to the original debug-level packet-rate log.
The dev checkout's parent directory has a literal space in its name
("openipc@100hz wifilinkvrx"), which sits above both this repo and
sbc-groundstations. pkg-config emits -I/-L flags as one plain,
unquoted, space-joined string; CMake's FindPkgConfig naively splits
that on whitespace, so any path containing a space loses its -I/-L
prefix. Symptom when configuring a target (aarch64) build against the
sbc-groundstations buildroot sysroot: "fatal error: drm.h: No such
file or directory" from xf86drm.h, plus gcc warnings about sysroot
include paths being passed as bare linker inputs.

tools/cross_configure_target.sh documents the cause and regenerates a
pkg-config wrapper that forces PKG_CONFIG_* to a space-free /tmp
mirror of the buildroot output tree, then configures a build directory
against it. The wrapper's env assignments must be unconditional, not
${VAR:-default}: buildroot's generated toolchainfile.cmake exports
PKG_CONFIG_SYSROOT_DIR (pointing at the real, space-having path) before
pkg-config ever runs, which silently defeats a fallback-style default.
The kernel-generated CVT-RB and standard CVT timings for 1280x720@100
either corrupt the picture (cropping/miscoloring/striping) or produce
no signal at all on this RK3566 board's HDMI-in on a Skyzone O4X Pro
(firmware 4.2.1). Even the "textbook" CEA-family timing captured from
an EDID describing a real Walksnail VRX's expected output still crops
on this unit.

Add the modeline arrived at by hand-tuning porches via live modetest
against this specific VRX+goggles pairing (mainly a larger horizontal
back porch and adjusted vertical front porch), applied only when the
requested mode is exactly 1280x720@100. Also force quant_range=1
(limited range) on the connector in the same atomic commit as the
modeset -- a non-atomic property write was silently ignored and left
colors oversaturated.

This is flagged experimental/hardware-specific, not a general default:
directly verified not to generalize to the textbook timing presumably
correct for other Walksnail VRX + goggles pairings. See issue_draft.md
for the full writeup and the open question about actual Walksnail VRX
HDMI output timing.
Adds a master switch (dvr_on_signal_enabled) that starts DVR recording
when the GS starts receiving data from the air unit and stops it when
that signal drops out. Wired into menu.c's drone_detect_timer -- the
same detected/lost transition that already greys out the drone pages
-- so no separate polling is needed, plus a live GS-menu toggle
(Auto-record on Signal, on the DVR settings page) and a --dvr-on-signal
CLI flag for starting enabled from launch.
New IconSelectorWidget entries in config_osd.json driven by the added
src/icons/*.png set: an armed/disarmed indicator, an 8-point compass
heading icon, and a GPS fix-quality icon (2D/3D/no-fix/none).
Reverts the icon-based widgets and their PNG assets, no longer
referenced anywhere in config_osd.json.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LWYaFJL5iNrmr73dY6Tvuz
The VTX's SoC temp isn't local hardware and isn't known to the flight
controller, so it can't go through the existing os_sensors/MSP paths.
A small script on the VTX opens a plain TCP connection over the
wfb-ng tunnel and writes the temperature as ASCII; VtxTempSensor
listens for it independently of MspDisplayPortWidget telemetry.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LWYaFJL5iNrmr73dY6Tvuz
SO_REUSEADDR: on some boards an unrelated process (wpa_supplicant/
udhcpc sharing a duplicated fd) intermittently holds this port at
boot, failing bind() and silently aborting widget setup -- the
startup preview then stays stuck with nothing left to clear it.

Buffer: msposd aggregates multiple MSP DisplayPort commands per UDP
datagram (wfb-ng's radio_mtu is 1445 bytes), so a full-screen OSD
update can exceed the previous 1024-byte buffer. recvfrom() silently
truncates oversized datagrams, dropping trailing commands (often the
terminating DRAW_SCREEN) and leaving stale glyphs on screen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LWYaFJL5iNrmr73dY6Tvuz
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Add signal-driven DVR and improve VRX video, OSD, and telemetry

✨ Enhancement 🐞 Bug fix 📝 Documentation ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

AI Description

• Auto-record DVR on air-unit signal changes, with live menu and CLI controls.
• Fix MSP OSD reliability and bound video queues to prevent stale playback.
• Add VTX temperature telemetry and a tuned Skyzone 720p100 display mode.
Diagram

graph TD
  AIR["Air Unit"] -->|Tunnel traffic| TUNNEL["Tunnel Input"] -->|Counter changes| SIGNAL{"Signal present?"} -->|Start or stop| DVR["DVR Recorder"]
  AIR -->|TCP temperature| TEMP["VTX Temp Sensor"] -->|Publish metric| OSD["OSD System"]
  AIR -->|MSP UDP| OSD
  VIDEO["Video Pipeline"] -->|Recording stream| DVR
  VIDEO -->|Live frames| DRM["DRM Output"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Receiver link-state callbacks
  • ➕ Avoids polling a global traffic counter every 500 milliseconds.
  • ➕ Can distinguish receiver state from unrelated tunnel traffic.
  • ➕ Centralizes link acquisition and timeout semantics. channels
  • ➖ Requires deeper receiver-to-UI event wiring.
  • ➖ Could introduce synchronization concerns across receiver and LVGL threads.
  • ➖ Duplicates the established drone-detection behavior unless that subsystem is refactored.
2. Structured telemetry for VTX temperature
  • ➕ Reuses an authenticated or framed telemetry transport.
  • ➕ Supports future VTX metrics without adding dedicated TCP ports.
  • ➕ Provides explicit message validation and versioning.
  • ➖ Requires coordinated air-unit protocol and deployment changes.
  • ➖ MSP telemetry originates from the flight controller and may not expose VTX SoC data.
  • ➖ Adds more integration scope than the simple one-value TCP feed.

Recommendation: The current signal-counter approach is appropriate because it deliberately reuses the ground station's existing VTX-presence semantics and keeps auto-record independent of MAVLink. The dedicated temperature feed is pragmatic for the present deployment, although a structured, extensible VTX telemetry protocol would be preferable if additional remote metrics are planned.

Files changed (12) +318 / -6

Enhancement (8) +186 / -2
drm.cAdd a tuned Skyzone 720p100 DRM mode +40/-0

Add a tuned Skyzone 720p100 DRM mode

• Selects a hand-tuned 1280x720@100Hz user-defined mode before connector mode matching. It also requests limited output range for that timing and logs the calculated requested refresh rate.

src/drm.c

dvr.cppDefine the signal-recording master switch +3/-0

Define the signal-recording master switch

• Introduces the shared runtime flag controlling whether air-unit signal transitions drive DVR recording.

src/dvr.cpp

dvr.hExpose signal-driven DVR state +6/-0

Expose signal-driven DVR state

• Declares the shared 'dvr_on_signal_enabled' switch and documents its relationship to ground-station drone detection.

src/dvr.h

colmenu_pages.cAdd the DVR Auto Record menu switch +8/-1

Add the DVR Auto Record menu switch

• Adds a live DVR menu callback and an Auto Record switch for signal-driven recording. The DVR page item count is increased to include the new control.

src/gsmenu/colmenu_pages.c

main.cppWire DVR CLI controls and VTX temperature monitoring +19/-1

Wire DVR CLI controls and VTX temperature monitoring

• Parses '--dvr-on-signal', exposes C-callable live toggle accessors, and documents the option in CLI help. It also starts the tunneled VTX temperature listener on TCP port 15552.

src/main.cpp

menu.cTrigger DVR from air-unit detection transitions +7/-0

Trigger DVR from air-unit detection transitions

• Extends the existing tunnel-traffic detector to start recording when the VTX appears and stop when it disappears. Automatic actions are gated by the runtime switch and disabled in simulator builds.

src/menu.c

os_mon.cppReceive and publish air-unit SoC temperature +100/-0

Receive and publish air-unit SoC temperature

• Adds a threaded TCP sensor that accepts ASCII temperature samples, stores the latest value atomically, and publishes it as 'vtx.temperature'. Socket shutdown and worker joining provide controlled teardown.

src/os_mon.cpp

os_mon.hppExpose VTX temperature sensor registration +3/-0

Expose VTX temperature sensor registration

• Adds the 'OsSensors::addVtxTemp' API for registering a tunneled TCP temperature source.

src/os_mon.hpp

Bug fix (2) +36 / -4
gstrtpreceiver.cppBound live, playback, and restream latency +15/-3

Bound live, playback, and restream latency

• Limits live and DVR playback appsinks to one buffer so stalled consumers drop stale frames. Reduces the leaky restream queue threshold from one second to 150 milliseconds.

src/gstrtpreceiver.cpp

osd.cppPrevent MSP OSD bind failures and packet truncation +21/-1

Prevent MSP OSD bind failures and packet truncation

• Enables address reuse on the MSP UDP socket to improve startup binding reliability. Expands the receive buffer from 1024 to 8192 bytes and warns when a datagram may still have filled it.

src/osd.cpp

Documentation (1) +6 / -0
manpage.mdDocument signal-driven DVR recording +6/-0

Document signal-driven DVR recording

• Adds the '--dvr-on-signal' option to the command summary and detailed Debian manpage reference.

debian/manpage.md

Other (1) +90 / -0
cross_configure_target.shAdd a space-safe Buildroot cross-configuration helper +90/-0

Add a space-safe Buildroot cross-configuration helper

• Creates a temporary space-free symlink and pkgconf wrapper before configuring an aarch64 Buildroot build. This avoids CMake splitting pkg-config include and library paths whose parent directories contain spaces.

tools/cross_configure_target.sh

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (6) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Auto record ignores an existing signal 🐞 Bug ≡ Correctness
Description
dvr_set_on_signal only changes the feature flag, while drone_detect_timer starts recording
exclusively inside a later detected-state transition. Enabling Auto Record after the air unit is
already detected therefore leaves recording stopped until the signal is lost and acquired again.
Code

src/main.cpp[R848-851]

+    // Live toggle for --dvr-on-signal, so the GS menu can flip it without a
+    // restart (dvr_on_signal_enabled itself is read by menu.c's
+    // drone_detect_timer on every signal-acquired/lost transition).
+    void dvr_set_on_signal(int enabled) { dvr_on_signal_enabled = (bool)enabled; }
Evidence
The setter writes only dvr_on_signal_enabled; the timer keeps detection in a function-local static
and enters the DVR calls only when now != detected. The menu subsystem exposes the current
detected state, but the new callback does not consult it.

src/main.cpp[848-852]
src/menu.c[62-87]
src/gsmenu/colmenu.c[1099-1111]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Enabling Auto Record while signal detection is already active does not start recording because the setter does not reconcile the current detected state.

## Fix Focus Areas
- src/main.cpp[848-852]
- src/menu.c[62-87]
- src/gsmenu/colmenu.c[1099-1111]

## Recommended Fix
Store or expose the current signal state to the DVR automation and reconcile recording ownership immediately when the feature is enabled, rather than waiting for another signal transition.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Signal loss cuts off armed recordings 🐞 Bug ≡ Correctness
Description
drone_detect_timer calls the global dvr_stop_all whenever detection is lost, without tracking
whether signal automation started the active recording. When signal automation coexists with manual
recording or recording-on-arm, a transient two-second link loss clears the shared recording state
and stops both recorder branches.
Code

src/menu.c[R82-85]

+        if(dvr_on_signal_enabled) {
+            if(detected) dvr_start_all();
+            else         dvr_stop_all();
+        }
Evidence
Manual controls and MAVLink arm handling use the same global start/stop functions as the new signal
handler. Those functions maintain one dvr_enabled flag, and dvr_stop_all disables both raw and
re-encoded recording regardless of which path originally requested them.

src/menu.c[32-45]
src/menu.c[71-85]
src/mavlink.c[144-154]
src/main.cpp[738-758]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Signal-loss automation stops the shared DVR unconditionally, including recordings requested manually or by the armed-state automation.

## Fix Focus Areas
- src/menu.c[32-45]
- src/menu.c[62-87]
- src/mavlink.c[144-154]
- src/main.cpp[738-758]

## Recommended Fix
Represent manual, signal, and armed recording requests as independent intents, derive the effective recording state from their union, and stop the backends only when no intent remains active.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. The menu cannot enable auto recording 🐞 Bug ≡ Correctness
Description
do_set() routes the new dvr_on_signal row through the generic gsmenu.sh command path before
invoking on_dvr_on_signal(), but the bundled script defines neither a getter nor a setter for that
parameter. Opening or toggling Auto Record reaches the script’s unknown-command failure, and because
the dispatcher runs callbacks only after a successful command, dvr_set_on_signal never updates
dvr_on_signal_enabled on hardware.
Code

src/gsmenu/colmenu_pages.c[440]

+    { .kind=COLMENU_SWITCH,   .label="Auto Record",       .param="dvr_on_signal",        .on_change=on_dvr_on_signal },
Evidence
The new row registers and requests dvr_on_signal, while normal menu reads and writes invoke
gsmenu.sh, and the write dispatcher runs on_change only after a zero exit status. The shipped
script includes cases for the sibling DVR settings but omits this parameter, so both reads and
writes reach the unsupported-command branch, exit with status 1, and prevent the callback from
applying the setting.

src/gsmenu/colmenu_pages.c[110-113]
src/gsmenu/colmenu_pages.c[437-449]
src/gsmenu/colmenu.c[399-420]
gsmenu.sh[414-473]
gsmenu.sh[558-563]
src/gsmenu/colmenu_pages.c[110-114]
src/gsmenu/colmenu.c[388-420]
gsmenu.sh[414-470]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new Auto Record menu row uses `dvr_on_signal`, but the bundled `gsmenu.sh` command interface does not support reading or writing this parameter. Consequently, reads fail and asynchronous writes complete unsuccessfully before the app-side callback can update the runtime flag.

## Fix Focus Areas
- src/gsmenu/colmenu_pages.c[110-114]
- src/gsmenu/colmenu_pages.c[438-449]
- src/gsmenu/colmenu_pages.c[440-440]
- gsmenu.sh[414-473]
- gsmenu.sh[414-470]
- src/gsmenu/colmenu.c[399-420]

## Recommended Fix
Add matching `get gs system dvr_on_signal` and `set gs system dvr_on_signal` cases to `gsmenu.sh`, following the project’s intended persistent GS configuration mechanism. Ensure the getter returns the persisted switch value and the setter exits successfully so `on_dvr_on_signal()` executes; alternatively, handle this parameter entirely in the app like the existing runtime-only menu parameters.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View action required (1)
4. Shutdown hangs on idle temperature client 🐞 Bug ☼ Reliability
Description
VtxTempSensor::listenLoop() performs a blocking read() on an accepted client descriptor, while
its destructor sets running, shuts down only the listening descriptor, and then joins the worker
thread. A client that connects without sending data or closing its connection leaves the worker
blocked on the separate accepted descriptor, so process teardown waits indefinitely in
worker.join().
Code

src/os_mon.cpp[R451-457]

+            int fd = accept(server_fd, reinterpret_cast<sockaddr *>(&client), &len);
+            if (fd < 0) {
+                continue; // server_fd was closed (shutdown) -> loop exits on `running` check
+            }
+            char buf[64] = {0};
+            ssize_t n = read(fd, buf, sizeof(buf) - 1);
+            if (n > 0) {
Evidence
The worker synchronously reads from the accepted client descriptor and closes it only after read()
returns. The destructor changes running, closes only server_fd, and immediately joins the
worker; neither changing the flag nor closing the separate listening descriptor releases a read()
blocked on the active client descriptor.

src/os_mon.cpp[421-429]
src/os_mon.cpp[447-465]
src/main.cpp[131-146]
src/main.cpp[1781-1784]
src/os_mon.cpp[421-430]
src/main.cpp[1081-1090]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The VTX temperature worker can block indefinitely while reading from an accepted TCP client. Shutdown changes `running` and closes only the listening socket, so it cannot wake a worker blocked on an idle accepted socket, preventing the destructor from joining the thread.

## Fix Focus Areas
- src/os_mon.cpp[421-430]
- src/os_mon.cpp[447-465]

## Recommended Fix
Make accepted-client reads interruptible during shutdown. Retain the active client descriptor under synchronization and shut it down from the destructor before joining, and/or use a finite receive timeout or nonblocking I/O with polling that checks `running`. Ensure the client descriptor is closed on every exit path before the worker terminates.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

5. Temperature readouts show stale data 🐞 Bug ≡ Correctness
Description
VtxTempSensor initializes latest to zero and publishes it on every run without recording whether
a valid sample exists or when it arrived. Before the first connection the readout reports zero, and
after the feed disappears it republishes the final temperature indefinitely as current data.
Code

src/os_mon.cpp[R436-439]

+        osd_tag tags[1];
+        strcpy(tags[0].key, "name");
+        strcpy(tags[0].val, "vtx");
+        osd_publish_double_fact("vtx.temperature", tags, 1, latest.load());
Evidence
The atomic value starts at 0.0, is updated only after parseable input, and is published
unconditionally by every sensor run. The main loop runs sensors once per second, so absent
connections continually emit either the initial zero or the last successful value.

src/os_mon.cpp[432-440]
src/os_mon.cpp[455-472]
src/main.cpp[1081-1089]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The remote temperature sensor publishes an initial zero and indefinitely republishes old samples without any validity or freshness state.

## Fix Focus Areas
- src/os_mon.cpp[432-440]
- src/os_mon.cpp[455-472]
- src/main.cpp[1081-1089]

## Recommended Fix
Track whether a complete valid sample has arrived and its timestamp; suppress or invalidate the fact until the first sample and after a configured freshness timeout.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. Relative paths break cross-build setup 🐞 Bug ≡ Correctness
Description
cross_configure_target.sh preserves an explicitly supplied relative SBC_DIR and uses it directly
as the target of a symlink located under /tmp. Because that target is resolved relative to /tmp,
a valid relative argument can pass the initial toolchain check but produce a broken mirror and an
unusable pkg-config wrapper.
Code

tools/cross_configure_target.sh[R43-44]

+BUILD_DIR="${1:-$REPO_DIR/build_target}"
+SBC_DIR="${2:-$REPO_DIR/../sbc-groundstations}"
Evidence
The second argument is accepted without normalization, while NOSPACE_ROOT is always under /tmp
and all generated host/sysroot paths traverse that symlink. Thus a relative target that was valid
from the caller's directory points somewhere else once installed under /tmp.

tools/cross_configure_target.sh[41-59]
tools/cross_configure_target.sh[61-72]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A relative SBC repository argument is reused as a `/tmp` symlink target, where it resolves from a different directory and breaks the generated wrapper paths.

## Fix Focus Areas
- tools/cross_configure_target.sh[41-59]
- tools/cross_configure_target.sh[61-72]

## Recommended Fix
After validating the directory, canonicalize `SBC_DIR` to an absolute path with `cd` and `pwd` before deriving the toolchain path or creating the `/tmp` symlink.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
Review mode: 🧠 Deep: This is a bug-dense, cross-cutting behavioral change spanning DVR state transitions, networking and threading, sensor I/O, DRM modesetting, GStreamer buffering, OSD packet handling, and build tooling, creating multiple independent opportunities for subtle defects.

Grey Divider

Tip of the day
💡 Did you know, you can group findings by type and pick your Finding display, from Minimal to Full

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread src/main.cpp
Comment on lines +848 to +851
// Live toggle for --dvr-on-signal, so the GS menu can flip it without a
// restart (dvr_on_signal_enabled itself is read by menu.c's
// drone_detect_timer on every signal-acquired/lost transition).
void dvr_set_on_signal(int enabled) { dvr_on_signal_enabled = (bool)enabled; }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Auto record ignores an existing signal 🐞 Bug ≡ Correctness

dvr_set_on_signal only changes the feature flag, while drone_detect_timer starts recording
exclusively inside a later detected-state transition. Enabling Auto Record after the air unit is
already detected therefore leaves recording stopped until the signal is lost and acquired again.
Agent Prompt
## Issue description
Enabling Auto Record while signal detection is already active does not start recording because the setter does not reconcile the current detected state.

## Fix Focus Areas
- src/main.cpp[848-852]
- src/menu.c[62-87]
- src/gsmenu/colmenu.c[1099-1111]

## Recommended Fix
Store or expose the current signal state to the DVR automation and reconcile recording ownership immediately when the feature is enabled, rather than waiting for another signal transition.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread src/menu.c
Comment on lines +82 to +85
if(dvr_on_signal_enabled) {
if(detected) dvr_start_all();
else dvr_stop_all();
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

2. Signal loss cuts off armed recordings 🐞 Bug ≡ Correctness

drone_detect_timer calls the global dvr_stop_all whenever detection is lost, without tracking
whether signal automation started the active recording. When signal automation coexists with manual
recording or recording-on-arm, a transient two-second link loss clears the shared recording state
and stops both recorder branches.
Agent Prompt
## Issue description
Signal-loss automation stops the shared DVR unconditionally, including recordings requested manually or by the armed-state automation.

## Fix Focus Areas
- src/menu.c[32-45]
- src/menu.c[62-87]
- src/mavlink.c[144-154]
- src/main.cpp[738-758]

## Recommended Fix
Represent manual, signal, and armed recording requests as independent intents, derive the effective recording state from their union, and stop the backends only when no intent remains active.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread src/os_mon.cpp
Comment on lines +436 to +439
osd_tag tags[1];
strcpy(tags[0].key, "name");
strcpy(tags[0].val, "vtx");
osd_publish_double_fact("vtx.temperature", tags, 1, latest.load());

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

5. Temperature readouts show stale data 🐞 Bug ≡ Correctness

VtxTempSensor initializes latest to zero and publishes it on every run without recording whether
a valid sample exists or when it arrived. Before the first connection the readout reports zero, and
after the feed disappears it republishes the final temperature indefinitely as current data.
Agent Prompt
## Issue description
The remote temperature sensor publishes an initial zero and indefinitely republishes old samples without any validity or freshness state.

## Fix Focus Areas
- src/os_mon.cpp[432-440]
- src/os_mon.cpp[455-472]
- src/main.cpp[1081-1089]

## Recommended Fix
Track whether a complete valid sample has arrived and its timestamp; suppress or invalidate the fact until the first sample and after a configured freshness timeout.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +43 to +44
BUILD_DIR="${1:-$REPO_DIR/build_target}"
SBC_DIR="${2:-$REPO_DIR/../sbc-groundstations}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

6. Relative paths break cross-build setup 🐞 Bug ≡ Correctness

cross_configure_target.sh preserves an explicitly supplied relative SBC_DIR and uses it directly
as the target of a symlink located under /tmp. Because that target is resolved relative to /tmp,
a valid relative argument can pass the initial toolchain check but produce a broken mirror and an
unusable pkg-config wrapper.
Agent Prompt
## Issue description
A relative SBC repository argument is reused as a `/tmp` symlink target, where it resolves from a different directory and breaks the generated wrapper paths.

## Fix Focus Areas
- tools/cross_configure_target.sh[41-59]
- tools/cross_configure_target.sh[61-72]

## Recommended Fix
After validating the directory, canonicalize `SBC_DIR` to an absolute path with `cd` and `pwd` before deriving the toolchain path or creating the `/tmp` symlink.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

static const colmenu_page_t sys_display_page = { "Display", "gs", "system", sys_display_items, 5 };
static const colmenu_item_t sys_dvr_items[] = {
{ .kind=COLMENU_SWITCH, .label="Enabled", .param="rec_enabled", .on_change=on_rec_enabled },
{ .kind=COLMENU_SWITCH, .label="Auto Record", .param="dvr_on_signal", .on_change=on_dvr_on_signal },

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

3. The menu cannot enable auto recording 🐞 Bug ≡ Correctness

do_set() routes the new dvr_on_signal row through the generic gsmenu.sh command path before
invoking on_dvr_on_signal(), but the bundled script defines neither a getter nor a setter for that
parameter. Opening or toggling Auto Record reaches the script’s unknown-command failure, and because
the dispatcher runs callbacks only after a successful command, dvr_set_on_signal never updates
dvr_on_signal_enabled on hardware.
Agent Prompt
## Issue description
The new Auto Record menu row uses `dvr_on_signal`, but the bundled `gsmenu.sh` command interface does not support reading or writing this parameter. Consequently, reads fail and asynchronous writes complete unsuccessfully before the app-side callback can update the runtime flag.

## Fix Focus Areas
- src/gsmenu/colmenu_pages.c[110-114]
- src/gsmenu/colmenu_pages.c[438-449]
- src/gsmenu/colmenu_pages.c[440-440]
- gsmenu.sh[414-473]
- gsmenu.sh[414-470]
- src/gsmenu/colmenu.c[399-420]

## Recommended Fix
Add matching `get gs system dvr_on_signal` and `set gs system dvr_on_signal` cases to `gsmenu.sh`, following the project’s intended persistent GS configuration mechanism. Ensure the getter returns the persisted switch value and the setter exits successfully so `on_dvr_on_signal()` executes; alternatively, handle this parameter entirely in the app like the existing runtime-only menu parameters.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread src/os_mon.cpp
Comment on lines +451 to +457
int fd = accept(server_fd, reinterpret_cast<sockaddr *>(&client), &len);
if (fd < 0) {
continue; // server_fd was closed (shutdown) -> loop exits on `running` check
}
char buf[64] = {0};
ssize_t n = read(fd, buf, sizeof(buf) - 1);
if (n > 0) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

4. Shutdown hangs on idle temperature client 🐞 Bug ☼ Reliability

VtxTempSensor::listenLoop() performs a blocking read() on an accepted client descriptor, while
its destructor sets running, shuts down only the listening descriptor, and then joins the worker
thread. A client that connects without sending data or closing its connection leaves the worker
blocked on the separate accepted descriptor, so process teardown waits indefinitely in
worker.join().
Agent Prompt
## Issue description
The VTX temperature worker can block indefinitely while reading from an accepted TCP client. Shutdown changes `running` and closes only the listening socket, so it cannot wake a worker blocked on an idle accepted socket, preventing the destructor from joining the thread.

## Fix Focus Areas
- src/os_mon.cpp[421-430]
- src/os_mon.cpp[447-465]

## Recommended Fix
Make accepted-client reads interruptible during shutdown. Retain the active client descriptor under synchronization and shut it down from the destructor before joining, and/or use a finite receive timeout or nonblocking I/O with polling that checks `running`. Ensure the client descriptor is closed on every exit path before the worker terminates.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant