diff --git a/docs/user-guide.md b/docs/user-guide.md index 93fb4b178..703998c09 100644 --- a/docs/user-guide.md +++ b/docs/user-guide.md @@ -303,7 +303,9 @@ All endpoints return JSON. In simulated mode, data is generated from a determini ## Data Sources The `--source` flag controls where CSI data comes from. Valid values are -`auto`, `esp32`, `wifi`, and `simulated` (alias `simulate`). Any other value +`auto`, `esp32`, `wifi`, and `simulated` (alias `simulate`), plus the vendor +feeds `mediatek`, `qualcomm`, `realtek` (RTL8720F radar), and `realtek_csi` +(RTL8721Dx CSI), which bind the UDP receiver like `esp32`. Any other value stops the server at startup with an error that lists them. ### Simulated Mode (No Hardware) @@ -1287,7 +1289,7 @@ The Rust sensing server binary accepts the following flags: | Flag | Default | Description | |------|---------|-------------| -| `--source` | `auto` | Data source: `auto`, `esp32`, `wifi`, `simulated` (alias `simulate`). Other values are rejected at startup | +| `--source` | `auto` | Data source: `auto`, `esp32`, `wifi`, `simulated` (alias `simulate`), `mediatek`, `qualcomm`, `realtek`, `realtek_csi`. Other values are rejected at startup | | `--http-port` | `8080` | HTTP port for REST API and UI | | `--ws-port` | `8765` | WebSocket port | | `--udp-port` | `5005` | UDP port for ESP32 CSI frames | diff --git a/v2/crates/wifi-densepose-sensing-server/src/main.rs b/v2/crates/wifi-densepose-sensing-server/src/main.rs index ebf928793..addfb393d 100644 --- a/v2/crates/wifi-densepose-sensing-server/src/main.rs +++ b/v2/crates/wifi-densepose-sensing-server/src/main.rs @@ -4659,8 +4659,20 @@ struct SourcePlan { /// Every `--source` value [`plan_source`] accepts. Clap rejects anything else /// at startup (issue #2097): an unknown value used to start the server with no -/// data task while `/api/v1/status` still reported a live source. -const SOURCE_VALUES: [&str; 5] = ["auto", "esp32", "wifi", "simulated", "simulate"]; +/// data task while `/api/v1/status` still reported a live source. The vendor +/// names match the labels the UDP receiver writes for MTC1, RAC1, Qualcomm +/// and RTL8720F radar frames, so `effective_source()` ages them correctly. +const SOURCE_VALUES: [&str; 9] = [ + "auto", + "esp32", + "wifi", + "simulated", + "simulate", + "mediatek", + "qualcomm", + "realtek", + "realtek_csi", +]; /// Pure decision function — fully unit-testable without binding sockets. /// @@ -4721,6 +4733,16 @@ fn plan_source( run_simulator: false, run_wifi: true, }, + // Vendor CSI and radar frames arrive on the same UDP receiver as ESP32 + // frames. Before #2097 these names fell into a catch-all that bound no + // receiver, so nothing was ever ingested. Never run the ESP32-shaped + // simulator alongside a vendor feed. + "mediatek" | "qualcomm" | "realtek" | "realtek_csi" => SourcePlan { + initial_source: requested.to_string(), + bind_udp: true, + run_simulator: false, + run_wifi: false, + }, other => { return Err(format!( "unknown --source '{other}' (valid values: {})", @@ -4924,9 +4946,48 @@ mod issue_2097_status_tests { } } + #[test] + fn vendor_sources_bind_the_udp_receiver() { + for vendor in ["mediatek", "qualcomm", "realtek", "realtek_csi"] { + let plan = plan_source(vendor, false, false).expect("vendor source plans"); + assert!(plan.bind_udp, "{vendor}: vendor frames arrive over UDP"); + assert!(!plan.run_simulator, "{vendor}: no ESP32-shaped simulator"); + assert!(!plan.run_wifi); + assert_eq!(plan.initial_source, vendor); + } + } + + #[test] + fn only_garbage_is_rejected() { + for bogus in [ + "bogus", + "macos", + "linux", + "mock", + "MEDIATEK", + "mediatek:simulated", + "esp32 ", + ] { + assert!( + Args::try_parse_from(["sensing-server", "--source", bogus]).is_err(), + "--source {bogus:?} must be rejected" + ); + } + } + #[test] fn every_documented_source_still_parses() { - for source in ["auto", "esp32", "wifi", "simulated", "simulate"] { + for source in [ + "auto", + "esp32", + "wifi", + "simulated", + "simulate", + "mediatek", + "qualcomm", + "realtek", + "realtek_csi", + ] { let args = Args::try_parse_from(["sensing-server", "--source", source]) .unwrap_or_else(|e| panic!("--source {source} must parse: {e}")); assert_eq!(args.source, source);