curryer.correction

Correction module for iterative geolocation alignment.

Provides tools for Monte Carlo-style correction loops, image matching against ground control points, PSF modelling, and error statistics.

Sub-module layout

config

Config models (GeolocationSetup, Sweep, OutputConfig, ParameterConfig, etc.) and the ParameterType enum.

io_config

NetCDF output configuration and standard attribute definitions (NetCDFConfig, NetCDFParameterMetadata, DEFAULT_NETCDF_ATTRIBUTES).

parameters

Random parameter-set generation (load_param_sets()).

kernel_ops

SPICE kernel creation and telemetry/time-offset application.

results_io

NetCDF result file read/write and checkpoint support.

pipeline

Main loop() orchestration and all per-iteration helpers. Preferred-name aliases: run_correction(), run_image_matching(), compute_error_stats(). run_correction() also accepts CorrectionInput objects.

grid_types

Pure grid data containers (ImageGrid, PSFGrid, …).

dataio

Validation helpers and S3 data-access utilities. The S3 utilities (S3Configuration, find_netcdf_objects, download_netcdf_objects) are optional convenience helpers for mission-specific data pipelines; the core correction API does not depend on them.

error_stats

Error statistics computation (ErrorStatsProcessor).

image_match

Image-matching algorithm (integrated_image_match).

io

