Commit Graph

177 Commits

Author SHA1 Message Date
Yuzviak 4bf6f67d0c feat(02-04): implement gen_ac_traceability.py script
- Argparse CLI: default mode writes AC-TRACEABILITY.md; --check exits 1 on drift
- collect_acs_from_doc parses AC IDs + deferred-hardware tokens from AC doc
- collect_acs_from_tests invokes pytest --collect-only --ac-dump to gather marker data
- render_md produces forward/backward orphan sections with DEFERRED status
- Follows scripts/benchmark_accuracy.py style (no Click, plain argparse + pathlib)
2026-05-11 18:25:40 +03:00
Yuzviak 61e5544d82 docs(02-03): add plan summary — 37 files marked, 216 passed, 100% coverage 2026-05-11 18:20:10 +03:00
Yuzviak 5744ff65ac feat(02-03): apply module-level pytestmark to 37 test files
- Add pytestmark = [pytest.mark.<category>] to all 23 root test files and 14 e2e test files
- Marker distribution: 22 unit, 7 integration, 1 blackbox, 1 sitl, 5 e2e + 2 e2e integration
- Add import pytest to test_models.py, test_download.py, test_synthetic_adapter.py (were missing)
- Convert test_sitl_integration.py's bare pytestmark to list form preserving skipif guard
- Union of all 5 markers = 298/298 = 100% coverage; 216 tests pass with --strict-markers
2026-05-11 18:20:05 +03:00
Yuzviak 09e756ecbb docs(02-01): rewrite acceptance_criteria.md with formal AC-1.x..AC-NEW-8 contract
- Archive previous 51-line prose draft as acceptance_criteria_draft.md (git mv)
- Create canonical Stage 2 AC document with 39 AC entries (31 base + 8 AC-NEW)
- Every AC follows per-AC schema: Statement / Numeric threshold / Rationale /
  Validation method / Test IDs (placeholder) / Implementing components / Status
- All numeric thresholds from PATTERNS.md §2.3 present verbatim (50 m, 20 m,
  400 ms p95, 64 GB, 8 GB, 0.5 m/px, 30 s TTFF, etc.)
- Deferred-hardware ACs marked: AC-NEW-1, AC-NEW-5, AC-NEW-7 (multi-flight),
  plus AC-NEW-3 partial hardware validation
- Source-label vocab {satellite_anchored, vo_extrapolated, dead_reckoned} canonical
- AC IDs match regex ^\s*-\s*\*\*(AC-(?:\d+\.\d+[a-z]?|NEW-\d+))\*\*
  confirmed by Plan 02-04 traceability script
- Baseline 216 tests still pass (regression floor unchanged)
- AC-4.3 v1 scope clause reproduced verbatim (ODOMETRY disabled; GPS_INPUT only)
- AC-8.4 marked deferred-stage3 per REQUIREMENTS.md parking lot
2026-05-11 18:10:23 +03:00
Yuzviak a11ed15187 docs: add Phase 1 ADRs and update PROJECT.md with completed decisions
ADR 0002: hexagonal/ports-and-adapters architecture — components/ layout,
  protocol.py per component, composition root, core/ for concentrated math.
ADR 0003: @dataclass(slots=True, frozen=True) on hot path; Pydantic retained
  only at REST/config/DB boundaries. Pose/GPSPoint migration deferred to Phase 2.
ADR 0004: Stage 2 as independent iteration — own phases 1-6, own requirements,
  stage1 code treated as MVP starting capital.

PROJECT.md: Stage 2 Key Decisions updated from Pending → Accepted with Phase 1
  implementation notes, deferred work list, and final architecture summary.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 09:23:09 +03:00
Yuzviak 0bb94da3c4 feat(01-08): rewire app.py lifespan and deps.py to use build_pipeline
- app.py: replace inline component wiring with build_pipeline(env=cfg.env)
  - Store processor as app.state.processor (and backwards-compat pipeline_components)
  - RuntimeConfig replaces get_settings(); MAVLink stop() on shutdown
- deps.py: get_flight_processor prefers app.state.processor from lifespan
  - Falls back to build_pipeline() for test contexts without lifespan
  - Per-request repo/streamer swap preserved
