mirror of
https://github.com/azaion/gps-denied-onboard.git
synced 2026-06-22 19:11:14 +00:00
[AZ-345] [AZ-346] [AZ-347] [AZ-349] C3 matchers + C3.5 AdHoP refiner
Implement the three concrete C3 CrossDomainMatcher strategies plus the C3.5 production-default AdHoPRefiner. C3 (AZ-345/346/347): - DiskLightGlueMatcher + AlikedLightGlueMatcher share a single shared _pipeline.run_lightglue_pipeline orchestrator (decode -> query extract -> per-candidate loop -> RANSAC sort -> health update -> FDR emit) so the only per-backbone delta is the keypoint+descriptor extractor closure. ALIKED adds a create-time engine output-schema probe (AC-special-1). - XFeatMatcher owns its own per-candidate loop (single forward fuses extraction + matching); it re-uses the shared FDR emission helpers to keep telemetry byte-identical across strategies. lightglue_runtime parameter accepted by factory but discarded (AC-special-1). - All three consume the shared LightGlueRuntime / RansacFilter / RollingHealthWindow helpers; no helper forks. InferenceRuntimeCut consumer-side Protocol added per AZ-507. C3.5 (AZ-349): - AdHoPRefiner implements the <= conditional gate, runs the OrthoLoC AdHoP TRT engine over best-candidate correspondences, re-runs RANSAC on the perspective-preconditioned set, and emits an enriched MatchResult with refinement_label="adhop". - Invariant 4 passthrough fall-through: any RefinerBackboneError (TRT failure, OOM, NaN, bad shape) is caught, logged ERROR, FDR-emitted with error: true, and converted to passthrough that still counts against the rolling invocation-rate window. MemoryError and other non-listed exceptions propagate by design (AC-5 closed-set semantics). - Rolling 60-s invocation-rate window + rate-limited WARN log (configurable via ratelimited_warn_window_ns; default 60 s). Shared changes: - C3MatcherConfig + C3_5RefinerConfig extended with the new weights/threshold/window fields. - matcher_factory + refiner_factory optionally forward clock + fdr_client to the strategy's create(); backward-compatible. - fdr_client.records registers five new kinds: matcher.frame_done, matcher.backbone_error, matcher.insufficient_inliers, matcher.all_failed, refiner.frame_done. Tests: 66 new (43 C3 parametrised + 23 AdHoP) covering 47/47 ACs; focused suite green; full project test suite green except for one pre-existing flaky CLI cold-start timing test unrelated to this batch. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -38,12 +38,14 @@ from gps_denied_onboard.components.c3_matcher._health_window import RollingHealt
|
||||
from gps_denied_onboard.runtime_root.errors import StrategyNotAvailableError
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from gps_denied_onboard.clock import Clock
|
||||
from gps_denied_onboard.components.c3_matcher import (
|
||||
C3MatcherConfig,
|
||||
CrossDomainMatcher,
|
||||
)
|
||||
from gps_denied_onboard.components.c7_inference import InferenceRuntime
|
||||
from gps_denied_onboard.config.schema import Config
|
||||
from gps_denied_onboard.fdr_client import FdrClient
|
||||
from gps_denied_onboard.helpers.lightglue_runtime import LightGlueRuntime
|
||||
from gps_denied_onboard.helpers.ransac_filter import RansacFilter
|
||||
|
||||
@@ -106,6 +108,8 @@ def build_matcher_strategy(
|
||||
lightglue_runtime: "LightGlueRuntime",
|
||||
ransac_filter: "RansacFilter",
|
||||
inference_runtime: "InferenceRuntime",
|
||||
clock: "Clock | None" = None,
|
||||
fdr_client: "FdrClient | None" = None,
|
||||
) -> "CrossDomainMatcher":
|
||||
"""Construct the :class:`CrossDomainMatcher` impl selected by config.
|
||||
|
||||
@@ -168,6 +172,15 @@ def build_matcher_strategy(
|
||||
"yet (AZ-345 / AZ-346 / AZ-347 pending)."
|
||||
) from exc
|
||||
create_fn = getattr(module, "create", None)
|
||||
# ``clock`` and ``fdr_client`` were added in AZ-345 / AZ-346 / AZ-347;
|
||||
# the AZ-344 fakes do not accept them. Only forward when the factory
|
||||
# has been wired with them so the existing protocol tests continue
|
||||
# to pass.
|
||||
extra_kwargs: dict[str, object] = {}
|
||||
if clock is not None:
|
||||
extra_kwargs["clock"] = clock
|
||||
if fdr_client is not None:
|
||||
extra_kwargs["fdr_client"] = fdr_client
|
||||
if create_fn is None:
|
||||
strategy_cls = getattr(module, class_name)
|
||||
instance = strategy_cls(
|
||||
@@ -176,6 +189,7 @@ def build_matcher_strategy(
|
||||
ransac_filter=ransac_filter,
|
||||
inference_runtime=inference_runtime,
|
||||
health_window=health_window,
|
||||
**extra_kwargs,
|
||||
)
|
||||
else:
|
||||
instance = create_fn(
|
||||
@@ -184,6 +198,7 @@ def build_matcher_strategy(
|
||||
ransac_filter=ransac_filter,
|
||||
inference_runtime=inference_runtime,
|
||||
health_window=health_window,
|
||||
**extra_kwargs,
|
||||
)
|
||||
_LOG.info(
|
||||
"c3.matcher.strategy_loaded",
|
||||
|
||||
@@ -28,12 +28,14 @@ from typing import TYPE_CHECKING
|
||||
from gps_denied_onboard.components.c3_5_adhop.errors import RefinerConfigError
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from gps_denied_onboard.clock import Clock
|
||||
from gps_denied_onboard.components.c3_5_adhop import (
|
||||
C3_5RefinerConfig,
|
||||
ConditionalRefiner,
|
||||
)
|
||||
from gps_denied_onboard.components.c7_inference import InferenceRuntime
|
||||
from gps_denied_onboard.config.schema import Config
|
||||
from gps_denied_onboard.fdr_client import FdrClient
|
||||
from gps_denied_onboard.helpers.ransac_filter import RansacFilter
|
||||
|
||||
__all__ = ["build_refiner_strategy"]
|
||||
@@ -71,6 +73,8 @@ def build_refiner_strategy(
|
||||
*,
|
||||
ransac_filter: "RansacFilter",
|
||||
inference_runtime: "InferenceRuntime",
|
||||
clock: "Clock | None" = None,
|
||||
fdr_client: "FdrClient | None" = None,
|
||||
) -> "ConditionalRefiner":
|
||||
"""Construct the :class:`ConditionalRefiner` impl selected by config.
|
||||
|
||||
@@ -120,17 +124,26 @@ def build_refiner_strategy(
|
||||
module_name, class_name = module_info
|
||||
module = __import__(module_name, fromlist=[class_name])
|
||||
create_fn = getattr(module, "create", None)
|
||||
# ``clock`` and ``fdr_client`` were added with AZ-349; the AZ-348
|
||||
# passthrough refiner does not accept them. Only forward when wired.
|
||||
extra_kwargs: dict[str, object] = {}
|
||||
if clock is not None:
|
||||
extra_kwargs["clock"] = clock
|
||||
if fdr_client is not None:
|
||||
extra_kwargs["fdr_client"] = fdr_client
|
||||
if create_fn is None:
|
||||
strategy_cls = getattr(module, class_name)
|
||||
instance = strategy_cls(
|
||||
ransac_filter=ransac_filter,
|
||||
inference_runtime=inference_runtime,
|
||||
**extra_kwargs,
|
||||
)
|
||||
else:
|
||||
instance = create_fn(
|
||||
config,
|
||||
ransac_filter=ransac_filter,
|
||||
inference_runtime=inference_runtime,
|
||||
**extra_kwargs,
|
||||
)
|
||||
_LOG.info(
|
||||
"c3_5.refiner.strategy_loaded",
|
||||
|
||||
Reference in New Issue
Block a user