diff --git a/docs/implementation-plan.md b/docs/implementation-plan.md index 53321e8..f7b3f87 100644 --- a/docs/implementation-plan.md +++ b/docs/implementation-plan.md @@ -503,10 +503,14 @@ a named invariant that the native runtime cannot protect yet. confidence. The same solver accepts `MimirFeatureTrackFieldCandidate` plus scene-space `TrackId` anchors, so PS Move sphere histories, sparse Eye tracks, and Eve-local marker tracks use the same Starfire frustum authority - as the LED strip. Synthetic smoke `--frustum-calibration-smoke` solves both + as the LED strip. `mimir.local_frustum_hint_state.v1` is the compact CultMesh + state document for local witness solves: producer node, calibration id, + marker set id, per-camera pose/frustum snapshot, residual, point count, and + confidence. Synthetic smoke `--frustum-calibration-smoke` solves both LED-spline and feature-track frustums for both Eyes, Kiyo Pro, Leap stereo - IR, and Eve camera, establishing the first fusion-ready frustum state before - dense surface fusion. + IR, and Eve camera, then emits the feature-track solve as a local frustum + hint state, establishing the first fusion-ready frustum surface before dense + surface fusion. Next useful cut is direct report decoding plus optical sphere observations from PS3 Eyes/Kiyos/Eve entering the same calibration-evidence path as LED spline constraints. diff --git a/src/Mimir.BufferSmoke/Program.cs b/src/Mimir.BufferSmoke/Program.cs index 69cd592..1161861 100644 --- a/src/Mimir.BufferSmoke/Program.cs +++ b/src/Mimir.BufferSmoke/Program.cs @@ -1160,9 +1160,13 @@ static int RunFrustumCalibrationSmoke() markerSetId: "move-and-eve-feature-markers", maximumDeltaMeters: 0.20, maximumRotationDeltaDegrees: 12.0); + var featureHint = MimirCultMeshContractFactory.CreateLocalFrustumHintState( + "eve-nightwing-local-frustum-hints", + "starfire-calibration-smoke", + featureFrame); Console.WriteLine( - $"frustum-calibration-smoke ledUpdates={frame.FrustumUpdates.Count} featureUpdates={featureFrame.FrustumUpdates.Count} confidence={frame.Confidence:0.000}/{featureFrame.Confidence:0.000} meanClipError={frame.MeanReprojectionErrorClip:0.000000}/{featureFrame.MeanReprojectionErrorClip:0.000000} sources={string.Join(",", frame.FrustumUpdates.Select(static update => $"{update.SourceId}:{update.UsedPointCount}/{update.RotationDeltaDegrees:0.00}deg/{update.HorizontalTanHalfFov:0.000}x{update.VerticalTanHalfFov:0.000}"))}"); + $"frustum-calibration-smoke ledUpdates={frame.FrustumUpdates.Count} featureUpdates={featureFrame.FrustumUpdates.Count} hintFrustums={featureHint.Frustums.Length} confidence={frame.Confidence:0.000}/{featureFrame.Confidence:0.000} meanClipError={frame.MeanReprojectionErrorClip:0.000000}/{featureFrame.MeanReprojectionErrorClip:0.000000} sources={string.Join(",", frame.FrustumUpdates.Select(static update => $"{update.SourceId}:{update.UsedPointCount}/{update.RotationDeltaDegrees:0.00}deg/{update.HorizontalTanHalfFov:0.000}x{update.VerticalTanHalfFov:0.000}"))}"); return frame.HasFrustumUpdate && featureFrame.HasFrustumUpdate && @@ -1172,6 +1176,9 @@ static int RunFrustumCalibrationSmoke() featureFrame.FrustumUpdates.All(update => update.UsedPointCount == scenePoints.Length) && frame.FrustumUpdates.All(static update => update.MeanReprojectionErrorClip < 0.010) && featureFrame.FrustumUpdates.All(static update => update.MeanReprojectionErrorClip < 0.010) && + featureHint.MarkerSetId == "move-and-eve-feature-markers" && + featureHint.Frustums.Length == truth.Length && + featureHint.Frustums.Any(static frustum => frustum.SourceId == "eve-camera") && frame.Confidence >= 0.70 && featureFrame.Confidence >= 0.70 ? 0 diff --git a/src/Mimir.Runtime/Synchronization/MimirCultMeshContracts.cs b/src/Mimir.Runtime/Synchronization/MimirCultMeshContracts.cs index 1bcd76b..28789f1 100644 --- a/src/Mimir.Runtime/Synchronization/MimirCultMeshContracts.cs +++ b/src/Mimir.Runtime/Synchronization/MimirCultMeshContracts.cs @@ -263,6 +263,37 @@ public sealed record MimirEveMediaObservationDocument( [property: Key(15)] string PayloadEncoding, [property: Key(16)] byte[] Payload); +[CultDocument("mimir.local_frustum_hint_state", "mimir.local_frustum_hint_state.v1")] +[MessagePackObject] +public sealed record MimirLocalFrustumHintStateDocument( + [property: Key(0)] + [property: CultName] + string HintId, + [property: Key(1)] string UpdatedAtUtc, + [property: Key(2)] string ProducerNodeId, + [property: Key(3)] string CalibrationId, + [property: Key(4)] string CandidateKey, + [property: Key(5)] string MarkerSetId, + [property: Key(6)] double MeanReprojectionErrorClip, + [property: Key(7)] double Confidence, + [property: Key(8)] MimirLocalFrustumSnapshot[] Frustums); + +[MessagePackObject] +public sealed record MimirLocalFrustumSnapshot( + [property: Key(0)] string SourceId, + [property: Key(1)] float PositionX, + [property: Key(2)] float PositionY, + [property: Key(3)] float PositionZ, + [property: Key(4)] float RotationX, + [property: Key(5)] float RotationY, + [property: Key(6)] float RotationZ, + [property: Key(7)] float RotationW, + [property: Key(8)] double HorizontalTanHalfFov, + [property: Key(9)] double VerticalTanHalfFov, + [property: Key(10)] int UsedPointCount, + [property: Key(11)] double MeanReprojectionErrorClip, + [property: Key(12)] double Confidence); + public static class MimirCultMeshContractFactory { public static MimirBioacousticCodebookState CreateCodebookState( @@ -334,6 +365,38 @@ public static class MimirCultMeshContractFactory .Select(pair => new MimirFaustControlSnapshot(pair.Key, pair.Value)) .ToArray()); + public static MimirLocalFrustumHintStateDocument CreateLocalFrustumHintState( + string hintId, + string producerNodeId, + MimirCameraFrustumSolveFrame frame, + DateTimeOffset? updatedAt = null) => + new( + hintId, + (updatedAt ?? DateTimeOffset.UtcNow).ToString("O"), + producerNodeId, + frame.CalibrationId, + frame.CandidateKey, + frame.MarkerSetId, + frame.MeanReprojectionErrorClip, + frame.Confidence, + frame.FrustumUpdates + .OrderBy(update => update.SourceId, StringComparer.Ordinal) + .Select(update => new MimirLocalFrustumSnapshot( + update.SourceId, + update.EstimatedPositionMeters.X, + update.EstimatedPositionMeters.Y, + update.EstimatedPositionMeters.Z, + update.EstimatedCameraToWorldRotation.X, + update.EstimatedCameraToWorldRotation.Y, + update.EstimatedCameraToWorldRotation.Z, + update.EstimatedCameraToWorldRotation.W, + update.HorizontalTanHalfFov, + update.VerticalTanHalfFov, + update.UsedPointCount, + update.MeanReprojectionErrorClip, + update.Confidence)) + .ToArray()); + private static MimirBioacousticDecoderConfigurationSnapshot Snapshot(MimirBioacousticDecoderConfiguration configuration) => new( configuration.Id, diff --git a/state/evidence.jsonl b/state/evidence.jsonl index b9b3d8d..c7c28d6 100644 --- a/state/evidence.jsonl +++ b/state/evidence.jsonl @@ -290,3 +290,4 @@ {"ts":"2026-05-31T22:45:00+01:00","type":"implementation","status":"ok","note":"Mimir editor authority was cut back to a 2D OBS-shaped program mixer. SceneEditorState no longer creates default SDF/model content, preview items are video-feed-only, runtime editor camera/SDF/spline output is silent, and the Eve/CultUI surface is Video Sources with source-adjacent layer actions left, chrome-free program canvas center, and Audio Mix right. Numeric transform inspector controls were removed from the live surface. `--scene-editor-smoke` now verifies only camera+video feed nodes, video-only preview items, and normalized program placement.","files":["src/Mimir.Runtime/MimirRuntime.cs","src/Mimir.Runtime/Synchronization/MimirSceneEditorState.cs","src/Mimir.BufferSmoke/Program.cs"],"source":"dotnet build src/Mimir.Runtime/Mimir.Runtime.csproj; dotnet build src/Mimir.BufferSmoke/Mimir.BufferSmoke.csproj; dotnet run --no-build --project src/Mimir.BufferSmoke/Mimir.BufferSmoke.csproj -- --scene-editor-smoke; Mimir.App --headless --headless-width 1280 --headless-height 720 --capture-frame artifacts/runtime/mimir-editor-flat-2d-program-mixer-headless.png"} {"ts": "2026-05-31T22:46:40+01:00", "type": "implementation", "status": "ok", "note": "Added the first Starfire-owned camera frustum solve. MimirCameraFrustumCalibrationSolver consumes stable LED/marker correspondences, scene-space marker points, and seed frustums, then emits position, rotation, horizontal/vertical tangent half-FOV, correspondence count, clipspace reprojection residual, and confidence. BufferSmoke --frustum-calibration-smoke solves five synthetic frustums for ps3-eye-0, ps3-eye-1, kiyo-pro-rgb, leap-stereo-ir, and eve-camera with mean clip error 0.000000; room calibration lock still passes. Eve is now modeled like Nightwing as eve-local-sensor-witness/eve-local-sensors: it owns local camera/mic flash and chirp solves and ships typed hints, not raw placement authority."} {"ts": "2026-05-31T22:52:47+01:00", "type": "implementation", "status": "ok", "note": "Extended MimirCameraFrustumCalibrationSolver beyond LED strips: it now also consumes MimirFeatureTrackFieldCandidate plus scene-space TrackId anchors, so PS Move sphere histories, sparse Eye tracks, and Eve-local marker tracks enter the same Starfire-owned frustum authority. BufferSmoke --frustum-calibration-smoke now proves five LED-spline frustums and five feature-track frustums for ps3-eye-0, ps3-eye-1, kiyo-pro-rgb, leap-stereo-ir, and eve-camera with mean clip error 0.000000."} +{"ts": "2026-05-31T22:55:38+01:00", "type": "implementation", "status": "ok", "note": "Added mimir.local_frustum_hint_state.v1 as the CultMesh document for local witness frustum solves. It carries producer node, calibration id, candidate key, marker set id, per-camera pose/frustum snapshots, residuals, point counts, and confidence, so Eve/Nightwing can publish compact solved hints while Starfire remains canonical global placement authority."} diff --git a/state/map.yaml b/state/map.yaml index 3753bc9..caf7f54 100644 --- a/state/map.yaml +++ b/state/map.yaml @@ -236,9 +236,15 @@ invariants: `MimirFeatureTrackFieldCandidate` plus scene-space `TrackId` anchors, so PS Move sphere histories, Eye feature tracks, and Eve-local marker tracks can enter the frustum solve without becoming a separate authority. + `mimir.local_frustum_hint_state.v1` is the typed CultMesh document for + publishing those local solved hints: producer node, calibration id, + candidate key, marker set id, per-camera pose/frustum snapshot, residual, + point count, and confidence. Starfire may ingest this as evidence; it is + still not canonical placement until the global solve accepts it. `Mimir.BufferSmoke --frustum-calibration-smoke` proves five LED-spline frustums and five feature-track frustums for both Eyes, Kiyo Pro, Leap - stereo IR, and Eve camera. + stereo IR, and Eve camera, then lowers the feature-track solve into a + `mimir.local_frustum_hint_state.v1` document. The Eyes have physically moved to Nightwing and Linux sees them as stock `ov534`/`gspca` V4L2 cameras on `/dev/video2` and `/dev/video3`. A repo-owned `tools/nightwing_v4l2_cadence_probe.py` copied to Nightwing