Orchestrator (NavOrchestrator)

Overview

NavOrchestrator is the top-level driver that turns one observation into one NavResult. The driver builds the per-image NavContext, runs every registered NavModel’s create_model() and to_features(), applies the feature-reliability gate, runs every feasible prior-free NavTechnique (pass 1), reconciles those results into a prior via ensemble(), runs every feasible prior-required technique against that prior (pass 2), and reconciles the union of pass-1 and pass-2 results into the final NavResult.

Two passes, four short-circuit gates, and an exception-sandbox discipline keep the driver from raising through to its caller — every failure mode surfaces on the returned NavResult instead. The four short-circuit gates are:

  • _HARD_FAILURE_TO_REASON — the NavImageClassifier reports blank, fully_overexposed, mostly_missing_data, or corrupt.

  • NO_FEATURES_EXTRACTED — every NavModel emitted zero features.

  • ALL_FEATURES_GATED — the reliability gate dropped every emitted feature.

  • NO_FEASIBLE_TECHNIQUES — pass 1 produced zero technique results (no technique was feasible on the gated cohort).

Theory

The orchestrator is a deterministic pipeline. Each per-image run is a sequence of stages: build context, build models, extract features, gate features, run pass 1, fuse pass 1 into a prior, run pass 2, fuse the union, return a NavResult.

Pipeline stages

  1. Provenance and context. Build a reproducibility envelope (git SHA, loaded SPICE kernels, static-data hashes) and a per-image global-state container. The context carries the extfov image, the per-image masks (sensor / saturation / cosmic-ray), the per-image noise sigma, the image-quality classifier verdict, the shared image-side derivatives (gradient magnitude, gradient vector, edge distance transform), and the per-instrument settings (data units, rotation flag, max rotation cap, signal-to-image scale).

  2. Model construction. Iterate the registered NavModel set, calling each model’s create_model() per the operator’s only_models glob filter. An exception in any model is logged and the model is dropped from the per-image set.

  3. Feature extraction. Each surviving model’s to_features() is invoked; an exception is logged and the model contributes zero features.

  4. Reliability gate. The per-feature reliability gate (an instance of FeatureReliabilityGate) drops features whose per-feature reliability score falls below the per-type floor.

  5. Pass 1. Run every registered technique with requires_prior False whose accepts_feature_types overlaps the surviving feature set and whose is_feasible reports feasible.

  6. Pass-1 ensemble. Reconcile the pass-1 results via ensemble(). When the ensemble produces an offset, that offset and its covariance become the pass-2 prior; when the ensemble fails the technique pipeline returns a failed NavResult immediately.

  7. Pass 2. Run every prior-required technique with the pass-2 context (a copy of the pass-1 context with the prior offset attached via with_prior()).

  8. Final ensemble. Reconcile the union of pass-1 and pass-2 results into the final NavResult.

Exception-sandbox discipline

Every NavModel callback (create_model, to_features, to_annotations) and every NavTechnique callback (navigate) runs inside a broad except Exception block. An exception is logged with full traceback and the offending model or technique is treated as if it produced no output. This is intentional — the orchestrator must never raise through to its caller; failures land on the status field instead so the per-image JSON sidecar always exists.

Glob-pattern model and technique filters

The constructor accepts only_models and only_techniques glob patterns (or pattern lists) so an operator can restrict which models or techniques run for debugging. The syntax matches gitignore-style globs with leading ! for exclusion. The model-name convention is prefix:VALUE (rings:SATURN, body:DIONE, plain stars for the catalog star model); a pattern missing a colon expands to prefix:* so the common shorthand (rings) matches every namespaced ring model under that prefix.

Restrictions and assumptions

  • The orchestrator assumes NavModel and NavTechnique subclasses are well-formed and non-malicious — the exception sandbox catches programmer bugs but cannot recover from state-corrupting plugins.

  • The pass-1 → pass-2 hand-off only fires when the pass-1 ensemble produces a non-failed offset. When pass 1 fails, pass 2 does not run.

  • The image-quality classifier’s hard-failure short-circuit fires before any model is constructed; a blank or fully_overexposed image never invokes a NavModel.

Sources of uncertainty

The orchestrator itself reports no uncertainty. Per-technique uncertainty is captured in each technique’s covariance_px2; the final covariance_px2 is the precision-weighted-merge result from ensemble().

Configuration

The orchestrator’s runtime knobs come from three places: per-instrument YAML (config_4N0_inst_*.yaml via InstrumentSettings), construction-time overrides on the constructor, and the per-image NavContext’s default values.

Per-instrument YAML keys (read via instrument_settings_from_obs()):

  • data_unitsraw_dn or calibrated_if. Drives the saturation-mask and noise-sigma branches.

  • noise.saturation_dn — saturation DN threshold (raw_dn only).

  • noise.marker_value — missing-data marker DN.

  • image_quality_thresholds.* — the per-instrument ImageQualityThresholds values.

  • camera_rotation.fit_camera_rotation — bool; turns on 3-DoF technique fits.

  • camera_rotation.max_rotation_deg — float; rotation cap.