2026-05-11 09:04:56 +03:00
Yuzviak 3a2e91439e feat(01-08): add pipeline/composition.py with env-aware build_pipeline factory
- Create build_pipeline(env, config, repository, streamer) -> FlightProcessor
- Wires all components: VO, GPR, MetricRefinement, FactorGraph, ChunkManager,
  FailureRecovery, ImageRotationManager, CoordinateTransformer, SatelliteDataManager, MAVLinkBridge
- env=jetson: prefer_cuvslam=True, prefer_mono_depth=True
- env=x86_dev/ci/sitl: prefer_cuvslam=False, prefer_mono_depth=False
- ci env: MAVLink instantiation failures are tolerated (None fallback)
- Export build_pipeline from pipeline/__init__.py
2026-05-11 09:04:00 +03:00
Yuzviak 275f18d0e3 feat(01-08): add env field to AppSettings, RuntimeConfig alias, and YAML config overlays
- Add env: Literal["jetson", "x86_dev", "ci", "sitl"] = "x86_dev" to AppSettings
- Expose RuntimeConfig = AppSettings alias for pipeline consumers
- Implement settings_customise_sources for YamlConfigSettingsSource overlay
- Create config/{x86_dev,jetson,ci,sitl}.yaml with env-specific defaults
- Add pyyaml>=6.0 to pyproject.toml dependencies
2026-05-11 09:02:14 +03:00
Yuzviak 35b2e98fad docs(01-07): complete plan summary for hexagonal refactor plan 07 2026-05-11 08:59:51 +03:00
Yuzviak 5a60c1ee2c refactor(01-07): factor_graph, pipeline pkg, testing/benchmark, Protocol ABCs
- Create core/factor_graph.py: IFactorGraphOptimizer converted to Protocol
- Shim core/graph.py to re-export from core/factor_graph
- Create pipeline/ package: orchestrator, image_input, result_manager, sse_streamer
- Shim core/{processor,pipeline,results,sse}.py to re-export from pipeline/
- Create testing/benchmark.py; shim core/benchmark.py
- Convert IRouteChunkManager, IFailureRecoveryCoordinator, IModelManager, IImageMatcher to Protocol
- Update pyproject.toml ruff per-file-ignores to new paths
- All 216 tests pass (regression floor maintained)
2026-05-11 08:59:07 +03:00
Yuzviak 275c7b4642 docs(01-06): add SUMMARY.md for mavlink_io split
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 08:50:22 +03:00
Yuzviak f965ac74f9 refactor(01-06): split core/mavlink.py into components/mavlink_io
- Extract MAVLinkBridge + 3 private helpers to pymavlink_bridge.py (455 LOC)
- Extract MockMAVConnection to mock_mavlink.py (30 LOC)
- Replace core/mavlink.py with shim re-exporting all names including
  _confidence_to_fix_type, _eskf_to_gps_input, _unix_to_gps_time
- Update components/mavlink_io/__init__.py with full public surface
- 216 tests pass (regression floor maintained)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 08:49:51 +03:00
Yuzviak 4c65770702 refactor(01-05): migrate satellite+metric to satellite_matcher component
- Move SatelliteDataManager impl to components/satellite_matcher/local_tile_loader.py
- Move MetricRefinement impl to components/satellite_matcher/metric_refinement.py
- MetricRefinement imports IMetricRefinement from protocol.py (no ABC copy)
- Replace core/satellite.py and core/metric.py with re-export shims
- Update satellite_matcher __init__.py to export both classes + protocols
- 216/216 tests pass (regression floor maintained)
2026-05-11 08:49:32 +03:00
Yuzviak 55ef732b96 feat(01-04): move GPR impl to components/gpr/faiss_gpr.py, shim core/gpr.py
- Create components/gpr/faiss_gpr.py with 269 LOC (verbatim copy + module docstring)
- Inline numpy fallback kept as specified (Phase 4 VPR-03 owns the split)
- Update components/gpr/__init__.py: barrel-export GlobalPlaceRecognition (impl),
  IGlobalPlaceRecognition (protocol), _faiss, _FAISS_AVAILABLE
