curryer.correction.verification¶
Verification module for geolocation requirements compliance.
Provides verify(), a standalone entry point that evaluates the current
set of SPICE kernels and alignment parameters against mission geolocation
requirements — without running the iterative correction loop.
Typical use-cases¶
- Weekly automated check (CLARREO)
Pass pre-computed
image_matching_results(the most common path):>>> result = verify(setup, image_matching_results=weekly_datasets, work_dir=work_dir) >>> if not result.passed: ... send_alert(result.summary_table)
- Post-correction validation
After a full GCS run, verify the optimised parameter set:
>>> result = verify(setup, image_matching_results=post_correction_datasets, work_dir=work_dir)
- One-off compliance check with in-memory geolocated data
Supply an already-geolocated dataset together with a GCP chip directory and calibration files; verification auto-pairs and image-matches without any additional setup:
>>> result = verify( ... setup, ... geolocated_data=raw_dataset, ... gcp_directory="data/gcps/", ... los_file="cal/b_HS.mat", ... psf_file="cal/optical_PSF_675nm.mat", ... )
Models¶
RequirementsConfigVerification thresholds (performance limit and pass-rate).
GCPErrorPer-measurement/GCP error detail.
VerificationResultStructured pass/fail result; serialisable via Pydantic JSON methods.
Attributes¶
Classes¶
Per-measurement/GCP error detail. |
|
Structured result from a |
Functions¶
|
Aggregate a list of per-GCP-pair image-matching datasets into one. |
|
Run |
|
Evaluate whether performance meets the threshold requirement. |
|
Generate user-facing warning messages when verification fails. |
|
Build a |
|
Map every measurement back to its (science_key, gcp_key) pair. |
|
Generate a human-readable summary table. |
|
Log a human-readable GCP pairing summary. |
Return the instrument boresight and HS→CTRS rotation matrix from SPICE. |
|
|
Extract spacecraft position at mid-frame from telemetry. |
|
Image matching using |
|
Aggregate multiple image matching results into one dataset. |
|
Run image matching between already-geolocated data and GCP reference files. |
|
Run image matching for a list of (observation, gcp) file-path pairs. |
|
Evaluate current alignment against mission requirements. |
|
Generate a side-by-side comparison of two verification results. |
Module Contents¶
- curryer.correction.verification.logger¶
- class curryer.correction.verification.GCPError(/, **data: Any)¶
Bases:
pydantic.BaseModelPer-measurement/GCP error detail.
Each instance corresponds to one row in the aggregated image-matching output — typically one measurement from a single GCP pair.
- gcp_index¶
Zero-based measurement index in the aggregated dataset.
- Type:
int
- science_key¶
Identifier for the science data segment (dataset label or index).
- Type:
str
- gcp_key¶
Identifier for the ground-control-point source.
- Type:
str
- lat_error_deg¶
Latitude error in degrees (positive = northward shift).
- Type:
float
- lon_error_deg¶
Longitude error in degrees (positive = eastward shift).
- Type:
float
- nadir_equiv_error_m¶
Nadir-equivalent total geolocation error in meters, or
Nonewhen error-stats processing was not performed.- Type:
float or None
- correlation¶
Image-matching correlation score, or
Nonewhen not available.- Type:
float or None
- passed¶
Whether this measurement satisfies the per-measurement threshold.
- Type:
bool
- model_config¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- gcp_index: int¶
- science_key: str¶
- gcp_key: str¶
- lat_error_deg: float¶
- lon_error_deg: float¶
- nadir_equiv_error_m: float | None = None¶
- correlation: float | None = None¶
- passed: bool¶
- class curryer.correction.verification.VerificationResult(/, **data: Any)¶
Bases:
pydantic.BaseModelStructured result from a
verify()call.Most fields are JSON-serialisable via Pydantic’s
model_dump()/model_dump_json(). Theaggregate_statsfield (anxr.Dataset) must be excluded when serialising to JSON — persist it separately (e.g. viaaggregate_stats.to_netcdf(path)):json_str = result.model_dump_json(exclude={"aggregate_stats"}) result.aggregate_stats.to_netcdf("verification_stats.nc")
- passed¶
Truewhenpercent_within_threshold≥requirements.performance_spec_percent.- Type:
bool
- aggregate_stats¶
Full output from
process_geolocation_errors().- Type:
xr.Dataset
- requirements¶
The thresholds used for this verification run.
- Type:
- summary_table¶
Human-readable ASCII table suitable for logging or reports.
- Type:
str
- percent_within_threshold¶
Percentage of measurements with nadir-equivalent error below
requirements.performance_threshold_m.- Type:
float
- files_processed¶
Science/GCP key pairs that were processed, as
"<sci_key>+<gcp_key>"strings. Empty when the source mapping is unavailable.- Type:
list[str]
- elapsed_time_s¶
Wall-clock time for the verify call in seconds, or
Nonewhen not measured.- Type:
float or None
- config_snapshot¶
Key config fields used for this run (threshold, spec percent, instrument name), for reproducibility records.
- Type:
dict or None
- model_config¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- passed: bool¶
- aggregate_stats: xarray.Dataset¶
- requirements: curryer.correction.config.RequirementsConfig¶
- summary_table: str¶
- percent_within_threshold: float¶
- warnings: list[str]¶
- timestamp: datetime.datetime¶
- files_processed: list[str] = None¶
- elapsed_time_s: float | None = None¶
- config_snapshot: dict | None = None¶
- curryer.correction.verification._aggregate_results(image_matching_results: list[xarray.Dataset], setup: curryer.correction.config.GeolocationSetup) xarray.Dataset¶
Aggregate a list of per-GCP-pair image-matching datasets into one.
For multiple input datasets, this delegates to the same aggregation logic used by the correction pipeline (
_aggregate_image_matching_results()). For a single input dataset, it is returned directly after ensuring that themeasurementcoordinate is present and consists of sequential integer indices.- Parameters:
image_matching_results (list[xr.Dataset]) – One element per GCP pair. Each dataset must have a
measurementdimension and at minimumlat_error_deg/lon_error_degvariables.setup (GeolocationSetup) – Used for mission-specific variable names (
spacecraft_position_name,boresight_name,transformation_matrix_name).
- Returns:
Combined dataset with a single
measurementdimension.- Return type:
xr.Dataset
- curryer.correction.verification._run_error_stats(aggregated: xarray.Dataset, setup: curryer.correction.config.GeolocationSetup) xarray.Dataset¶
Run
ErrorStatsProcessoron aggregated.- Parameters:
aggregated (xr.Dataset) – Combined image-matching result with a
measurementdimension.setup (GeolocationSetup) – Used to build
ErrorStatsConfig.
- Returns:
Processed dataset with
nadir_equiv_total_error_mand related intermediate variables.- Return type:
xr.Dataset
- curryer.correction.verification._check_threshold(aggregate_stats: xarray.Dataset, requirements: curryer.correction.config.RequirementsConfig) tuple[bool, float]¶
Evaluate whether performance meets the threshold requirement.
Uses
nadir_equiv_total_error_mfrom theErrorStatsProcessoroutput.- Parameters:
aggregate_stats (xr.Dataset) – Output of
ErrorStatsProcessor.process_geolocation_errors().requirements (RequirementsConfig) – Performance limits to evaluate against.
- Returns:
(passed, percent_within_threshold)- Return type:
tuple[bool, float]
- curryer.correction.verification._generate_warnings(passed: bool, percent_below: float, requirements: curryer.correction.config.RequirementsConfig) list[str]¶
Generate user-facing warning messages when verification fails.
- Parameters:
passed (bool) – Overall pass/fail result.
percent_below (float) – Percentage of measurements within the threshold.
requirements (RequirementsConfig) – Performance limits used for the check.
- Returns:
Empty list when passed is
True; otherwise one warning string.- Return type:
list[str]
- curryer.correction.verification._build_per_gcp_errors(aggregate_stats: xarray.Dataset, source_mapping: list[tuple[str, str]], requirements: curryer.correction.config.RequirementsConfig) list[GCPError]¶
Build a
GCPErrorfor every measurement in aggregate_stats.- Parameters:
aggregate_stats (xr.Dataset) – Processed dataset from
process_geolocation_errors().source_mapping (list[tuple[str, str]]) –
[(science_key, gcp_key), ...]parallel to the measurement dimension. If shorter than the number of measurements the remainder fall back to("sci_{i}", "gcp_{i}").requirements (RequirementsConfig) – Used for per-measurement pass/fail evaluation.
- Return type:
list[GCPError]
- curryer.correction.verification._build_source_mapping(image_matching_results: list[xarray.Dataset]) list[tuple[str, str]]¶
Map every measurement back to its (science_key, gcp_key) pair.
The mapping is derived from dataset attributes (
sci_key/gcp_key) when present, otherwise falls back to"result_{i}"labels.- Parameters:
image_matching_results (list[xr.Dataset]) – Raw per-GCP-pair datasets before aggregation.
- Returns:
Parallel to the
measurementdimension of the aggregated dataset.- Return type:
list[tuple[str, str]]
- curryer.correction.verification._format_summary_table(per_gcp_errors: list[GCPError], requirements: curryer.correction.config.RequirementsConfig, percent_within: float, passed: bool) str¶
Generate a human-readable summary table.
Example output:
┌──────────────────────────────────────────────────────┐ │ Verification Summary │ ├──────┬────────────┬────────────┬──────────┬──────────┤ │ GCP │ Lat err(°) │ Lon err(°) │ Nadir(m) │ Status │ ├──────┼────────────┼────────────┼──────────┼──────────┤ │ 0 │ 0.00123 │ -0.00045 │ 145.2 │ ✓ │ │ 1 │ 0.00567 │ 0.00234 │ 312.8 │ ✗ │ ├──────┴────────────┴────────────┴──────────┴──────────┤ │ Result: PASSED — 60.0% within 250.0m (req: 39.0%) │ └──────────────────────────────────────────────────────┘
- Parameters:
per_gcp_errors (list[GCPError]) – Per-measurement detail.
requirements (RequirementsConfig) – Thresholds used for evaluation.
percent_within (float) – Percentage of measurements within the threshold.
passed (bool) – Overall pass/fail result.
- Returns:
Multi-line formatted table.
- Return type:
str
- curryer.correction.verification._log_pairing_summary(pairs: list[tuple[pathlib.Path, pathlib.Path]], unpaired: list[pathlib.Path] | None = None) None¶
Log a human-readable GCP pairing summary.
- Parameters:
pairs (list of (Path, Path)) – Successfully paired (observation, gcp) paths.
unpaired (list of Path or None, optional) – Observation paths for which no matching GCP was found.
- curryer.correction.verification._get_spice_boresight_and_rotation(instrument_name: str, et_midframe: float, ref_frame: str = 'ITRF93') tuple[numpy.ndarray, numpy.ndarray]¶
Return the instrument boresight and HS→CTRS rotation matrix from SPICE.
- Parameters:
instrument_name (str) – SPICE instrument name (e.g.
"CPRS_HYSICS").et_midframe (float) – Ephemeris time (ET seconds past J2000) at the mid-frame epoch.
ref_frame (str, optional) – Target reference frame. Default
"ITRF93"(ECEF).
- Returns:
boresight (np.ndarray, shape (3,)) – Unit boresight vector in instrument (HS) frame.
t_hs2ctrs (np.ndarray, shape (3, 3)) – Rotation matrix
v_ctrs = R @ v_hs.
- Raises:
SpiceyError – If required kernels are not loaded or do not cover et_midframe.
- curryer.correction.verification._extract_spacecraft_position_midframe(telemetry: pandas.DataFrame, setup: curryer.correction.config.GeolocationSetup | None = None) numpy.ndarray¶
Extract spacecraft position at mid-frame from telemetry.
- Parameters:
telemetry (pd.DataFrame) – Telemetry DataFrame with spacecraft position columns.
setup (GeolocationSetup or None, optional) – If provided and
setup.data_config.position_columnsis set, those column names are used directly. Otherwise falls back to pattern-guessing (with a deprecation warning).
- Returns:
Shape
(3,)—[x, y, z]position in meters (J2000 frame).- Return type:
np.ndarray
- Raises:
ValueError – If
position_columnshas wrong length, or specified columns are not found, or pattern-guessing fails.
- curryer.correction.verification.image_matching(geolocated_data: xarray.Dataset, gcp_reference_file: pathlib.Path, telemetry: pandas.DataFrame | None = None, params_info: list | None = None, setup: curryer.correction.config.GeolocationSetup | None = None, los_vectors_cached: numpy.ndarray | None = None, optical_psfs_cached: list | None = None, r_iss_midframe: numpy.ndarray | None = None) xarray.Dataset¶
Image matching using
integrated_image_match().Performs image correlation between geolocated pixels and a Landsat GCP reference image to measure geolocation error.
This function is the single implementation used by both the correction loop (
loop()) and standalone verification (verify()).pipeline.pyimports it from here.- Parameters:
geolocated_data (xr.Dataset) – Geolocation output with
latitude,longitude, and aframecoordinate (GPS seconds =ugps_times / 1e6).gcp_reference_file (Path) – Path to GCP reference image (
.mator.nc).telemetry (pd.DataFrame or None, optional) – Telemetry DataFrame with spacecraft state. Required when r_iss_midframe is not supplied.
params_info (list or None, optional) – Current parameter values for error tracking. Defaults to
[].setup (GeolocationSetup or None, optional) – Setup for coordinate names, calibration paths, and instrument metadata.
los_vectors_cached (np.ndarray or None, optional) – Pre-loaded LOS vectors.
optical_psfs_cached (list or None, optional) – Pre-loaded optical PSF entries.
r_iss_midframe (np.ndarray of shape (3,) or None, optional) – Spacecraft ECEF position in meters at mid-frame. When provided, telemetry is not consulted for position.
- Returns:
Error measurements:
lat_error_deg,lon_error_deg, and metadata.- Return type:
xr.Dataset
- Raises:
ValueError – If neither telemetry nor r_iss_midframe is supplied, or if calibration files are missing.
- curryer.correction.verification._aggregate_image_matching_results(image_matching_results: list[xarray.Dataset], setup: curryer.correction.config.GeolocationSetup) xarray.Dataset¶
Aggregate multiple image matching results into one dataset.
- Parameters:
image_matching_results (list[xr.Dataset]) – Per-GCP-pair datasets from
image_matching().setup (GeolocationSetup) – Used for variable name mappings.
- Returns:
Combined dataset with a single
measurementdimension.- Return type:
xr.Dataset
- curryer.correction.verification.match_geolocated_to_gcp_files(geolocated_data: xarray.Dataset, gcp_files: list[pathlib.Path], setup: curryer.correction.config.GeolocationSetup, los_vectors_cached: numpy.ndarray | None = None, optical_psfs_cached: list | None = None) list[xarray.Dataset]¶
Run image matching between already-geolocated data and GCP reference files.
This is the reusable pipeline tail: both the correction loop (after kernel tweaking and geolocation) and standalone
verify()call this function. Given geolocated data and a list of GCP reference files it performs image matching against each file and returns the per-GCP error datasets ready for aggregation and error-stats processing.- Parameters:
geolocated_data (xr.Dataset) – Geolocated observation dataset with
latitude,longitude, and aframecoordinate (GPS seconds).gcp_files (list of Path) – GCP reference files to match against.
setup (GeolocationSetup) – Mission setup (calibration paths, variable names, instrument name).
los_vectors_cached (np.ndarray or None, optional) – Pre-loaded LOS vectors.
optical_psfs_cached (list or None, optional) – Pre-loaded optical PSF entries.
- Returns:
One error dataset per successfully matched GCP file. Failures are logged as warnings and skipped.
- Return type:
list of xr.Dataset
- curryer.correction.verification._run_image_matching_for_pairs(pairs: list[tuple[str | pathlib.Path, str | pathlib.Path]], los_file: str | pathlib.Path, psf_file: str | pathlib.Path, setup: curryer.correction.config.GeolocationSetup, default_altitude_m: float = 400000.0) list[xarray.Dataset]¶
Run image matching for a list of (observation, gcp) file-path pairs.
Loads each observation and GCP file, infers spacecraft state, runs
integrated_image_match(), and packages the result as anxr.Datasetcompatible withverify().- Parameters:
pairs (list of (Path, Path)) –
(observation_path, gcp_path)tuples.los_file (Path) – Instrument line-of-sight vectors (
.matfile).psf_file (Path) – Optical PSF
.matfile.setup (GeolocationSetup) – Used for spacecraft-state variable names.
default_altitude_m (float, optional) – Fallback spacecraft altitude in meters when the observation file does not contain position data. Default 400 000 m (ISS nominal orbit).
- Returns:
One dataset per successfully matched pair. Failures are logged as warnings and skipped.
- Return type:
list[xr.Dataset]
- curryer.correction.verification.verify(setup: curryer.correction.config.GeolocationSetup, gcp_pairs: list[tuple[str | pathlib.Path, str | pathlib.Path]] | None = None, observation_paths: list[str | pathlib.Path] | None = None, gcp_directory: str | pathlib.Path | None = None, los_file: str | pathlib.Path | None = None, psf_file: str | pathlib.Path | None = None, max_distance_m: float = 0.0, gcp_pattern: str = '*_regridded.nc', default_altitude_m: float = 400000.0, image_matching_results: list[xarray.Dataset] | None = None, geolocated_data: xarray.Dataset | None = None, work_dir: pathlib.Path | None = None) VerificationResult¶
Evaluate current alignment against mission requirements.
No parameter variation, no kernel creation, no iteration loop. This function checks whether a given set of alignment parameters meets geolocation requirements.
Input priority (first match wins)¶
image_matching_results — pre-computed outputs from image matching; the most common entry point for weekly automated checks.
geolocated_data — raw geolocated data. Either set
setup.image_matching_funcfor a custom matcher, or supply gcp_directory, los_file, and psf_file to run built-in spatial pairing + image matching.gcp_pairs — explicit
(observation_path, gcp_path)file-path pairs. Requires los_file and psf_file.observation_paths + gcp_directory — auto-paired via spatial overlap. Requires los_file and psf_file.
None of the above provided — raises
ValueError.
- param setup:
Mission setup with all geolocation/calibration settings.
- type setup:
GeolocationSetup
- param gcp_pairs:
Explicit
(observation_path, gcp_path)pairs. Each path may be a local path or ans3://URI (requiresboto3).- type gcp_pairs:
list of (path, path) or None
- param observation_paths:
Observation file paths for automatic GCP pairing. Requires gcp_directory, los_file, and psf_file.
- type observation_paths:
list of path or None
- param gcp_directory:
Directory of GCP reference images for automatic pairing with observation_paths.
- type gcp_directory:
path or None
- param los_file:
Instrument line-of-sight vectors (
.matfile). Required when gcp_pairs or observation_paths is provided.- type los_file:
path or None
- param psf_file:
Optical PSF
.matfile. Required when gcp_pairs or observation_paths is provided.- type psf_file:
path or None
- param max_distance_m:
Spatial pairing margin for the auto-pair mode (default
0.0— GCP center must be inside the observation footprint).- type max_distance_m:
float, optional
- param gcp_pattern:
Glob pattern used to discover GCP chips when gcp_directory is provided. Defaults to
"*_regridded.nc".- type gcp_pattern:
str, optional
- param default_altitude_m:
Fallback spacecraft altitude (meters) used when observation files do not contain position data. Default
400_000.0(ISS nominal orbit). Override for other platforms (e.g.505_000.0for CTIM).- type default_altitude_m:
float, optional
- param image_matching_results:
Pre-computed image-matching datasets, one per GCP pair.
- type image_matching_results:
list[xr.Dataset] or None
- param geolocated_data:
Already-geolocated data. Matched either via
setup.image_matching_func(custom override) or, when gcp_directory, los_file, and psf_file are supplied, via built-in spatial pairing + image matching.- type geolocated_data:
xr.Dataset or None
- param work_dir:
Working directory for outputs. Created if absent.
- type work_dir:
Path or None, optional
- returns:
Structured pass/fail result with per-GCP detail and a human-readable
summary_table.- rtype:
VerificationResult
- raises ValueError:
When none of the input modes is provided; when geolocated_data is supplied without gcp_directory / los_file / psf_file and
setup.image_matching_funcis not set; when los_file or psf_file isNonefor a file-path mode (gcp_pairs or observation_paths + gcp_directory); when observation_paths and gcp_directory are not both supplied; or when image matching produces no results.- raises FileNotFoundError:
If any of the supplied file paths do not exist.
- curryer.correction.verification.compare_results(before: VerificationResult, after: VerificationResult) str¶
Generate a side-by-side comparison of two verification results.
Useful for evaluating whether a correction run improved geolocation accuracy relative to a baseline.
- Parameters:
before (VerificationResult) – Baseline verification result (e.g., pre-correction).
after (VerificationResult) – Updated verification result (e.g., post-correction).
- Returns:
Human-readable side-by-side comparison table.
- Return type:
str