Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# The contributor harness hashes provenance inputs byte-for-byte. Keep text
# files in this boundary on LF even when Windows enables core.autocrlf.
harness/ruview/** text=auto eol=lf
harness/homecore/** text=auto eol=lf
39 changes: 37 additions & 2 deletions v2/crates/ruview-offaxis/src/projection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,17 @@ impl Screen {
let vr = (pb - pa)
.normalize()
.ok_or(OffAxisError::DegenerateScreen)?;
let vu = (pc - pa)
let vu_raw = (pc - pa)
.normalize()
.ok_or(OffAxisError::DegenerateScreen)?;
let vn = vr
.cross(vu)
.cross(vu_raw)
.normalize()
.ok_or(OffAxisError::DegenerateScreen)?;
// Orthonormalize vu: guarantees that {vr, vu, vn} forms an exact
// right-handed orthonormal basis (zero shear / pure rotation matrix).
let vu = vn
.cross(vr)
.normalize()
.ok_or(OffAxisError::DegenerateScreen)?;
Ok(Self {
Expand Down Expand Up @@ -446,4 +452,33 @@ mod tests {
OffAxisError::InvalidClipPlanes
);
}

#[test]
fn screen_basis_is_strictly_orthonormal_under_skewed_corners() {
// pa, pb, pc where pc is intentionally slightly skewed (85 degrees instead of 90 degrees)
let pa = Vec3::new(0.0, 0.0, 0.0);
let pb = Vec3::new(1.0, 0.0, 0.0);
let pc = Vec3::new(0.1, 0.9, 0.0);
let screen = Screen::new(pa, pb, pc).unwrap();

let vr = screen.vr();
let vu = screen.vu();
let vn = screen.vn();

// Check unit lengths
assert_close(vr.dot(vr), 1.0, EPS, "vr length");
assert_close(vu.dot(vu), 1.0, EPS, "vu length");
assert_close(vn.dot(vn), 1.0, EPS, "vn length");

// Check pairwise orthogonality
assert_close(vr.dot(vu), 0.0, EPS, "vr . vu");
assert_close(vr.dot(vn), 0.0, EPS, "vr . vn");
assert_close(vu.dot(vn), 0.0, EPS, "vu . vn");

// Check right-handed orientation: vr x vu == vn
let cross = vr.cross(vu);
assert_close(cross.x, vn.x, EPS, "cross.x");
assert_close(cross.y, vn.y, EPS, "cross.y");
assert_close(cross.z, vn.z, EPS, "cross.z");
}
}
2 changes: 1 addition & 1 deletion v2/crates/wifi-densepose-desktop/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ authors.workspace = true

[lib]
name = "wifi_densepose_desktop"
crate-type = ["staticlib", "cdylib", "rlib"]
crate-type = ["lib"]

[build-dependencies]
tauri-build = { version = "2", features = [] }
Expand Down
41 changes: 19 additions & 22 deletions v2/crates/wifi-densepose-sensing-server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8452,10 +8452,24 @@ async fn main() {
let field_surface: rufield_surface::FieldState =
Arc::new(RwLock::new(rufield_surface::FieldSurface::from_env()));

// Populated inside the `multistatic_fuser` field initializer below, then
// threaded into `engine_bridge` so both fusion paths honor the same
// #1031/#1049: the default guard (60 ms hard / 20 ms soft)
// accommodates a real TDM slot offset. A deployment overrides it via
// WDP_GUARD_INTERVAL_US (direct, e.g. 200000 for WiFi/ESP-NOW sync —
// #1049) or WDP_TDM_SLOTS + WDP_TDM_SLOT_US (derive from schedule).
// Threaded into `engine_bridge` so both fusion paths honor the same
// WDP_TDM_SLOTS/WDP_GUARD_INTERVAL_US-derived guard (#1049/#1057).
let mut engine_bridge_multistatic_cfg: Option<MultistaticConfig> = None;
let multistatic_cfg = {
let cfg = multistatic_guard_config_from_env();
info!(
"Multistatic fusion guard: {} µs hard / {} µs soft (override via \
WDP_GUARD_INTERVAL_US / WDP_SOFT_GUARD_US, or WDP_TDM_SLOTS+WDP_TDM_SLOT_US)",
cfg.guard_interval_us, cfg.soft_guard_us
);
MultistaticConfig {
min_nodes: 1, // single-node passthrough
..cfg
}
};

let state: SharedState = Arc::new(RwLock::new(AppStateInner {
latest_update: None,
Expand Down Expand Up @@ -8526,20 +8540,7 @@ async fn main() {
pose_tracker: PoseTracker::new(),
last_tracker_instant: None,
multistatic_fuser: {
// #1031/#1049: the default guard (60 ms hard / 20 ms soft)
// accommodates a real TDM slot offset. A deployment overrides it via
// WDP_GUARD_INTERVAL_US (direct, e.g. 200000 for WiFi/ESP-NOW sync —
// #1049) or WDP_TDM_SLOTS + WDP_TDM_SLOT_US (derive from schedule).
let cfg = multistatic_guard_config_from_env();
info!(
"Multistatic fusion guard: {} µs hard / {} µs soft (override via \
WDP_GUARD_INTERVAL_US / WDP_SOFT_GUARD_US, or WDP_TDM_SLOTS+WDP_TDM_SLOT_US)",
cfg.guard_interval_us, cfg.soft_guard_us
);
let mut fuser = MultistaticFuser::with_config(MultistaticConfig {
min_nodes: 1, // single-node passthrough
..cfg.clone()
});
let mut fuser = MultistaticFuser::with_config(multistatic_cfg.clone());
if let Some(ref pos_str) = args.node_positions {
let positions = field_bridge::parse_node_positions(pos_str);
if !positions.is_empty() {
Expand All @@ -8550,18 +8551,14 @@ async fn main() {
fuser.set_node_positions(positions);
}
}
engine_bridge_multistatic_cfg = Some(MultistaticConfig {
min_nodes: 1,
..cfg
});
fuser
},
engine_bridge: engine_bridge::EngineBridge::new(
wifi_densepose_bfld::PrivacyMode::PrivateHome,
1,
"default",
"Default Room",
engine_bridge_multistatic_cfg,
Some(multistatic_cfg),
),
field_model: if args.calibrate {
info!("Field model calibration enabled — room should be empty during startup");
Expand Down
7 changes: 2 additions & 5 deletions v2/crates/wifi-densepose-sensing-server/src/matter/bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,7 @@

use crate::mqtt::discovery::EntityKind;

use super::clusters::{
matter_mapping, MatterClusterMapping, DEVICE_TYPE_AGGREGATOR,
DEVICE_TYPE_BRIDGED_NODE,
};
use super::clusters::{matter_mapping, DEVICE_TYPE_AGGREGATOR};

/// One endpoint on the Matter device tree.
#[derive(Debug, Clone, PartialEq, Eq)]
Expand Down Expand Up @@ -107,7 +104,7 @@ pub fn build_bridge_tree(nodes: &[(String, String, Vec<EntityKind>)]) -> BridgeT

let ep_id = next_endpoint;
next_endpoint += 1;
let mut ep = Endpoint {
let ep = Endpoint {
endpoint_id: ep_id,
device_type: m.device_type,
label: format!("{:?}", entity),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,6 @@
//! vector `(passcode=20202021, discriminator=3840)` encodes to the
//! Matter-published `34970112332`.

use super::super::matter::clusters::VENDOR_ATTR_PERSON_COUNT as _; // re-export-only guard

/// Inputs to setup-code generation. `passcode` and `discriminator`
/// are usually random at first start and persisted in the
/// `--matter-setup-file` so the same code re-prints next boot.
Expand Down