Software validation and emulationLink to the section Software validation and emulation

On this page

Run the reference implementations without an aircraft or DJI Dock. The workflow combines protocol emulators, clean package installation, network faults, recording/replay, .NET interoperability and a recorded browser demonstration. Results are interoperability evidence, not conformance claims.

Run the software milestoneLink to the section Run the software milestone

Requirements: Python 3.12+, .NET 10, FFmpeg with libx264, Google Chrome, and coturn (turnserver on PATH, or UNIVIP_TURNSERVER set to its executable). Ubuntu packages: ffmpeg coturn. macOS: coturn is available through Homebrew. Tests start an authenticated loopback coturn process and stop it afterward; no system service is started.

From the repository root:

python3.12 -m venv gateway/.venv
gateway/.venv/bin/python -m pip install -e 'gateway[media,mavlink,dji,test]' build
gateway/.venv/bin/python tools/run_software_checks.py

The command exits nonzero on a failed check or missing current-run demonstration evidence. It retains Markdown/JSON reports, logs, JUnit/TRX results, screenshots and browser video under output/software-validation-<date>/. Skips are counted explicitly. The same command can run on an Ubuntu CI runner with these dependencies installed.

CoverageLink to the section Coverage

Software validation paths

Protocol fakes and real autopilot software connect through separate adapters to the reference gateway. The SDK and viewer carry telemetry and authorized commands, while synthetic video reaches the browser through TURN. Reports preserve the evidence from these distinct paths.

View Mermaid source
flowchart TB
    accTitle: Software validation paths
    accDescr: Protocol fakes and real autopilot software connect through separate adapters to the reference gateway. The SDK and viewer carry telemetry and authorized commands, while synthetic video reaches the browser through TURN. Reports preserve the evidence from these distinct paths.
    FakeMav["Fake PX4"] -->|MAVLink| Gateway["Python gateway"]
    FakeDock["Fake DJI Dock"] -->|MQTT| Gateway
    SITL["PX4 / ArduCopter SITL"] -->|MAVLink| Gateway
    Gateway -->|telemetry and events| SDK[".NET SDK and viewer"]
    SDK -->|leased commands| Gateway
    SDK -->|UI and state| Browser["Chrome browser"]
    Gateway -->|synthetic H.264| TURN["Authenticated loopback TURN"]
    TURN --> Browser
    Browser --> Evidence["Recordings, screenshots and reports"]
Check Evidence
Standalone package Build an sdist, build its wheel outside the checkout, install into a fresh environment, compare all ten bundled schemas and validate telemetry without repository paths
DJI launch preparation Unconfirmable REST cancellation returns 409; vehicle.cancel rejects with BUSY; failed stopping after lease loss reports FAILED. The fake continues launch, exposing any false cancellation promise
Failsafe authority Both gateways refuse new movement while the controller reports a failsafe return
Proxy ownership Two authenticated callers sharing one device-facing identity cannot delete each other's views; encoded paths obey the same check and ownership survives device-session reconnection
Media limits Per-client quotas combine WHEP and session-channel views; device limits still apply; Python INTERNAL errors omit exception details
Network faults Native MAVLink bytes traverse a seeded loopback UDP relay with delay, jitter, loss, duplication, outage and recovery
Recording H.264, telemetry and ST 0601 replay; missing telemetry; remuxed timestamps; independent burnt-in frame counters; deliberately corrupted clock mapping remains measurable
Browser demonstration Fake PX4 over MAVLink and fake DJI Dock over MQTT connect through the Python gateway to the .NET viewer. Takeoff and a short leg use authenticated UNIVIP commands. Chrome decodes synthetic H.264 through authenticated TURN; the operator holds the vehicle and changes zoom under separate leases

The recordings label the emulator and synthetic video. iceTransportPolicy: relay is mandatory and the selected local candidate must actually be relay. This is an authenticated loopback relay test. Public-network NAT behavior, WAN latency, radios and real camera capture timestamps need separate validation.

Observed on 2026-10-16 at source commit e1ff3d8: the local milestone passed 593 tests with no failures and two explicit macOS TLS skips, including both recorded TURN demonstrations. Linux .NET CI passed all 142 gateway tests without skips. The separate autopilot run passed all 20 SITL cases. These are separate runs, not one combined test count.

Browser ICE configurationLink to the section Browser ICE configuration

Use Viewer:Video:IceServers entries with Urls, Username, Credential, and Viewer:Video:IceTransportPolicy (all or relay). TURN entries require credentials; relay policy requires a TURN entry. The protected /api/config response is not cached.

{"Viewer":{"Video":{
  "IceServers":[{"Urls":["turn:turn.example.org:3478?transport=udp"],
    "Username":"example-user","Credential":"example-turn-credential"}],
  "IceTransportPolicy":"relay"
}}}

These settings configure the browser peer. The .NET gateway remains ICE-lite and requires reachable host candidates; it has not acquired a full ICE implementation. Python gateway ICE entries also accept credential-bearing objects with urls, username and credential beside plain URL strings.

Both gateways expose media.max_peers_per_client (default 2) alongside media.max_peers (default 4). Counts span WHEP and session signaling using the authenticated client_id.

Hosted proxy caller identityLink to the section Hosted proxy caller identity

The authentication callback supplies a stable UnivipProxyCaller.CallerId, or the caller has a ClaimTypes.NameIdentifier claim. Video POST/DELETE without identity is refused. The endpoint keeps the id locally; it never forwards it to the device or substitutes it for the audit-only operator_user. A creator, or a caller with univip.admin, may delete a known view; other callers receive 404.

Autopilot software in the loopLink to the section Autopilot software in the loop

Protocol fakes are separate from real autopilot software. The SITL harness runs pinned PX4 and ArduCopter binaries. Extended cases cover gateway restart, lease expiry during motion, GNSS loss/recovery and controller geofence enforcement, alongside the original commands and failsafes. Parameter writes use negotiated wire encoding and require read-back acknowledgments. Actual GPS disable/recovery is exercised on ArduCopter. The pinned PX4 SIH build did not acknowledge GPS failure injection; its separate case verifies missing GPS messages and recovery at the gateway boundary while the controller GPS remains available. This is not a PX4 GPS-failsafe claim.

Build on Ubuntu with tools/sitl/build_px4.sh and tools/sitl/build_ardupilot.sh, then run:

UNIVIP_SITL_PX4="$HOME/sitl/PX4-Autopilot/build/px4_sitl_default/bin/px4" \
UNIVIP_SITL_ARDUPILOT="$HOME/sitl/ardupilot/build/sitl/bin/arducopter" \
gateway/.venv/bin/python tools/run_software_checks.py --include-sitl

An explicitly requested SITL run fails if either binary is missing. The manual sitl GitHub Actions workflow builds both autopilots and runs their tests. Check the completed job logs for observed results; adding test cases alone does not establish a pass.

Configuration references: ArduPilot simulation parameters, cylindrical fences, PX4 failure injection. Copter 4.7.2 uses SIM_GPS1_ENABLE instead of the older SIM_GPS_DISABLE.

Platform and release limitsLink to the section Platform and release limits

The .NET macOS TLS backend used here rejects explicit TLS 1.3 with PlatformNotSupportedException. Two gateway TLS tests explicitly skip on macOS and continue to run on Linux CI. Loopback development remains available on macOS; the protocol TLS requirement is unchanged.

Real vehicle authority, physical pilot override, DJI hardware conventions, independent security review and certification remain outside this software milestone. See the roadmap.

Generated from docs/software-validation.md.