Star Field Pattern Match (StarFieldFromCatalogNav)
Overview
StarFieldFromCatalogNav is the pass-1
multi-star pattern matcher. It hashes catalog and detection triplets into a translation- and
rotation-invariant feature space, finds correspondences via a KD-tree match in that space,
and runs a RANSAC similarity-transform fit to recover the translation (plus optional in-plane
rotation) that maps the predicted catalog cohort onto the observed detections. The technique
runs without a prior — it is the ensemble’s primary star path on scenes where the SPICE
pointing error is too large for
StarUniqueMatchNav’s search
window to contain the brightest predictable star.
Feasibility passes when the predictable-star cohort has at least three usable
STAR features (the matcher cannot form a
single triplet below that count); feasibility fails otherwise.
Theory
The technique solves the global star-pattern-matching problem from scratch — without a prior offset — by reducing translation- and rotation-invariant pattern matching to a high-dimensional nearest-neighbour search.
Triplet hashing
For every triplet of three predictable catalog stars the matcher computes a translation- and rotation-invariant hash: the two ratios of side lengths (sorted longest-to-shortest), plus the included angle at the longest-side vertex. The same hash is computed for every triplet of three detected sources in the image. Triplets with matching hashes (within a small tolerance) are candidate correspondences.
Source detection
Detections come from a matched-filter search over the extfov image: the image is correlated
with a Gaussian PSF kernel of the configured psf_sigma_px, peaks above
detection_sigma * image_noise_sigma are kept, and a brightness-weighted moment around
each peak gives the sub-pixel centroid. Up to max_sources brightest detections feed the
hash-space search; the catalog side is similarly capped.
KD-tree match
The catalog and detection triplet hashes are loaded into a KD-tree in
(ratio_short_to_long, ratio_mid_to_long, angle_radians) space, weighted per axis
(hash_ratio_weight, hash_angle_weight); a query of every catalog hash against the
detection KD-tree returns the nearest detection triplet within hash_match_tolerance.
Each match implies a per-star correspondence between three catalog vertices and three
detection vertices.
RANSAC inlier validation
Each candidate correspondence proposes a similarity transform (rotation, translation, and an
implied unit scale). The matcher applies the proposed transform to every catalog star and
counts how many predicted positions land within inlier_tolerance_px of a detection.
The transform with the most inliers wins; below pattern_match_min_inliers the technique
reports spurious.
PSF-fit inlier refinement
The detection centroid that pins each source is a brightness-weighted moment: unbiased, but only noise-limited, so on a faint field its per-star scatter dominates the field offset. Once the inlier correspondences are fixed, each matched inlier is re-centroided to drive that scatter down. Two estimators are available per star and they trade off with brightness:
The brightness-weighted moment is unbiased; its error falls roughly as \(1/\mathrm{SNR}\) as the star brightens.
A maximum-likelihood PSF fit (
obs.star_psf().find_positionagainst the instrument’s modelled point-spread function) reaches the minimum variance and so wins decisively when the star is faint. An undersampled PSF, however, carries a fixed sub-pixel-phase bias floor (~0.08 px for the COISS NAC star PSF, sigma ~0.54 px) that does not improve with brightness.
The two error curves cross near an integrated SNR of ~30 at the field level (the per-star
crossover is a little higher, ~40, but the field fit averages the moment’s noise down faster
than the PSF fit’s partly-correlated bias). The technique therefore refines a matched inlier
with the PSF fit only while its box SNR is below the configurable ceiling
psf_refine_snr_max (default 30), and keeps the moment above it. The box SNR is
\(\sum (\text{box} - \text{median}) / \sqrt{\text{signal} + n_{\text{pix}}\,\sigma^{2}}\)
over the fit box. The PSF fit reports its position in the eval_rect convention (offset
measured from a pixel’s lower edge), so the technique subtracts the half-pixel to land in the
pixel-centre convention shared by the detection moment and the catalog prediction. Any inlier
whose fit fails (too close to the image edge, too few good pixels, no convergence) silently
falls back to its moment centroid. The whole step is gated by psf_refine_enabled and the
obs supplying a star_psf(); without either it is a no-op and the moment centroids stand.
The observation that drives this is brightness-dependent: a dim field (vmag 3-4) improves its recovered-offset median from ~0.052 px (moment-only) to ~0.023 px, while a bright field (vmag 0-0.8) recovers to ~0.005 px on the moment alone – where forcing the PSF fit would instead raise the error to ~0.056 px by exposing the bias floor. See the simulator performance report’s star-field centroiding section for the dim-vs-bright sweep.
Similarity-transform refit
Once the inlier set is known, the matcher runs a weighted Procrustes / Kabsch fit on the inlier correspondences (using the per-star inverse-trace covariance weights) to refine the similarity transform. The translation read off the fit is the reported offset; the rotation, when present, is the converged angle about the catalog-side weighted centroid.
Per-axis covariance
The reported translation covariance is derived from the inlier residual scatter and the
catalog-side centroid spread, per the same precision-weighted-mean form
StarRefineNav uses; see
Star Refinement (StarRefineNav) for the algebra. When the per-instrument
camera-rotation flag is on and the inlier count supports it, a 3x3 covariance with the
rotation diagonal is reported; otherwise the 2x2 translation block is reported on its own
or embedded in the rank-deficient 3x3.
Restrictions and assumptions
The matcher needs at least three usable stars to form one triplet. Below that count the technique reports infeasible without running.
The hash-space tolerance is empirically tuned to a typical centroid sigma of ~0.1 px; significantly noisier centroids may cause the KD-tree match to miss correct correspondences.
The matcher is translation- and rotation-invariant by construction but not scale-invariant; a Procrustes refit explicitly forces unit scale, which is correct for navigation but means the fit will not produce a useful result when the per-instrument plate scale is wrong.
Triplet hashing scales as \(O(M^{3})\) per side where \(M\) is the per-side cap (
max_sources). At the defaultmax_sources = 30the cost is ~27,000 triplets per side.
Sources of uncertainty
The reported covariance is the precision-weighted-mean form derived from inlier residual
scatter; it does not capture systematic biases (a per-star centroid offset that affects every
detection equally moves the global solution by the same offset and is invisible to the
inlier scatter), and it does not capture catalog-side errors (a star with a wrong predicted
position bumps the inlier count by zero or one and the bulk of the fit is unaffected). When
the converged offset sits within the at-edge tolerance of any axis bound, or when the
rotation parameter is at the configured fraction of its cap, the result is flagged
at_edge and the hard-zero
gate forces confidence to zero. When the inlier count falls below
pattern_match_min_inliers the result is flagged
spurious.
Configuration
All numeric tunables for this technique live in techniques.StarFieldFromCatalogNav.tuning
in src/nav/config_files/config_510_techniques.yaml.
max_sources— int, default30(count). Maximum number of brightest detected sources / brightest catalog stars per side feeding the matcher. Triplet count is \(O(M^{3})\), so 30 keeps the candidate list bounded at ~27,000 triplets per side.detection_sigma— float, default4.0(dimensionless). Detection-threshold multiplier on the per-image noise sigma; matched-filter peaks above this threshold are candidate detections.psf_sigma_px— float, default1.0px. Gaussian PSF sigma for the matched-filter kernel. Matches the typical ungroomed star PSF; per-instrument overrides may tighten or loosen this once the integration library exercises the full instrument set.centroid_box_half_px— int, default3px. Half-width of the brightness-weighted moment box around each accepted peak.hash_match_tolerance— float, default0.05(dimensionless). KD-tree match radius in the (ratio, ratio, angle_rad) hash space; weighted Euclidean. Empirical default for centroid sigma ~0.1 px against typical triplet scales.hash_ratio_weight— float, default1.0(dimensionless). Per-axis weight on the ratio dimensions in the hash distance metric.hash_angle_weight— float, default1.0(dimensionless). Per-axis weight on the angle dimension in the hash distance metric.inlier_tolerance_px— float, default2.0px. Maximum residual for a detection-to-catalog correspondence to count as an inlier under a candidate similarity transform.pattern_match_min_inliers— int, default6(count). Minimum inlier count for the matcher to accept a transform; below this the technique returns spurious. Must be at least 3 (the matcher needs at least one triplet per side).at_edge_tolerance_px— float, default1.0px. A converged offset whose absolute distance from any search-window axis bound falls within this tolerance is flaggedat_edge.rotation_at_edge_fraction— float, default0.95(dimensionless). Whenfit_camera_rotationis true, the converged rotation magnitude tripsat_edgeonce it crosses this fraction of the per-imagemax_rotation_degcap.psf_refine_enabled— int flag, default1.1enables the PSF-fit re-centroiding of matched inliers;0keeps the brightness-weighted moment centroid everywhere.psf_refine_box_px— int, default11px (odd). Square box side for the PSF fit and the integrated-SNR estimate around each inlier.psf_refine_search_limit_px— float, default2.0px. Maximum search distance from the moment centroid for the PSF fit; the moment is already within a pixel of truth.psf_refine_snr_max— float, default30.0(dimensionless). Integrated-SNR ceiling for the moment/PSF crossover: an inlier whose box SNR exceeds this keeps its moment centroid (its noise has already fallen below the PSF fit’s sub-pixel-phase bias floor); fainter inliers are refined with the PSF fit. The default is tuned to the field-level crossover on a nominal background; elevated read noise or a stray-light gradient pulls the optimum down toward ~16-21 (see the simulator performance report).
Per-instrument overrides
Per-instrument YAML files in src/nav/config_files/config_4N0_inst_*.yaml do not
override any of these knobs.
Confidence formula
The technique reports a calibrated confidence in \([0, 1]\) produced by the shared
sigmoid combination; see Confidence Calibration (Shared Sigmoid-of-Linear Combination). Spec is
techniques.StarFieldFromCatalogNav; consumes attributes off
StarFieldDiagnostics plus
at_edge and
spurious.
n_inliers— alpha = 1.0, offset = 6.0, divisor = 6.0, cap at 1.0. Number of detection-to-catalog inliers after RANSAC. Saturates at 12 inliers (offset = 6 plus full cap).median_residual_px— alpha = -1.0, offset = 0.0, divisor = 1.0, no cap. Median position residual on inliers. Larger residuals pull confidence down.n_detected_sources— alpha = 0.0, offset = 0.0, divisor = 30.0, cap at 1.0. Number of bright sources detected in the image. Carries no weight in the current confidence formula; the wiring is in place so a downstream recalibration can tune the alpha.n_catalog_predicted— alpha = 0.0, offset = 0.0, divisor = 30.0, cap at 1.0. Number of catalog stars in the extfov. Same posture as the detected-sources term.
Hard-zero gate: at_edge and
spurious either firing forces
confidence to zero before the sigmoid evaluates. The constant baseline is
\(\alpha_{0} = -2.0\). No post-sigmoid hard_cap is applied.
Implementation
Source files:
src/nav/nav_technique/nav_technique_star_field.py—StarFieldFromCatalogNavand the hashing / RANSAC / fit helpers.src/nav/nav_technique/_star_helpers.py— package-private helpersusable_stars(filter),local_centroid(per-source centroid), andsimilarity_transform_fit(Kabsch / Procrustes solve).src/nav/nav_technique/confidence.py— sigmoid-combination evaluator; documented at Confidence Calibration (Shared Sigmoid-of-Linear Combination).src/nav/nav_technique/diagnostics.py—StarFieldDiagnostics; documented at Per-Technique Diagnostics (Shared Dataclass Family).
Public class
StarFieldFromCatalogNav, base
NavTechnique. Self-registers via
__init_subclass__.
Class attributes:
name—'StarFieldFromCatalogNav'.accepts_feature_types—frozenset({STAR}).requires_prior—False.confidence_attributes—{'at_edge', 'spurious', 'n_inliers', 'median_residual_px', 'n_detected_sources', 'n_catalog_predicted'}.
Public methods (autodocumented at nav.nav_technique):
is_feasible() and
navigate().
Diagnostics
n_inliers— number of detection-to-catalog inliers. Consumed by the confidence formula and the spurious- detection gate.median_residual_px— median position residual on inliers. Consumed by the confidence formula.n_detected_sources— number of bright sources detected in the image.n_catalog_predicted— number of catalog stars in the extfov.n_triplets_evaluated— number of triplet candidates considered by RANSAC.
Call path
Call path traced through
navigate():
Open a logged section. Filter the offered features down to
usable_stars(predictable and not occluded by a body silhouette or ring annulus) and pull the predicted catalog positions / SNR off the per-featuregeometryandflags.Run the matched-filter detection over
image_extto find the brightest sources. Cap the catalog and detection cohorts atmax_sourceseach.Compute every catalog and detection triplet’s (ratio, ratio, angle) hash. Load both sets into a KD-tree and find correspondences within
hash_match_tolerance.RANSAC: for each candidate correspondence triplet, propose a similarity transform and count inlier matches across the full catalog cohort using
inlier_tolerance_px. Keep the transform with the most inliers.Below min_inliers. Return a spurious zero-confidence result with the diagnostic fields populated for the JSON sidecar.
Refine the inlier detection positions: for each matched inlier below the
psf_refine_snr_maxintegrated-SNR ceiling, replace its moment centroid with a maximum-likelihood PSF fit (obs.star_psf().find_position, half-pixel-corrected); brighter inliers and failed fits keep the moment. Skipped entirely whenpsf_refine_enabledis0or the obs exposes nostar_psf().Run a weighted Procrustes / Kabsch fit on the (refined) inlier correspondences via
similarity_transform_fit. The fit returns the rotation and translation; the translation is the reported offset.Result-shape branches on
fit_camera_rotationand the inlier count:Translation only (
fit_camera_rotationfalse). The (2, 2) covariance comes from the inlier residual scatter scaled by the catalog-side centroid spread.rotation_radandsigma_rotation_radareNone.Rotation fit, two or more inliers. The (3, 3) covariance has the per-axis translation variances and the rotation variance derived from the inlier residual scatter against the catalog-side spread (same algebra as
StarRefineNavdocumented at Star Refinement (StarRefineNav)).Rotation fit, one inlier. The (2, 2) translation block is embedded in a rank-deficient (3, 3) via
embed_rotation_unobservable();rotation_radis0.0and the sigma is the rotation-unobservable sentinel.
Apply the at-edge tests against the search-window axis bounds and the rotation cap.
Build a
StarFieldDiagnostics, evaluate the confidence spec viaevaluate_sigmoid_combination(), log the breakdown vialog_confidence_breakdown(), and assemble theNavTechniqueResult.
The feature_ids field
preserves every consumed
feature_id so the orchestrator’s curator can
attribute each per-star contribution at audit time.
Examples
star_dominated(Cassini ISS WAC, imageW1580760393_1)Dense star field with no body in FOV. The stars model emits one
STARfeature per predictable catalog star;StarFieldFromCatalogNavruns the triplet hash against the detected sources, lands on a similarity transform with several inliers, and reports a translation against the operator-verified offset \((\Delta v, \Delta u) = (-2.68, -3.68)\) px. The pass-2StarRefineNavconsumes this prior and polishes the offset using the full predictable cohort; see Star Refinement (StarRefineNav) for that walk-through.stars_plus_body(Cassini long-exposure background-stars scene class)One body and at least three usable catalog stars in the same FOV. The stars model emits one
STARfeature per predictable catalog star whose predicted position lies outside the body silhouette and any ring annulus; the body model emits aLIMB_ARC(orBODY_BLOB) for the body. On pass 1, theBodyLimbNavconsumes the body’s feature first and the orchestrator’s ensemble combine populates the per-image prior from the limb-derived offset.StarFieldFromCatalogNavruns in parallel on the star cohort: the triplet hash matches the predicted catalog stars against detected sources, the RANSAC inlier set lands on the same translation as the body fit, and the ensemble combine tightens the per-image covariance. On pass 2 theStarRefineNavconsumes the cohort and polishes the offset further.faint_stars(Galileo SSI / Voyager outer-leg scene class)Every catalog star in the FOV is fainter than the per-observation limiting magnitude
obs.star_max_usable_vmag(). The stars model emits noSTARfeatures that clear the magnitude gate. The technique’sis_feasible()fails with reasonno_usable_starsand the technique skips its navigate pass entirely. The orchestrator falls back to whichever body- or ring-derived technique is feasible on the scene and surfaces the per-technique infeasibility on the per-imageNavResultso the curator records which gate fired.