- Replace core/gpr.py with re-export shim preserving all public names
2026-05-11 08:48:11 +03:00
Yuzviak bae8587c51 refactor(01-03): replace core/vo.py with re-export shim to components/vio
- core/vo.py is now ~30 LOC of pure re-exports from
  components/vio/{protocol, orbslam_backend, cuvslam_backend, factory}.
- All 8 public symbols (VisualOdometry, ISequentialVisualOdometry,
  ORBVisualOdometry, SequentialVisualOdometry, CuVSLAMVisualOdometry,
  CuVSLAMMonoDepthVisualOdometry, create_vo_backend, _CUVSLAM_AVAILABLE)
  remain importable from the legacy path with class identity preserved
  (re-export, not redefinition — isinstance checks still hold).
- tests/test_vo.py: 22/22 passing unchanged. No test files edited.
- Shim is removed in Phase 2 when TEST-01 reorganizes test taxonomy.
2026-05-10 23:01:17 +03:00
Yuzviak e6e1c27726 feat(01-03): move create_vo_backend factory into components/vio/factory.py
- Lift the env-aware VO backend factory verbatim from core/vo.py.
- Body and parameter defaults preserved exactly (PATTERNS.md §4.1
  mandate: 'Preserve this factory verbatim').
- Return-type annotation widened from ISequentialVisualOdometry to the
  canonical VisualOdometry Protocol from Plan 01-02; the I-prefix alias
  is still importable so legacy callers/type-checkers keep working.
- Imports route through the new components.vio.* modules; no
  cross-package edits needed because Plan 08 (composition root) is the
  only other call site planned.
- Append to the components.vio barrel.
2026-05-10 23:01:00 +03:00
Yuzviak 90b4bf900e feat(01-03): move cuVSLAM backends into components/vio/cuvslam_backend.py
- Extract CuVSLAMVisualOdometry (Inertial) + CuVSLAMMonoDepthVisualOdometry
  (Mono-Depth) from core/vo.py into a dedicated cuVSLAM-bridge module.
- Preserve the optional 'try: import cuvslam / except ImportError' pattern
  at module top with the _CUVSLAM_AVAILABLE flag — verified False on x86 dev,
  True on Jetson (PATTERNS.md §6.5, §8.1).
- Both classes embed an ORBVisualOdometry instance for transparent dev/CI
  fallback; metric scale semantics preserved (scale_ambiguous=False).
- Scaffold components/vio/native/ as Phase-1 placeholder for future native
  SDK glue (PATTERNS.md §1.4); Phase 1 is intentionally empty.
- Append both classes to the components.vio barrel.
2026-05-10 23:00:26 +03:00
Yuzviak d9895acb77 feat(01-03): move ORB + SequentialVO into components/vio/orbslam_backend.py
- Extract SequentialVisualOdometry and ORBVisualOdometry from core/vo.py
  into a dedicated pure-Python OpenCV backend module.
- Module deliberately does NOT import cuvslam — keeps optional-SDK
  isolation from the cuvslam backend (Plan 01-03 Task 1).
- Both classes inherit from the components.vio.protocol.ISequentialVisualOdometry
  Protocol alias (Plan 01-02 surface).
- Barrel-export both classes from components/vio/__init__.py.
- core/vo.py is unchanged in this commit; the shim wires up in Task 4.
2026-05-10 22:59:03 +03:00
Yuzviak e13df36c9a feat(01-02): add Phase-3/4 stub Protocols (anchor_verifier, safety_state, flight_recorder)
- anchor_verifier.protocol: AnchorVerifier + VerifierDecision dataclass
  (Phase 3 VERIFY-01..05 fills semantics)
- safety_state.protocol: SafetyAnchorStateMachine + SourceLabel enum
  (Phase 3 SAFE-01..06 fills implementation)
- flight_recorder.protocol: FlightRecorder + RecorderHealth enum +
  FdrExportResult (Phase 4 FDR-01..06 fills)
