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.pyThe 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
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.
Scroll horizontally to see the whole diagram.
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-sitlAn 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.