Constructor-level overrides:

  • models — list of constructed NavModel instances. The caller builds these per-image.

  • config — optional Config override.

  • only_models — glob pattern or list selecting which models run. Default '*'.

  • only_techniques — glob pattern or list selecting which techniques run. Default '*'.

  • ensemble_config — optional EnsembleConfig override.

  • image_quality_thresholds — optional ImageQualityThresholds override.

  • image_derivatives_config — optional ImageDerivativesConfig override.

  • rms_nav_version — string written into provenance.

Implementation

Source files:

Public class NavOrchestrator, base NavBase.

Public methods (autodocumented at nav.nav_orchestrator):

  • prepare() — build per-image state without running any technique. Returns (context, features). Used by the manual-nav dialog.

  • navigate() — run the full pipeline on one observation. Returns one NavResult.

Call path

Call path traced through navigate():

  1. Build the Provenance envelope.

  2. Build the per-image NavContext and the image classifier verdict. Log the verdict at INFO.

  3. Hard-failure short-circuit. When the classifier’s image_class is in _HARD_FAILURE_TO_REASON (blank / fully_overexposed / mostly_missing_data / corrupt), return a failed NavResult with the matching NavStatusReason.

  4. Build every NavModel by invoking create_model() (exception-sandboxed).

  5. Extract features from each surviving model (to_features(), exception-sandboxed) and apply the reliability gate.

  6. Build the feature inventory, the model metadata snapshot, and the merged annotations.

  7. Three short-circuit gates on the gated cohort:

    • NO_FEATURES_EXTRACTED when every model emitted zero features.

    • ALL_FEATURES_GATED when the gate dropped every feature.

    Each returns a failed NavResult via the _fail helper.

  8. Pass 1. Run every prior-free technique whose feature types overlap the gated cohort and whose is_feasible returns feasible. An exception in any technique is logged and the technique contributes no result.

  9. NO_FEASIBLE_TECHNIQUES when pass 1 produced zero results — return a failed NavResult.

  10. Reconcile pass-1 results via ensemble(). When the ensemble’s status is 'failed' return the failed NavResult.

  11. Build the pass-2 context via with_prior() carrying the pass-1 ensemble’s offset and covariance.

  12. Pass 2. Run every prior-required technique against the pass-2 context.

  13. Reconcile the union of pass-1 and pass-2 results via ensemble() to produce the final NavResult.

  14. Log the final offset / confidence / per-technique summary and the per-status-reason operator INFO lines from STATUS_REASON_INFO_TEMPLATE.

The _fail helper wraps failed() and emits the per-status-reason INFO log lines defined in STATUS_REASON_INFO_TEMPLATE. Every short-circuit gate listed above invokes _fail to keep the operator log consistent.

Examples

body_partial_overflow (Cassini ISS NAC, image N1484593951_2)

Rhea visible in the upper right with about 22 % of the disc off-frame. The orchestrator builds the body model, extracts LIMB_ARC + BODY_DISC + TERMINATOR_ARC features, runs pass 1 with BodyLimbNav, BodyDiscCorrelateNav, and BodyTerminatorNav (the latter two flag spurious; only BodyLimb produces a usable result). The pass-1 ensemble selects BodyLimb’s offset of (12.06, 30.53) px, the pass-2 prior is set, and pass 2 runs no prior-required techniques (no STAR features in this scene). The final NavResult reports status 'ok' against the operator-verified offset \((\Delta v, \Delta u) = (11.0, 29.5)\) px.

one_bright_star_no_body (Cassini ISS WAC, image W1449079117_1)

Single bright star (Vega) in an otherwise empty FOV. The orchestrator’s pass-1 StarUniqueMatchNav produces a 1-star prior; pass 2 runs StarRefineNav against that prior, polishes the offset, and returns it with confidence capped at 0.5 by the single-inlier post-sigmoid cap. The final ensemble fuses both technique results and reports status 'ok' against the operator-verified offset \((\Delta v, \Delta u) = (3.06, -0.02)\) px.

star_dominated (Cassini ISS WAC, image W1580760393_1)

Dense star field with no body in FOV. Pass 1 runs StarFieldFromCatalogNav, which produces a multi-star prior via the triplet hash; pass 2 runs StarRefineNav, which polishes the offset using the full predictable cohort. The final ensemble fuses both and reports the operator-verified offset \((\Delta v, \Delta u) = (-2.68, -3.68)\) px.

Hard-failure short-circuit example. An over-exposed Cassini calibration target returns the classifier’s fully_overexposed class. The orchestrator returns the failed NavResult with status_reason IMAGE_OVEREXPOSED before any NavModel constructs. The per-image JSON sidecar still emits with the classifier verdict and the provenance envelope so reviewer tooling can correlate the failure across an entire campaign.