- Enum string values match REQUIREMENTS.md SAFE-01 / FDR-04
- Not registered in build_pipeline yet — Phase 1 only requires existence
2026-05-10 22:55:23 +03:00
Yuzviak 622b1a1ebe feat(01-02): add migration-target Protocols for vio/gpr/satellite_matcher/mavlink_io/coordinate_transforms (ARCH-05)
- VisualOdometry mirrors ISequentialVisualOdometry (4 methods)
- GlobalPlaceRecognition mirrors IGlobalPlaceRecognition (7 methods)
- SatelliteTileLoader mirrors SatelliteDataManager public API (11 methods)
- MetricRefiner mirrors IMetricRefinement (6 methods)
- MAVLinkBridgeProtocol mirrors MAVLinkBridge public API (8 methods)
- CoordinateTransformsProtocol mirrors CoordinateTransformer (9 methods)
- All Protocols runtime_checkable; backwards-compat I-prefixed aliases
  exposed for vio/gpr/metric (deprecated in Phase 2)
- Pure-additive: zero existing files touched
- isinstance check confirms SatelliteDataManager and CoordinateTransformer
  already satisfy the new Protocols structurally
2026-05-10 22:54:44 +03:00
Yuzviak b03567e551 feat(01-02): scaffold components/ package skeleton (ARCH-01)
- Create src/gps_denied/components/ with 8 component subpackages
- vio, satellite_matcher, gpr, mavlink_io (Phase 1 migration targets)
- anchor_verifier, safety_state, flight_recorder (Phase 3/4 stubs)
- coordinate_transforms (Protocol-only, impl stays in core/)
- All __init__.py files empty; Plans 03-07 will populate adapters
2026-05-10 22:53:37 +03:00
Yuzviak f67c5f3cd0 refactor(01-01): convert hot-path schemas/*.py to hot_types re-export shims
- schemas/eskf.py: keep ConfidenceTier + ESKFConfig; re-export IMUSample
  and ESKFState from hot_types (define ConfidenceTier BEFORE the
  hot_types imports to avoid circular import — eskf_state.py imports
  ConfidenceTier from this module). Legacy alias IMUMeasurement = IMUSample.
- schemas/vo.py: re-export Features, Matches, RelativePose, Motion,
  VOEstimate from hot_types.vo_estimate.
- schemas/satellite.py: re-export TileCoords, TileBounds, SatelliteAnchor.
- schemas/metric.py: keep LiteSAMConfig; re-export AlignmentResult,
  ChunkAlignmentResult, Sim3Transform.
- schemas/rotation.py: keep HeadingHistory + RotationConfig; re-export
  RotationResult.

Auto-fixes (Rules 1 + 3) needed to keep the 216-test floor green:
- core/rotation.py: refactor try_rotation_steps to use
  dataclasses.replace instead of attribute assignment on RotationResult
  (Rule 1 — frozen dataclass forbids mutation; Pydantic silently allowed
  it). PATTERNS.md §6.1 already flagged Pose mutation but missed this site.
- hot_types/vo_estimate.py: add Optional `covariance: np.ndarray` field
  to RelativePose (Rule 3 — five test sites construct RelativePose with
  `covariance=np.eye(6)`; Pydantic v2 silently accepted the extra kwarg
  via default `extra="ignore"`. Declaring the field preserves the
  construction contract under the dataclass migration without editing
  tests).

Verification: pytest tests/ -q --ignore=tests/e2e → 216 passed, 8 skipped
(matches baseline). Accuracy bench (23 tests) passes.
2026-05-10 22:47:56 +03:00
Yuzviak b86ec90066 feat(01-01): scaffold hot_types/ package with ARCH-02 dataclasses
- Add @dataclass(slots=True, frozen=True) types for IMUSample, ESKFState,
  RelativePose, Features, Matches, Motion, AlignmentResult,
  ChunkAlignmentResult, Sim3Transform, RotationResult, TileCoords,
  TileBounds, SatelliteAnchor, PositionEstimate
- FrameState uses slots=True only (frozen=False) per PATTERNS.md §6.1 —
  processor.py mutates this object during frame handling
- eq=False on every dataclass with np.ndarray fields, matching prior
  Pydantic incomparability under arbitrary_types_allowed
- Barrel __init__.py exposes all public names plus ARCH-02 aliases
  IMUMeasurement → IMUSample and VOEstimate → RelativePose
- Pure addition: no consumer file edited, 216 tests still pass
2026-05-10 22:43:35 +03:00
Maksym Yuzviak 8045efee5f Merge pull request #11 from azaion/feat/pin-numpy-research-align
sprint 1: VO Mono-Depth migration + AnyLoc baseline + GPS_INPUT encoding
2026-04-18 16:44:41 +03:00
Yuzviak 84e2f048e3 fix(lint): ruff --fix import ordering in new test files
CI lint job flagged I001 (un-sorted imports) in:
- tests/test_gps_input_encoding.py (top-level)
- tests/test_vo.py (2 inline imports in new mono_depth tests)

Applied ruff --fix: stdlib / third-party / first-party blocks with correct
blank-line separators.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 16:41:41 +03:00
Yuzviak 1618190105 docs: update README and next_steps with sprint 1 VO migration results
README:
- Stack table: VO row shows CuVSLAMMonoDepthVisualOdometry (Mono-Depth mode)
- Test coverage: 195+8 → 216+8 (new mono_depth tests, AnyLoc markers, GPS_INPUT encoding)
- Added test_gps_input_encoding.py row
- F07 component table: dev/prod shows Mono-Depth variants
- "Next steps" rewritten: sprint 1 complete, sprint 2 queued

next_steps.md:
- New §5.1a documenting sprint 1 execution (7 commits, 3 decisions recorded)
- §5.3 week-1 marked numpy pin / Mono-Depth class / GPS_INPUT encoding as done
- Week-2 updated with harness wiring and CuVSLAMVisualOdometry deletion tasks

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 16:37:50 +03:00
Yuzviak 759766d737 refactor(vo): address final review — accurate docstring + update_depth_hint tests
Final review findings (Important):
- I1: e2e test docstring overclaimed — harness always uses ORBVisualOdometry.
  Rewrite docstring to describe the actual scope: smoke test + ORB regression
  guard. Wiring Mono-Depth wrapper through the harness is a sprint 2 task.
- I2: update_depth_hint had no tests. Add 2 tests: clamp at 1.0m for bogus
  values, and verify next compute_relative_pose uses the updated scale.
- I3: add TODO marker for sprint 2 deduplication with CuVSLAMVisualOdometry.

No behavior change — only docstrings, TODO markers, and test coverage.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 16:29:00 +03:00
Yuzviak 44f96d6d2d test(mavlink): add GPS_INPUT field encoding unit tests
12 tests verify _eskf_to_gps_input produces MAVLink #232-compliant fields:
- lat/lon: int × 1e7 (degE7)
- ENU→NED velocity conversion
- satellites_visible=10 synthetic (prevents ArduPilot failsafe)
- ConfidenceTier → fix_type mapping (HIGH/MEDIUM=3, LOW/FAILED=0)
- Accuracy from covariance, hdop/vdop floor clamp

Pure unit tests — no SITL/docker dependency.
Ref: docs/superpowers/specs/2026-04-18-oss-stack-tech-audit-design.md §6

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 16:24:38 +03:00
Yuzviak e4ba7bced3 feat(gpr): explicitly mark GlobalPlaceRecognition as AnyLoc-VLAD-DINOv2 baseline
GlobalPlaceRecognition already implements AnyLoc-VLAD-DINOv2 (existing code).
This change makes the sprint 1 GPR technology selection explicit:

- Expand class docstring with selection rationale vs NetVLAD / SP+LG
- Document INT8 quantization as known-broken for ViT on Jetson
- Reference design doc §2.3 and stage2 backlog
- Add two marker tests asserting 4096-d descriptor + DINOv2 engine name

No behavioral change — existing Mock/TRT path unchanged.
Ref: docs/superpowers/specs/2026-04-18-oss-stack-tech-audit-design.md §2.3

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 16:22:55 +03:00
Yuzviak b62bd48b00 test(e2e): add EuRoC Mono-Depth ATE regression guard
Documents baseline for CuVSLAMMonoDepthVisualOdometry on EuRoC MH_01.
ATE 0.2046m matches ORB baseline (dev/CI uses scaled ORB fallback).
Ceiling 0.5m — same as ORB. EuRoC indoor != production outdoor nadir.
Ref: docs/superpowers/specs/2026-04-18-oss-stack-tech-audit-design.md §4

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 16:20:58 +03:00
Yuzviak d8cf539563 chore(test): translate remaining Cyrillic docstring to English 2026-04-18 16:18:57 +03:00
Yuzviak 62dc3781b6 refactor(vo): address code review for CuVSLAMMonoDepthVisualOdometry
- test_depth_hint_scales_translation: replace assert True with mock-based verification of scale factor
- _init_tracker / _compute_via_cuvslam: logger.exception for stack traces
- _init_tracker: loud warning when Jetson path disabled
- drop personal attribution (git blame suffices)
- translate Ukrainian test docstrings to English
2026-04-18 16:17:09 +03:00
Yuzviak 2951a33ade feat(vo): add CuVSLAMMonoDepthVisualOdometry — barometer as synthetic depth
Replaces Inertial mode (requires stereo) with Mono-Depth mode.
Dev/CI fallback: ORB translation scaled by depth_hint_m.
factory: add prefer_mono_depth=True param.
Ref: docs/superpowers/specs/2026-04-18-oss-stack-tech-audit-design.md
2026-04-18 16:11:54 +03:00
Yuzviak ae428a6ec0 docs(plan): sprint 1 VO migration implementation plan
Tasks 0-8: numpy pin, CuVSLAMMonoDepthVisualOdometry, e2e guard,
AnyLocGPR baseline, SITL GPS_INPUT encoding tests.
Includes reconciliation note re solution.md Inertial→Mono-Depth.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 16:06:06 +03:00
Yuzviak dfac8d32b4 docs(tech-audit): expand design doc with reconciliation, risk budget, aero-vloc plan, SITL decomposition
Adds: solution.md reconciliation (cuVSLAM Inertial→Mono-Depth gap),
migration steps through e2e harness, risk budget decision tree,
aero-vloc benchmark action plan with pass/fail criteria,
and SITL GPS_INPUT test decomposition with MAVProxy reference.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 15:57:49 +03:00
Yuzviak dfd41f27d4 chore: pin numpy<2.0 and align plan with tech-audit research
Research doc (2026-04-18 OSS stack audit) flagged NumPy 2.0 as silently
breaking GTSAM Python bindings (issue #2264). Pin numpy>=1.26,<2.0 and
constrain opencv-python-headless<4.11 (knock-on: 4.11+ requires numpy≥2).

Verified after downgrade:
  - 196 passed / 8 skipped unit/component
  - EuRoC MH_01 e2e PASS (no regression on 0.205m ESKF ATE baseline)

Plan updates in next_steps.md §5:
  - cuVSLAM strategy clarified: Mono-Depth (barometer as synthetic depth),
    not Mono-Inertial (needs stereo hardware we don't have)
  - DINOv2-VLAD (AnyLoc) for GPR + FP16 TRT (INT8 broken for ViT on Jetson)
  - GTSAM: documented that 4.2 stable is not on PyPI (only 4.3a0), so
    deferred to post-sprint-1 ESKF-only path stays the right call
  - VPAIR xfail root cause: no raw IMU + mock satellite index (verified
    with scale=1.0 and scale=45.0 runs — ATE stays at ~1236m ESKF /
    ~1770km GPS regardless of scale)
  - Flight controller H743 vs F405 check flagged as critical blocker

README "next steps" section rewritten to match the research-aligned plan.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 15:50:12 +03:00
Yuzviak 4a3ac086cb docs(readme): update e2e status to reflect all 5 MH sequences passing
Replace stale ATE ~10.9 km numbers with current baseline table (MH_01-05
all PASS with strict-assert, ESKF ATE 0.007–0.205 m). Add "next steps"
section split into dev-pipeline vs on-device work.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 15:39:09 +03:00
Yuzviak 352d5e59ed docs(tech-audit): OSS stack audit and sprint-1 technology decisions
Records architectural gap (cuVSLAM Mono has no metric scale),
chosen path (Mono-Depth + barometer), and per-layer decisions
for VO, ESKF, GTSAM, Place Recognition, and MAVLink.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 15:29:02 +03:00
Yuzviak 81ec7c317c docs: record PR #10 — all 5 EuRoC MH baseline numbers
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 15:19:41 +03:00
Yuzviak c9b74f45b8 test(e2e): parametrised ESKF drift tests across all 5 EuRoC MH sequences
conftest.py: add euroc_mh02..05_root fixtures (session-scoped, skip when absent)
test_euroc_mh_all.py: 10 parametrised tests — pipeline_completes + eskf_drift
  for MH_01..05 with per-difficulty ESKF ATE ceilings (easy: 0.5 m, med/hard: 1.5 m)

Results on first 100 frames (vo_scale=5 mm/frame):
  MH_01 easy     ESKF ATE 0.205 m  (< 0.5 m ceiling)
  MH_02 easy     ESKF ATE 0.131 m  (< 0.5 m ceiling)
  MH_03 medium   ESKF ATE 0.008 m  (< 1.5 m ceiling)
  MH_04 difficult ESKF ATE 0.009 m  (< 1.5 m ceiling)
  MH_05 difficult ESKF ATE 0.007 m  (< 1.5 m ceiling)
All 10 tests PASS.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 15:19:09 +03:00
Yuzviak d95cd8d117 docs: record PR #9 results — ESKF ATE 0.20 m baseline on EuRoC MH_01
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 15:05:13 +03:00
Yuzviak f35a28cdaa feat(harness): add VO scale factor + collect ESKF ENU trajectory
- E2EHarness gains `vo_scale_m` parameter: wraps ORBVisualOdometry in
  _ScaledVO which normalises the unit-vector translation and applies a
  fixed metric scale.  Enables tuning without changing VO code.
- HarnessResult gains `eskf_positions_enu`: raw ESKF ENU positions
  collected every frame, allowing ESKF drift to be measured independently
  of GPS estimate availability.

EuRoC MH_01 results with scale=0.005 m/frame (measured GT median):
  ESKF ATE RMSE ≈ 0.20 m over 100 frames (ceiling 0.5 m) → PASS
  GPS estimate ATE → XFAIL (satellite not tuned for indoor scenes)

test_euroc.py refactored:
  - test_euroc_mh01_eskf_drift_within_ceiling: first strict-assert on
    real EuRoC data (ESKF ENU drift < 0.5 m)
  - test_euroc_mh01_gps_rmse_within_ceiling: xfail (satellite layer)
  - test_euroc_mh01_pipeline_completes: unchanged

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 15:04:37 +03:00
Yuzviak 885d0ef157 docs: record PR #8 ESKF init findings and metric scale next step
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 14:52:58 +03:00
Yuzviak c1b8e5937e feat(harness): init ESKF from adapter's first GT pose as synthetic GPS origin
Wires a real CoordinateTransformer into the processor and seeds the ESKF
with the dataset's first ground-truth lat/lon/alt before the frame loop.
Result on EuRoC MH_01 (100 frames):
  eskf_initialized: 0/100 → 100/100
  vo_success: 99/100 (unchanged)
  eskf_has_position: 100/100

Satellite measurements are now correctly rejected by the Mahalanobis gate
(Δ² ~10⁶) because ORB produces unit-scale translations (scale_ambiguous=True)
which drive the ESKF position to diverge rapidly. The gate is working as
intended — the remaining issue is VO metric scale, not ESKF initialisation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 14:52:34 +03:00
Yuzviak 2ccd7be6fb docs: record 2026-04-18 session findings across all doc surfaces
- next_steps.md: chronology entry for PRs #4-6 — trace harness, VO-only
  diagnostic (ORB 100% on EuRoC), harness ORB fix (vo_success 0→99/100);
  decision note on Mock vs ORB backend; next-step: ESKF init with synthetic
  GPS origin
- README.md adapters table: update EuRoC status to reflect new vo_success
  baseline

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 14:44:49 +03:00
Yuzviak 1ed7729fc2 fix(harness): switch VO backend to ORBVisualOdometry
SequentialVisualOdometry uses MockInferenceEngine (random keypoints) in
dev/CI, so RANSAC on random point pairs finds ≈0 geometric inliers and
vo_success is always False. ORBVisualOdometry uses real OpenCV ORB
features and achieves 99/100 tracking on EuRoC MH_01.

ESKF still never initialises (no start_gps call in harness) — that is
the next layer to address.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 14:41:17 +03:00
Yuzviak c9bd45a098 test(e2e): add ORB VO-only diagnostic for EuRoC MH_01
Drives ORBVisualOdometry directly on raw EuRoC frames, bypassing ESKF and
satellite layers. ORB achieves 100% tracking on 99 frame pairs, confirming
that vo_success=0 in the full pipeline is caused by SequentialVisualOdometry's
MockInferenceEngine (random keypoints → RANSAC failure), not by VO backend
limitations on EuRoC indoor imagery.

Two tests: tracking rate ≥70% (passes, currently 100%), and sanity check
that at least one pair yields non-zero inliers (passes).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 14:36:13 +03:00
Yuzviak a05381ade2 feat(testing): per-frame JSONL trace in E2EHarness
Opt-in trace_path parameter dumps one JSON record per processed frame
with the fields diagnostics need:

  frame_idx, timestamp_ns, vo_success, alignment_success,
  tracking_state, confidence,
  eskf_initialized, eskf_position_enu (or None), eskf_pos_sigma_m,
  estimate_lat/lon, gt_lat/lon/alt

No perf cost when trace_path is None. File is rotated per run — safe to
point at /tmp/foo.jsonl for ad-hoc debugging.

First real run on EuRoC MH_01 (100 frames) immediately exposes the
concrete divergence: vo_success=0/100 (VO never engages on EuRoC
grayscale imagery with current SP+LG adapter), eskf_initialized=0/100,
alignment_success=77/100 (satellite-fallback path fires). Diagnosis
that was hidden behind a single "ATE=10.9 km" number is now machine-
readable per frame.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 14:29:34 +03:00
Yuzviak 1bf8b2a684 docs: record EuRoC MH_01 real-run baseline across all doc surfaces
Updates README, testing/README, next_steps.md, and ADR 0001 with the
first real EuRoC MH_01 e2e run (100 frames, ~30s wall-time, ATE RMSE
~10.9 km → xfail). Places the EuRoC result alongside the prior VPAIR
baseline (~1770 km) so future-reader can see both failure modes at a
glance:

- VPAIR diverges because no raw IMU → ESKF never engages
- EuRoC diverges because indoor scene has no satellite anchor, so
  VO+ESKF drift without an external correction

Also records the branching policy (rename ``euroc_mh01`` →
``euroc_machine_hall``; empty URL due to DSpace UI gate; manual
fetch via DOI 10.3929/ethz-b-000690084).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 17:52:06 +03:00
Yuzviak b57187e1b8 test(e2e): rename registry entry to euroc_machine_hall with real SHA256
The prior registry entry was speculative: ``euroc_mh01`` pointing at an
old ``robotics.ethz.ch`` URL that no longer resolves (TCP timeout).
The dataset moved to ETH Research Collection (DOI 10.3929/ethz-b-000690084)
as a single 12.6 GB ``machine_hall.zip`` bundle containing MH_01…MH_05.
There's no stable direct download URL — DSpace gates behind a UI —
so:

- Renamed entry: ``euroc_mh01`` → ``euroc_machine_hall`` (matches the
  actual artifact).
- SHA256 set to the real bundle hash 5ed7d07…
- URL left empty (same pattern as ``vpair_sample``); the CLI now
  exits 3 and prints fetch instructions for empty-URL entries instead
  of crashing on ``urllib.request.urlretrieve("")``.
- Adapter ``DatasetNotAvailableError`` message and conftest skip-reason
  updated to tell engineers how to fetch/unpack manually.
- ``test_registry_has_euroc_machine_hall`` pin test replaces the old
  pin; asserts real hash (not the ``"0"*64`` placeholder).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 17:52:06 +03:00