Unified path resolution (resolve_path). Transparently handles local paths and S3 URIs (s3://…) when boto3 is installed. Optional convenience — the public API contract is local Path objects; S3 support is opt-in.

pairing

Ground-control-point pairing utilities.

psf

Point-spread-function modelling.

search

Image-search / correlation routines.

verification

Standalone geolocation compliance check (verify()).

Submodules

Classes

CalibrationFiles

Direct paths to instrument calibration inputs.

CorrectionInput

A single input set for the correction loop.

DataConfig

Configuration for config-driven internal data loading.

GeolocationConfig

SPICE kernel paths and instrument settings for geolocation.

GeolocationSetup

Durable, mission-specific setup for geolocation correction/verification.

NetCDFConfig

Configuration for NetCDF output structure and metadata.

NetCDFParameterMetadata

NetCDF metadata for a single output parameter variable.

OutputConfig

Output settings for a correction run.

ParameterConfig

A single parameter to vary during correction analysis.

ParameterSpec

Typed sampling specification for a single correction parameter.

ParameterType

Parameter types used in the correction configuration.

PSFSamplingConfig

Configuration for PSF sampling during image matching.

RegridConfig

Configuration for GCP chip regridding.

RequirementsConfig

Verification requirements / thresholds.

SearchConfig

Configuration for the image-matching correlation search grid.

SearchStrategy

Strategy used to generate parameter sets during correction analysis.

Sweep

A parameter-variation experiment run against a GeolocationSetup.

ErrorStatsConfig

Configuration for geolocation error statistics processing.

ErrorStatsProcessor

Production-ready processor for geolocation error statistics.

ImageGrid

Container for image data sampled on a latitude/longitude grid.

PSFGrid

Point spread function sampled on a latitude/longitude grid.

CorrectionResult

Structured result from run_correction().

ParameterSetResult

Results for a single parameter set in a correction sweep.

GCPError

Per-measurement/GCP error detail.

VerificationResult

Structured result from a verify() call.

Functions

load_config_files(→ tuple[GeolocationSetup, Sweep, ...)

Load (GeolocationSetup, Sweep, OutputConfig) from one JSON file.

load_setup_from_json(→ GeolocationSetup)

Load a GeolocationSetup from the "setup" section of a JSON file.

load_sweep_from_json(→ Sweep)

Load a Sweep from the "sweep" section of a JSON file.

compute_percent_below(→ float)

Compute the percentage of errors below a given threshold.

geolocated_to_image_grid(...)

Convert a geolocated xr.Dataset to an ImageGrid for image matching.

infer_spacecraft_state(→ tuple[numpy.ndarray, ...)

Deprecated — use resolve_spacecraft_ecef from curryer.correction.psf instead.

load_image_grid(→ curryer.correction.grid_types.ImageGrid)

Load any supported image file as an ImageGrid.

load_los_vectors(→ numpy.ndarray)

Load line-of-sight unit vectors from a .mat calibration file.

load_named_image_grid(...)

Load any supported image file as a NamedImageGrid.

load_observation_file(...)

Load one observation file and return (ImageGrid, spacecraft_position_m).

load_optical_psf(...)

Load optical PSF entries from a .mat calibration file.

save_image_grid(→ None)

Save an ImageGrid to file.

resolve_path(→ pathlib.Path)

Resolve a file path, downloading from S3 if necessary.

compute_error_stats(image_matching_results, setup)

Compute error statistics from image matching results.

loop(setup, sweep, work_dir, tlm_sci_gcp_sets[, ...])

Correction loop for parameter sensitivity analysis.

run_correction(...)

Run the correction parameter sweep.

run_image_matching(→ xarray.Dataset)

Run image matching against GCP reference.

compare_results(→ str)

Generate a side-by-side comparison of two verification results.

match_geolocated_to_gcp_files(→ list[xarray.Dataset])

Run image matching between already-geolocated data and GCP reference files.

verify(→ VerificationResult)

Evaluate current alignment against mission requirements.

Package Contents

class curryer.correction.CalibrationFiles(/, **data: Any)

Bases: pydantic.BaseModel

Direct paths to instrument calibration inputs.

Both fields are optional and interim: real line-of-sight vectors and spacecraft geometry will be SPICE-derived from telemetry rather than loaded from files, so nothing in the pipeline requires these.

los_vectors_file

Per-detector line-of-sight unit vectors (instrument frame).

psf_file

Optical point-spread-function calibration.

los_vectors_file: pathlib.Path | None = None
psf_file: pathlib.Path | None = None
class curryer.correction.CorrectionInput(/, **data: Any)

Bases: pydantic.BaseModel

A single input set for the correction loop.

Replaces the positional tuple (telemetry_path, science_path, gcp_path) with named fields for clarity and IDE autocomplete.

The reader for each file is chosen by DataConfig.file_format, so the inputs are format-agnostic. The first-class real-data path is a NetCDF image observation (radiance as the science variable) carrying telemetry, metadata, and science times; .mat files are interim test scaffolding.

Parameters:
  • telemetry_file (Path) – Telemetry observation file (NetCDF for real data; CSV/HDF5 also read).

  • science_file (Path) – Science/timing observation file (NetCDF for real data; CSV/HDF5 also read).

  • gcp_file (Path) – GCP reference-image file (NetCDF or .mat).

Examples

>>> from curryer.correction import CorrectionInput
>>> inputs = [
...     CorrectionInput(
...         telemetry_file="data/obs_20240317.nc",
...         science_file="data/obs_20240317.nc",
...         gcp_file="gcps/landsat_chip_001.nc",
...     )
... ]
telemetry_file: pathlib.Path
science_file: pathlib.Path
gcp_file: pathlib.Path
class curryer.correction.DataConfig(/, **data: Any)

Bases: pydantic.BaseModel

Configuration for config-driven internal data loading.

Replaces mission-specific loader callables with a declarative specification of how files should be read. The pipeline reads telemetry and science data directly from the provided file paths using pandas/xarray, applying the time_scale_factor to convert the science time column to uGPS.

file_format

File format for both telemetry and science data files. "csv" uses pandas.read_csv(); "netcdf" converts via xarray.open_dataset(); "hdf5" uses pandas.read_hdf().

time_scale_factor

Multiply science timestamps by this factor to obtain uGPS (microseconds since GPS epoch). For example, 1e6 converts GPS seconds to uGPS; 1.0 means the file already contains uGPS. The time column name is taken from GeolocationConfig.time_field (single source of truth).

position_columns

Explicit column name mappings for telemetry spacecraft-position data, e.g. ["sc_pos_x", "sc_pos_y", "sc_pos_z"]. None means use mission defaults from the geolocation configuration.

file_format: Literal['csv', 'netcdf', 'hdf5'] = 'csv'
time_scale_factor: float = 1.0
position_columns: list[str] | None = None
class curryer.correction.GeolocationConfig(/, **data: Any)

Bases: pydantic.BaseModel

SPICE kernel paths and instrument settings for geolocation.

meta_kernel_file

Path to the mission meta-kernel JSON file.

generic_kernel_dir

Directory containing generic/shared SPICE kernels.

dynamic_kernels

Kernels regenerated from telemetry each run (SC-SPK, SC-CK, etc.) but not altered by parameter variations.

instrument_name

SPICE instrument name (e.g. "CPRS_HYSICS").

time_field

Column name in the science DataFrame that holds uGPS timestamps.

minimum_correlation

Optional image-matching quality filter threshold (0.0–1.0).

meta_kernel_file: pathlib.Path
generic_kernel_dir: pathlib.Path
dynamic_kernels: list[pathlib.Path] = None
instrument_name: str
time_field: str
minimum_correlation: float | None = None
class curryer.correction.GeolocationSetup(/, **data: Any)

Bases: pydantic.BaseModel

Durable, mission-specific setup for geolocation correction/verification.

Built once per mission and reused across many Sweep runs. Holds everything that does not change when you vary which parameters are swept: SPICE kernels and instrument identity (GeolocationConfig), the pass/fail RequirementsConfig, how input data is read (DataConfig), static instrument calibration (CalibrationFiles), the science-Dataset variable names, and an optional custom image-matching implementation.

geo

SPICE kernels, instrument name, and science time field.

requirements

Pass/fail thresholds used by verification and the correction verdict.

data_config

How telemetry/science files are read. None uses CSV defaults.

calibration

Optional direct calibration file paths. None when geometry is supplied another way (e.g. SPICE-derived).

spacecraft_position_name, boresight_name, transformation_matrix_name

Variable names for the spacecraft-state fields in the image-matching xr.Dataset (mission-configurable; generic defaults).

image_matching_func

Optional custom image-matching callable. None uses the built-in image_matching(). Excluded from JSON serialisation because callables are not serialisable.

model_config

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

geo: GeolocationConfig
requirements: RequirementsConfig
data_config: DataConfig | None = None
calibration: CalibrationFiles | None = None
spacecraft_position_name: str = 'sc_position'
boresight_name: str = 'boresight'
transformation_matrix_name: str = 't_inst2ref'
image_matching_func: collections.abc.Callable | None = None
class curryer.correction.NetCDFConfig(/, **data: Any)

Bases: pydantic.BaseModel

Configuration for NetCDF output structure and metadata.

performance_threshold_m

Accuracy threshold in meters used to derive threshold-specific variable names (e.g. "percent_under_250m").

title

Global title attribute for the output NetCDF file.

description

Global description attribute for the output NetCDF file.

parameter_metadata

Optional mapping of parameter key → NetCDFParameterMetadata. Auto-generated from the sweep’s parameters when None.

standard_attributes

Optional mission-specific attribute overrides. Falls back to the module-level DEFAULT_NETCDF_ATTRIBUTES when None.

performance_threshold_m: float
title: str = 'Correction Geolocation Analysis Results'
description: str = 'Parameter sensitivity analysis'
parameter_metadata: dict[str, NetCDFParameterMetadata] | None = None
standard_attributes: dict[str, dict[str, str]] | None = None
property threshold_metric_name: str

Threshold-derived metric variable name, e.g. "percent_under_250m".

property standard_attributes_dict: dict[str, dict[str, str]]

Standard variable attributes, with optional mission-specific overrides.

Returns a shallow copy so callers may add derived entries (e.g. the threshold metric) without mutating the stored config.

get_parameter_netcdf_metadata(param_config: curryer.correction.config.ParameterConfig, angle_type: str | None = None) NetCDFParameterMetadata

Get NetCDF metadata for a parameter.

_auto_generate_metadata(param_config: curryer.correction.config.ParameterConfig, angle_type: str | None, base_key: str) NetCDFParameterMetadata

Auto-generate NetCDF metadata from parameter configuration.

class curryer.correction.NetCDFParameterMetadata(/, **data: Any)

Bases: pydantic.BaseModel

NetCDF metadata for a single output parameter variable.

variable_name: str
units: str
long_name: str
class curryer.correction.OutputConfig(/, **data: Any)

Bases: pydantic.BaseModel

Output settings for a correction run.

netcdf

NetCDF structure/metadata config. None is auto-populated by run_correction() from the setup’s performance threshold.

output_filename

Output NetCDF filename. None falls back to the default in get_output_filename().

netcdf: curryer.correction.io_config.NetCDFConfig | None = None
output_filename: str | None = None
get_output_filename(default: str = 'correction_results.nc') str

Return output_filename if set, otherwise default.

class curryer.correction.ParameterConfig(/, **data: Any)

Bases: pydantic.BaseModel

A single parameter to vary during correction analysis.

ptype

How this parameter is applied (constant kernel, offset kernel, or time offset).

config_file

Path to the SPICE kernel JSON template, or None for time offsets that require no kernel file.

spec

Sampling specification. Accepts a plain dict or None on construction (Pydantic coerces both to ParameterSpec automatically; None becomes an empty ParameterSpec()).

ptype: ParameterType
config_file: pathlib.Path | None = None
spec: ParameterSpec = None
classmethod _coerce_none_spec(values: Any) Any

Convert spec=None to an empty ParameterSpec (backward compat).

class curryer.correction.ParameterSpec(/, **data: Any)

Bases: pydantic.BaseModel

Typed sampling specification for a single correction parameter.

Strict by design (extra="forbid"): unknown fields raise a ValidationError so typos surface immediately. Mission-specific extras that the pipeline does not interpret go in metadata.

current_value

Baseline parameter value(s). A scalar for OFFSET_KERNEL/OFFSET_TIME and a 3-element list [roll, pitch, yaw] for CONSTANT_KERNEL.

bounds

[min, max] offset limits (same units as sigma).

sigma

Standard deviation for normal-distribution sampling. None means the parameter is held fixed at current_value.

units

Physical units string, e.g. "arcseconds" or "milliseconds".

distribution

Sampling distribution name. Stored for documentation purposes; the current implementation always uses a normal distribution.

field

Telemetry / science DataFrame column that this parameter modifies (required for OFFSET_KERNEL and OFFSET_TIME).

transformation_type

Optional hint consumed by kernel-creation routines (e.g. "dcm_rotation" or "angle_bias").

coordinate_frames

Optional list of SPICE frame names affected by this parameter.

metadata

Free-form mission-specific extras not interpreted by the pipeline (e.g. a display "name", provenance, calibration date).

model_config

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

current_value: float | list[float] = 0.0
bounds: list[float] = None
sigma: float | None = None
units: str | None = None
distribution: str = 'normal'
field: str | None = None
transformation_type: str | None = None
coordinate_frames: list[str] | None = None
metadata: dict[str, Any] = None
class curryer.correction.ParameterType

Bases: str, enum.Enum

Parameter types used in the correction configuration.

Specifies how a parameter is applied during geolocation analysis: whether as a constant kernel value, a kernel offset, or a time offset. String-valued so JSON configs use readable names ("OFFSET_TIME").

CONSTANT_KERNEL

Set a specific kernel value (e.g., fixed rotation angles).

OFFSET_KERNEL

Modify input kernel data by an offset.

OFFSET_TIME

Modify input timetags by an offset.

CONSTANT_KERNEL = 'CONSTANT_KERNEL'
OFFSET_KERNEL = 'OFFSET_KERNEL'
OFFSET_TIME = 'OFFSET_TIME'
class curryer.correction.PSFSamplingConfig

Configuration for PSF sampling during image matching.

Default values are calibrated for Landsat 30 m GCPs, which are the standard reference for GCP-based geolocation verification. Override all fields for instruments with different ground resolution or motion characteristics.

Parameters:
  • gcp_step_m (float, optional) – Ground control point step size in meters. Default is 30.0.

  • motion_convolution_step_m (float or None, optional) – Step size for spacecraft motion convolution in meters. If None (default), derived as gcp_step_m / 20.0 in __post_init__.

  • psf_lat_sample_dist_deg (float, optional) – PSF sample distance in the latitude direction in degrees. Default approximately 2.7 m at the equator (Landsat calibration).

  • psf_lon_sample_dist_deg (float, optional) – PSF sample distance in the longitude direction in degrees. Default approximately 2.7 m at the equator (Landsat calibration).

gcp_step_m: float = 30.0
motion_convolution_step_m: float | None = None
psf_lat_sample_dist_deg: float = 2.4397105613972e-05
psf_lon_sample_dist_deg: float = 2.8737038710207e-05
__post_init__() None
class curryer.correction.RegridConfig(/, **data: Any)

Bases: pydantic.BaseModel

Configuration for GCP chip regridding.

Specifies output grid parameters for transforming irregular geodetic grids to regular latitude/longitude grids. ECEF → geodetic conversion always uses the WGS84 ellipsoid, which is the only ellipsoid supported by curryer.compute.spatial.ecef_to_geodetic.

Parameters:
  • output_grid_size (tuple[int, int], optional) – Desired output grid dimensions as (nrows, ncols). Mutually exclusive with output_resolution_deg.

  • output_resolution_deg (tuple[float, float], optional) – Desired output resolution as (dlat, dlon) in degrees. Mutually exclusive with output_grid_size. Required when output_bounds is set.

  • output_bounds (tuple[float, float, float, float], optional) – Explicit output grid bounds as (minlon, maxlon, minlat, maxlat) in degrees. Requires output_resolution_deg.

  • conservative_bounds (bool, default=True) – If True, shrink bounds to ensure all output points lie within the input irregular grid (avoids edge extrapolation).

  • interpolation_method (str, default="bilinear") – Interpolation method; one of "bilinear" or "nearest".

  • fill_value (float, default=NaN) – Value assigned to output points that fall outside the input grid.

output_grid_size: tuple[int, int] | None = None
output_resolution_deg: tuple[float, float] | None = None
output_bounds: tuple[float, float, float, float] | None = None
conservative_bounds: bool = True
interpolation_method: str = 'bilinear'
fill_value: float
classmethod validate_method(v: str) str

Validate interpolation method name.

classmethod validate_grid_size(v: tuple[int, int] | None) tuple[int, int] | None

Validate that grid size has at least 2 rows and 2 columns.

classmethod validate_resolution(v: tuple[float, float] | None) tuple[float, float] | None

Validate that resolution values are positive.

classmethod validate_bounds(v: tuple[float, float, float, float] | None) tuple[float, float, float, float] | None

Validate that bounds are properly ordered.

validate_grid_spec() RegridConfig

Validate that grid specification options are mutually consistent.

class curryer.correction.RequirementsConfig(/, **data: Any)

Bases: pydantic.BaseModel

Verification requirements / thresholds.

Held as the required requirements field on GeolocationSetup and consumed by verify() and the correction verdict.

performance_threshold_m

Per-measurement nadir-equivalent error limit in meters (must be > 0). A measurement passes when its error is below this value.

Type:

float

performance_spec_percent

Minimum fraction of measurements (0–100) that must pass for the overall verification to be considered successful.

Type:

float

performance_threshold_m: float = None
performance_spec_percent: float = None
class curryer.correction.SearchConfig

Configuration for the image-matching correlation search grid.

Default values are tuned for Landsat 30 m GCPs. Override all fields for instruments with different ground resolution.

Parameters:
  • grid_size (int, optional) – Number of grid points per axis in the correlation search grid. Default 44 (Landsat-tuned).

  • grid_span_km (float, optional) – Half-width of the search grid in kilometers. Default 11.0.

  • reduction_factor (float, optional) – Multiplicative reduction applied to grid spacing each iteration. Default 0.8.

  • spacing_limit_m (float, optional) – Minimum grid spacing in meters; search stops when reached. Default 10.0 (Landsat-tuned).

grid_size: int = 44
grid_span_km: float = 11.0
reduction_factor: float = 0.8
spacing_limit_m: float = 10.0
class curryer.correction.SearchStrategy

Bases: str, enum.Enum

Strategy used to generate parameter sets during correction analysis.

RANDOM

Monte Carlo random walk (current default). Each iteration draws an independent sample from a normal distribution centered on the parameter’s current_value with the specified sigma, clipped to bounds. Requires seed and n_iterations on Sweep.

Deterministic cartesian-product sweep. For every parameter, grid_points_per_param evenly-spaced values are generated across the full bounds offset range and the cartesian product of all per-parameter grids is enumerated. n_iterations is ignored.

SINGLE_OFFSET

Deterministic single-parameter sweep. Each parameter is varied independently across n_iterations evenly-spaced values (spanning its bounds offset range) while all other parameters are held at their nominal current_value. Total parameter sets produced: len(parameters) × n_iterations.

RANDOM = 'random'
GRID_SEARCH = 'grid'
SINGLE_OFFSET = 'single'
class curryer.correction.Sweep(/, **data: Any)

Bases: pydantic.BaseModel

A parameter-variation experiment run against a GeolocationSetup.

Lightweight and cheap to copy, so a setup can be held fixed while rapidly trying parameter variations.

parameters

The parameters to vary (at least one).

search_strategy

How parameter sets are generated (RANDOM / GRID_SEARCH / SINGLE_OFFSET).

n_iterations

Iterations for RANDOM and values-per-parameter for SINGLE_OFFSET; ignored by GRID_SEARCH.

seed

Random seed for reproducible RANDOM sweeps.

grid_points_per_param

Evenly-spaced points per parameter for GRID_SEARCH.

max_grid_sets

Safety cap on total GRID_SEARCH parameter sets.

parameters: list[ParameterConfig] = None
search_strategy: SearchStrategy
n_iterations: int = None
seed: int | None = None
grid_points_per_param: int = None
max_grid_sets: int = None
_validate_search_strategy() Sweep

Ensure strategy-specific settings are consistent.

with_strategy(strategy: SearchStrategy | str, **sweep_changes: Any) Sweep

Return a copy of this sweep using a different search strategy.

Additional sweep-level fields (n_iterations, seed, grid_points_per_param, max_grid_sets) may be overridden via keyword arguments. The result is re-validated, so typos and strategy-inconsistent settings fail eagerly.

Examples

>>> grid = sweep.with_strategy("grid", grid_points_per_param=5)
>>> repro = sweep.with_strategy(SearchStrategy.RANDOM, seed=7, n_iterations=200)
update_param(selector: int | str, **spec_changes: Any) Sweep

Return a copy of this sweep with one parameter’s ParameterSpec changed.

selector is either an integer index into parameters, or a string matched against each parameter’s spec.field or its config_file stem. The changed spec is re-validated against ParameterSpec (which is extra="forbid"), so out-of-spec values or unknown field names raise immediately rather than being silently swallowed.

Examples

>>> wider = sweep.update_param("hps.az_ang_nonlin", bounds=[-100.0, 100.0])
>>> tighter = sweep.update_param(0, sigma=5.0)
_resolve_param_index(selector: int | str) int

Resolve selector (index, spec.field, or config_file stem) to an index.

curryer.correction.load_config_files(config_path: pathlib.Path) tuple[GeolocationSetup, Sweep, OutputConfig]

Load (GeolocationSetup, Sweep, OutputConfig) from one JSON file.

The file has three top-level sections — "setup", "sweep", and an optional "output" — each validated directly against its model. The "sweep".parameters entries mirror ParameterConfig (ptype / config_file / spec); rotation frames are authored as a single CONSTANT_KERNEL parameter with spec.current_value = [roll, pitch, yaw].

curryer.correction.load_setup_from_json(config_path: pathlib.Path) GeolocationSetup

Load a GeolocationSetup from the "setup" section of a JSON file.

curryer.correction.load_sweep_from_json(config_path: pathlib.Path) Sweep

Load a Sweep from the "sweep" section of a JSON file.

class curryer.correction.ErrorStatsConfig

Configuration for geolocation error statistics processing.

Parameters:
  • minimum_correlation (float or None, optional) – Minimum correlation filter threshold (0.0–1.0). Measurements whose correlation score falls below this value are excluded before processing. Default is None (no filtering).

  • variable_names (dict of str to str or None, optional) – Mission-agnostic variable name mappings from semantic names to actual dataset variable names. If None, generic defaults are used.

Notes

Pass/fail thresholds are not part of this config. ErrorStatsProcessor computes statistics only; whether those numbers meet mission requirements is the caller’s responsibility.

Earth radius is not a config field either. _EARTH_RADIUS_M (derived from curryer.compute.constants.WGS84_SEMI_MAJOR_AXIS_KM) is used directly in all calculations.

minimum_correlation: float | None = None
variable_names: dict[str, str] | None = None
classmethod from_setup(setup) ErrorStatsConfig

Create an ErrorStatsConfig from a GeolocationSetup.

Extracts the science-Dataset variable names and minimum_correlation from the setup, the single source of truth for those settings.

Parameters:

setup (GeolocationSetup) – The geolocation setup (variable names + geo settings).

Return type:

ErrorStatsConfig

get_variable_name(semantic_name: str) str

Get actual variable name for a semantic concept.

Parameters:

semantic_name (str) – Semantic name like ‘spacecraft_position’, ‘boresight’, etc.

Returns:

Actual variable name in the dataset.

Return type:

str

Raises:

ValueError – If variable_names is None or semantic_name is not found.

class curryer.correction.ErrorStatsProcessor(config: ErrorStatsConfig)

Production-ready processor for geolocation error statistics.

config
_filter_by_correlation(data: xarray.Dataset) xarray.Dataset

Filter measurements by correlation coefficient threshold.

Parameters:

data – Input dataset with optional ‘correlation’ or ‘ccv’ variable

Returns:

Filtered dataset with low-correlation measurements removed

compute_nadir_equivalent_errors(input_data: xarray.Dataset) xarray.Dataset

Compute per-measurement nadir-equivalent errors WITHOUT aggregate statistics.

This is the method to call inside the correction loop — it requires observation geometry (spacecraft position, boresight, transformation matrix) that is only available during each iteration, and produces nadir-equivalent errors for each measurement. No aggregate statistics are computed (meaningless for a single GCP pair in isolation).

Use this inside the loop for checkpoint/resume support. Call process_geolocation_errors() for the final aggregate pass (nadir-equivalent + comprehensive statistics).

Parameters:

input_data (xr.Dataset) – Dataset with required error measurement variables and a measurement dimension.

Returns:

Dataset with nadir_equiv_total_error_m and related intermediate variables. No statistical attributes are set on the output.

Return type:

xr.Dataset

Raises:

ValueError – If required variables are missing or all measurements are filtered out by the correlation threshold.

process_geolocation_errors(input_data: xarray.Dataset) xarray.Dataset

Full processing: nadir-equivalent errors + aggregate statistics.

Use this for final aggregation after the loop, or in verify(). For per-iteration computation (single GCP pair), prefer compute_nadir_equivalent_errors() to avoid computing aggregate statistics on a small or single-measurement sample.

Parameters:

input_data (xr.Dataset) – Dataset with required error measurement variables.

Returns:

Dataset with nadir_equiv_total_error_m and related intermediate variables, plus comprehensive statistics as global attributes.

Return type:

xr.Dataset

_validate_input_data(data: xarray.Dataset) None

Validate that input dataset contains all required variables.

_transform_boresight_vectors(bhat_hs: numpy.ndarray, t_hs2ctrs: numpy.ndarray) numpy.ndarray

Transform boresight vectors from HS to CTRS coordinate system.

_process_to_nadir_equivalent(ns_error_m: numpy.ndarray, ew_error_m: numpy.ndarray, riss_ctrs: numpy.ndarray, bhat_ctrs: numpy.ndarray, gcp_lat_rad: numpy.ndarray, gcp_lon_rad: numpy.ndarray, n_measurements: int) dict[str, numpy.ndarray]

Process error measurements to nadir-equivalent values.

_create_ctrs_to_uen_transform(lat_rad: float, lon_rad: float) numpy.ndarray

Create transformation matrix from CTRS to Up-East-North coordinates.

_calculate_view_plane_vectors(bhat_uen: numpy.ndarray) ViewPlaneVectors

Calculate view-plane and cross-view-plane unit vectors in UEN coordinates.

_calculate_scaling_factors(riss_ctrs: numpy.ndarray, theta: float) ScalingFactors

Calculate scaling factors for nadir-equivalent transformation.

_create_output_dataset(input_data: xarray.Dataset, results: dict[str, numpy.ndarray]) xarray.Dataset

Create output Xarray Dataset with processing results.

_calculate_statistics(nadir_equiv_errors_m: numpy.ndarray) dict[str, float | int]

Calculate comprehensive, mission-agnostic performance statistics.

This method intentionally does NOT include any pass/fail evaluation. Whether these statistics meet mission requirements is the caller’s responsibility. Use compute_percent_below() for custom threshold queries not covered by the standard table.

Parameters:

nadir_equiv_errors_m (np.ndarray) – Array of nadir-equivalent geolocation errors in meters.

Returns:

Keys: central tendency (mean_error_m, median_error_m, rms_error_m), spread (std_error_m, min_error_m, max_error_m), percentiles (p25_error_mp99_error_m), count (total_measurements), and a threshold table at standard intervals (percent_below_100mpercent_below_1000m).

Return type:

dict[str, float | int]

process_from_netcdf(filepath: str | pathlib.Path, minimum_correlation: float | None = None) xarray.Dataset

Load previous results from NetCDF and reprocess error statistics.

This enables iterative post-processing of Correction results without re-running expensive image matching operations.

Parameters:
  • filepath – Path to NetCDF file from previous Correction run

  • minimum_correlation – Override correlation threshold (if provided)

Returns:

Xarray Dataset with reprocessed error statistics

Example

>>> processor = ErrorStatsProcessor()
>>> # Try different correlation thresholds
>>> results_50 = processor.process_from_netcdf(
...     "correction_results/run_001.nc",
...     minimum_correlation=0.5
... )
>>> results_70 = processor.process_from_netcdf(
...     "correction_results/run_001.nc",
...     minimum_correlation=0.7
... )
curryer.correction.compute_percent_below(errors: numpy.ndarray, threshold_m: float) float

Compute the percentage of errors below a given threshold.

Useful for evaluating custom thresholds not in the standard table produced by ErrorStatsProcessor._calculate_statistics().

Parameters:
  • errors (np.ndarray) – Array of nadir-equivalent geolocation errors in meters.

  • threshold_m (float) – Threshold in meters.

Returns:

Percentage (0–100) of errors strictly below threshold_m. Returns 0.0 when errors is empty.

Return type:

float

class curryer.correction.ImageGrid

Container for image data sampled on a latitude/longitude grid.

data: numpy.ndarray
lat: numpy.ndarray
lon: numpy.ndarray
h: numpy.ndarray | None = None
__post_init__() None
_validate_shapes() None
property mid_indices: tuple[int, int]

Return the row/column indices of the central pixel.

class curryer.correction.PSFGrid

Point spread function sampled on a latitude/longitude grid.

data: numpy.ndarray
lat: numpy.ndarray
lon: numpy.ndarray
__post_init__() None
_validate_shapes() None
curryer.correction.geolocated_to_image_grid(geo_dataset: xarray.Dataset) curryer.correction.grid_types.ImageGrid

Convert a geolocated xr.Dataset to an ImageGrid for image matching.

This is the canonical adapter between SPICE geolocation output and the image-matching subsystem. It should be used wherever an ImageGrid is needed from an already-geolocated dataset, ensuring a single consistent implementation across the correction pipeline and the verification module.

Parameters:

geo_dataset (xr.Dataset) – Geolocated dataset with latitude and longitude variables (2-D arrays) and optionally altitude/height, radiance, or reflectance.

Returns:

Image grid with lat, lon, h from the dataset and data set to the first available of radiance, reflectance, or an all-ones placeholder.

Return type:

ImageGrid

Notes

Altitude/height defaults to an all-zeros array when absent. The placeholder data array (all ones) is used only for geometric operations where actual radiance values are not yet required.

curryer.correction.infer_spacecraft_state(grid: curryer.correction.grid_types.ImageGrid, r_spacecraft_m: numpy.ndarray | None, default_altitude_m: float = 400000.0) tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray]

Deprecated — use resolve_spacecraft_ecef from curryer.correction.psf instead.

curryer.correction.load_image_grid(filepath: pathlib.Path | str, mat_key: str = 'subimage') curryer.correction.grid_types.ImageGrid

Load any supported image file as an ImageGrid.

Dispatches on file extension — callers do not need to know the underlying format.

Parameters:
  • filepath (path-like) – Path to the image file. Local paths and s3:// URIs supported (S3 requires boto3).

  • mat_key (str, optional) – MATLAB struct key. Defaults to "subimage". Ignored for NetCDF.

Return type:

ImageGrid

Raises:
  • ValueError – If the file extension is not recognised.

  • FileNotFoundError – If filepath does not exist.

Examples

>>> obs = load_image_grid(Path("subimage.mat"), mat_key="subimage")
>>> gcp = load_image_grid(Path("GCP12055_regridded.nc"))
curryer.correction.load_los_vectors(mat_file: str | pathlib.Path, key: str = 'b_HS') numpy.ndarray

Load line-of-sight unit vectors from a .mat calibration file.

Parameters:
  • mat_file (str or Path) – Path to MATLAB file with LOS vectors (local path or s3:// URI).

  • key (str, default="b_HS") – Primary key to try for LOS data.

Returns:

LOS unit vectors in instrument frame, shape (n_pixels, 3).

Return type:

np.ndarray

Raises:
  • FileNotFoundError – If mat_file is a local path and doesn’t exist.

  • ImportError – If mat_file is an S3 URI and boto3 is not installed.

  • KeyError – If no LOS vectors found with common key names.

curryer.correction.load_named_image_grid(filepath: pathlib.Path | str, mat_key: str = 'subimage') curryer.correction.grid_types.NamedImageGrid

Load any supported image file as a NamedImageGrid.

Dispatches on file extension so callers do not need to know the underlying format. The returned grid always carries the file path as its name.

Supported formats

.mat

MATLAB struct file. The struct accessed via mat_key must have data, lat, and lon attributes (and optionally h).

.nc / .netcdf / .nc4

NetCDF file written by save_image_grid() or the regridding pipeline (expects band_data, lat, lon).

param filepath:

Path to the image file.

type filepath:

path-like

param mat_key:

MATLAB struct key to read. Defaults to "subimage". Ignored for NetCDF files.

type mat_key:

str, optional

returns:

Loaded image grid with name set to the string representation of filepath.

rtype:

NamedImageGrid

raises ValueError:

If the file extension is not recognised.

raises FileNotFoundError:

If filepath does not exist.

Examples

>>> obs = load_named_image_grid(Path("TestCase1a_subimage.mat"), mat_key="subimage")
>>> gcp = load_named_image_grid(Path("GCP12055Dili_regridded.nc"))
curryer.correction.load_observation_file(filepath: str | pathlib.Path, mat_key: str = 'subimage') tuple[curryer.correction.grid_types.ImageGrid, numpy.ndarray | None]

Load one observation file and return (ImageGrid, spacecraft_position_m).

Supports .mat and NetCDF (.nc, .nc4, .netcdf) formats. The spacecraft ECEF position is extracted when available in the file; when absent, None is returned and callers should fall back to infer_spacecraft_state().

Parameters:
  • filepath (str or Path) – Local path or s3:// URI to a .mat or .nc observation file.

  • mat_key (str, optional) – MATLAB struct key containing the image data. Defaults to "subimage". Ignored for NetCDF files.

Returns:

  • grid (ImageGrid) – Radiance data on a lat/lon grid.

  • r_spacecraft_m (ndarray of shape (3,) or None) – Spacecraft ECEF position in meters at the mid-frame, extracted from the file when available. None when not present — caller should use infer_spacecraft_state() to approximate.

Raises:
  • ValueError – If the file extension is not recognised.

  • FileNotFoundError – If filepath is a local path that does not exist.

  • ImportError – If filepath is an S3 URI and boto3 is not installed.

curryer.correction.load_optical_psf(mat_file: str | pathlib.Path, key: str = 'PSF_struct_675nm') list[curryer.correction.grid_types.OpticalPSFEntry]

Load optical PSF entries from a .mat calibration file.

Parameters:
  • mat_file (str or Path) – Path to MATLAB file with PSF data (local path or s3:// URI).

  • key (str, default="PSF_struct_675nm") – Primary key to try for PSF data.

Returns:

Optical PSF samples with data, x, and field_angle arrays.

Return type:

list[OpticalPSFEntry]

Raises:
  • FileNotFoundError – If mat_file is a local path and doesn’t exist.

  • ImportError – If mat_file is an S3 URI and boto3 is not installed.

  • KeyError – If no PSF data found with common key names.

  • ValueError – If PSF entries missing field angle attribute.

curryer.correction.save_image_grid(filepath: pathlib.Path, image_grid: curryer.correction.grid_types.ImageGrid, metadata: dict | None = None, compression: bool = True, band_units: str = 'DN', band_long_name: str = 'regridded_radiance') None

Save an ImageGrid to file.

The output format is determined by the file extension:

  • .nc / .netcdf / .nc4 → CF-1.8 NetCDF (via netCDF4)

  • .mat → MATLAB struct (key "GCP")

  • .tif / .tiff → GeoTIFF (requires rasterio)

Parameters:
  • filepath (Path) – Output file path. Extension controls the format.

  • image_grid (ImageGrid) – Image data with lat/lon coordinates.

  • metadata (dict, optional) – Additional metadata written as file-level attributes or tags.

  • compression (bool, optional) – Enable compression for NetCDF output (default True). Ignored for other formats.

  • band_units (str, optional) – units / long_name for the band data variable in NetCDF output (defaults describe a regridded Landsat GCP chip in digital numbers). Ignored for other formats.

  • band_long_name (str, optional) – units / long_name for the band data variable in NetCDF output (defaults describe a regridded Landsat GCP chip in digital numbers). Ignored for other formats.

Raises:

ValueError – If the file extension is not supported.

Examples

>>> save_image_grid(Path("chip.nc"), regridded, metadata={"band": "red"})
>>> save_image_grid(Path("chip.mat"), regridded)
curryer.correction.resolve_path(path: str | pathlib.Path, *, s3_client=None) pathlib.Path

Resolve a file path, downloading from S3 if necessary.

Parameters:
  • path (str or Path) – Local file path or S3 URI (s3://bucket/key).

  • s3_client (boto3 S3 client, optional) – Injected client for testing. If omitted and path is an S3 URI, a default client is created via boto3.

Returns:

Local file path. For S3 URIs, a temporary local file that is cleaned up at process exit via atexit().

Return type:

Path

Raises:
  • ImportError – If path is an S3 URI and boto3 is not installed.

  • FileNotFoundError – If path is a local path that does not exist.

  • ValueError – If path is an S3 URI with no object key (e.g. s3://bucket).

curryer.correction.compute_error_stats(image_matching_results, setup: curryer.correction.config.GeolocationSetup)

Compute error statistics from image matching results.

This is the preferred name for call_error_stats_module(). See call_error_stats_module() for full documentation.

Parameters:
  • image_matching_results (xr.Dataset or list of xr.Dataset) – Output from image matching, either a single dataset or a list.

  • setup (GeolocationSetup) – Geolocation setup used to initialise the error stats processor.

Returns:

Aggregate error statistics dataset.

Return type:

xr.Dataset

curryer.correction.loop(setup: curryer.correction.config.GeolocationSetup, sweep: curryer.correction.config.Sweep, work_dir: pathlib.Path, tlm_sci_gcp_sets: list[tuple[str, str, str]], output: curryer.correction.config.OutputConfig | None = None, resume_from_checkpoint: bool = False)

Correction loop for parameter sensitivity analysis.

Parameters:
  • setup (GeolocationSetup) – Durable mission setup: SPICE kernels/instrument (geo), pass/fail requirements, data_config (file format + time scaling), optional calibration files, mission variable names, and an optional image_matching_func override.

  • sweep (Sweep) – The parameter-variation experiment: parameters, search_strategy, n_iterations, seed, and grid settings.

  • work_dir (Path) – Working directory for temporary files.

  • tlm_sci_gcp_sets (list of (str, str, str)) – List of (telemetry_key, science_key, gcp_key) tuples. File paths are expected to be local. S3 URIs (s3://…) are also accepted as a convenience when boto3 is installed; see resolve_path().

  • output (OutputConfig or None, optional) – Output settings (NetCDF metadata + filename). None uses defaults derived from setup.requirements.

  • resume_from_checkpoint (bool, optional) – If True, resume from an existing checkpoint.

Returns:

  • results (list) – List of iteration results (order: pair_idx * N + param_idx).

  • netcdf_data (dict) – Dictionary of NetCDF variables indexed as [param_idx, pair_idx].

Notes

This implementation uses a pair-outer, parameter-inner loop order: - Outer loop: GCP pairs (load data once per image) - Inner loop: Parameter sets (reuse loaded data) This reduces file I/O and centralizes mission-specific behavior through the setup object.

Examples

Correction mode (parameter optimization):

from curryer.correction.config import DataConfig

results, netcdf_data = loop(setup, sweep, work_dir, tlm_sci_gcp_sets)

Where each element of tlm_sci_gcp_sets is a tuple of file paths:

tlm_sci_gcp_sets = [
    ("telemetry.csv", "science.csv", "landsat_chip_001.mat"),
]
curryer.correction.run_correction(setup: curryer.correction.config.GeolocationSetup, sweep: curryer.correction.config.Sweep, inputs: collections.abc.Sequence[curryer.correction.config.CorrectionInput | tuple[str, str, str]], work_dir: pathlib.Path, output: curryer.correction.config.OutputConfig | None = None, resume_from_checkpoint: bool = False) curryer.correction.results.CorrectionResult

Run the correction parameter sweep.

This is the preferred user-facing entry point (compared to loop()). Returns a structured CorrectionResult with the best parameter set, pass/fail verdict, recommendation, and a human-readable summary table. The raw results list and netcdf_data dict from loop() are available as result.results and result.netcdf_data for advanced use.

Parameters:
  • setup (GeolocationSetup) – Durable mission setup (kernels, requirements, calibration, names).

  • sweep (Sweep) – The parameter-variation experiment to run against setup.

  • inputs (list of CorrectionInput or list of (str, str, str)) – Each element is either a CorrectionInput (named fields) or a legacy (telemetry_key, science_key, gcp_key) tuple. Both forms may be mixed in the same list. File paths are expected to be local. S3 URIs (s3://…) are also accepted as a convenience when boto3 is installed; see resolve_path().

  • work_dir (Path) – Working directory for temporary files.

  • output (OutputConfig or None, optional) – Output settings (NetCDF metadata + filename). None uses defaults derived from setup.requirements.

  • resume_from_checkpoint (bool, optional) – If True, resume from an existing checkpoint.

Returns:

Structured result with best parameters, pass/fail verdict, recommendation, summary table, and raw NetCDF/intermediate data available on the returned object (for example, result.netcdf_data).

Return type:

CorrectionResult

curryer.correction.run_image_matching(geolocated_data: xarray.Dataset, gcp_reference_file: pathlib.Path, telemetry: pandas.DataFrame, params_info: list, setup: curryer.correction.config.GeolocationSetup, los_vectors_cached: numpy.ndarray | None = None, optical_psfs_cached: list | None = None) xarray.Dataset

Run image matching against GCP reference.

This is the preferred name for image_matching(). See image_matching() for full documentation.

Parameters:
  • geolocated_data (xr.Dataset) – Geolocated scene data with latitude/longitude.

  • gcp_reference_file (Path) – Path to the GCP reference image (.mat file).

  • telemetry (pd.DataFrame) – Telemetry DataFrame with spacecraft state.

  • params_info (list) – Parameter information for the current iteration.

  • setup (GeolocationSetup) – Geolocation setup (calibration paths, variable names, instrument name).

  • los_vectors_cached (np.ndarray or None, optional) – Pre-loaded LOS vectors; loaded from disk if None.

  • optical_psfs_cached (list or None, optional) – Pre-loaded optical PSFs; loaded from disk if None.

Returns:

Image matching results dataset.

Return type:

xr.Dataset

class curryer.correction.CorrectionResult(/, **data: Any)

Bases: pydantic.BaseModel

Structured result from run_correction().

Provides the instrument engineer with a clear answer: what is the best parameter set, does it meet requirements, and what should they do next.

Serialisation

Most fields are JSON-serialisable via model_dump() / model_dump_json(). The results and netcdf_data fields are excluded from serialisation by default because they contain non-JSON-serialisable types (xr.Dataset, numpy arrays). Access them directly on the object when raw data is needed:

result = run_correction(setup, sweep, inputs, work_dir)
result.best_parameter_set   # dict[str, float] — use this
result.results              # raw per-iteration dicts — advanced use only
result.netcdf_data          # raw numpy arrays — advanced use only
best_parameter_set

Parameter values that produced the lowest aggregate RMS.

Type:

dict[str, float]

best_rms_m

Best aggregate RMS achieved across all parameter sets (meters).

Type:

float

best_index

Index of the best parameter set (for cross-referencing with NetCDF output).

Type:

int

worst_rms_m

Worst aggregate RMS across all parameter sets (meters).

Type:

float

mean_rms_m

Mean of all aggregate RMS values (meters).

Type:

float

n_parameter_sets

Number of parameter sets tested in the sweep.

Type:

int

n_gcp_pairs

Number of GCP pairs used.

Type:

int

all_parameter_sets

All tested parameter sets sorted by mean RMS (ascending).

Type:

list[ParameterSetResult]

met_threshold

Whether the best parameter set met the mission performance requirements.

Type:

bool

recommendation

Human-readable next-step guidance for the instrument engineer.

Type:

str

summary_table

Human-readable ASCII table of the top results.

Type:

str

netcdf_path

Path to the saved NetCDF output file.

Type:

Path or None

config_snapshot

Key config values used, for reproducibility records.

Type:

dict

elapsed_time_s

Total wall-clock processing time in seconds.

Type:

float

timestamp

UTC time when the run completed.

Type:

datetime

results

Raw per-iteration result dicts from loop(). Excluded from JSON serialisation.

Type:

list

netcdf_data

Raw NetCDF numpy arrays from loop(). Excluded from JSON serialisation.

Type:

dict

model_config

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

best_parameter_set: dict[str, float]
best_rms_m: float
best_index: int
worst_rms_m: float
mean_rms_m: float
n_parameter_sets: int
n_gcp_pairs: int
all_parameter_sets: list[ParameterSetResult]
met_threshold: bool
recommendation: str
summary_table: str
netcdf_path: pathlib.Path | None = None
config_snapshot: dict = None
elapsed_time_s: float = 0.0
timestamp: datetime.datetime = None
results: list = None
netcdf_data: dict = None
class curryer.correction.ParameterSetResult(/, **data: Any)

Bases: pydantic.BaseModel

Results for a single parameter set in a correction sweep.

index

Zero-based index of this parameter set in the sweep.

Type:

int

parameter_values

Parameter name → sampled value for this set.

Type:

dict[str, float]

mean_rms_m

Mean RMS geolocation error across all GCP pairs (meters).

Type:

float

best_pair_rms_m

RMS error of the best-performing GCP pair (meters).

Type:

float

worst_pair_rms_m

RMS error of the worst-performing GCP pair (meters).

Type:

float

index: int
parameter_values: dict[str, float]
mean_rms_m: float
best_pair_rms_m: float
worst_pair_rms_m: float
class curryer.correction.GCPError(/, **data: Any)

Bases: pydantic.BaseModel

Per-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 None when error-stats processing was not performed.

Type:

float or None

correlation

Image-matching correlation score, or None when 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.VerificationResult(/, **data: Any)

Bases: pydantic.BaseModel

Structured result from a verify() call.

Most fields are JSON-serialisable via Pydantic’s model_dump() / model_dump_json(). The aggregate_stats field (an xr.Dataset) must be excluded when serialising to JSON — persist it separately (e.g. via aggregate_stats.to_netcdf(path)):

json_str = result.model_dump_json(exclude={"aggregate_stats"})
result.aggregate_stats.to_netcdf("verification_stats.nc")
passed

True when percent_within_thresholdrequirements.performance_spec_percent.

Type:

bool

per_gcp_errors

One entry per measurement in the aggregated dataset.

Type:

list[GCPError]

aggregate_stats

Full output from process_geolocation_errors().

Type:

xr.Dataset

requirements

The thresholds used for this verification run.

Type:

RequirementsConfig

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

warnings

Non-empty when passed is False.

Type:

list[str]

timestamp

UTC wall-clock time when verify() was called.

Type:

datetime

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 None when 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
per_gcp_errors: list[GCPError]
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.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:
Returns:

Human-readable side-by-side comparison table.

Return type:

str

curryer.correction.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 a frame coordinate (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.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)

  1. image_matching_results — pre-computed outputs from image matching; the most common entry point for weekly automated checks.

  2. geolocated_data — raw geolocated data. Either set setup.image_matching_func for a custom matcher, or supply gcp_directory, los_file, and psf_file to run built-in spatial pairing + image matching.

  3. gcp_pairs — explicit (observation_path, gcp_path) file-path pairs. Requires los_file and psf_file.

  4. observation_paths + gcp_directory — auto-paired via spatial overlap. Requires los_file and psf_file.

  5. 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 an s3:// URI (requires boto3).

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 (.mat file). Required when gcp_pairs or observation_paths is provided.

type los_file:

path or None

param psf_file:

Optical PSF .mat file. 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.0 for 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_func is not set; when los_file or psf_file is None for 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.