curryer.correction.config

Configuration models and enumerations for the geolocation correction pipeline.

This module defines the data structures that represent the complete configuration for a correction analysis run, including:

  • ParameterType – enum of the three parameter application strategies

  • ParameterSpec – sampling specification for a single correction parameter

  • ParameterConfig – assembles a parameter’s type, kernel file, and sampling spec

  • GeolocationConfig – SPICE kernel paths and instrument settings

  • NetCDFParameterMetadata / NetCDFConfig – NetCDF output metadata (re-exported from io_config)

  • GeolocationSetup – durable, mission-specific setup (built once, reused across sweeps)

  • Sweep – the lightweight, frequently-varied parameter experiment

  • OutputConfig – output settings (NetCDF metadata + filename)

  • KernelContext, CalibrationData, ImageMatchingContext – lightweight NamedTuples used to pass state between pipeline helper functions

  • load_setup_from_json / load_sweep_from_json / load_config_files – build the setup / sweep / output models from a JSON file

All mission-specific values (kernel filenames, parameter ranges, instrument names) live in mission configuration modules (e.g. tests/test_correction/clarreo_config.py) and are injected via GeolocationSetup / Sweep.

All config objects are pydantic.BaseModel subclasses which provide: - Automatic type validation and clear ValidationError messages on construction - Free JSON serialization via model_dump_json() / model_validate_json() - IDE autocomplete on every field

Parameter configuration — three cooperating classes

These three classes each capture a distinct, orthogonal concern:

ParameterType (enum)

How the value is applied to the pipeline. Each member maps to a different pipeline code path:

  • CONSTANT_KERNEL — replace the kernel value with the sampled value

  • OFFSET_KERNEL — shift the existing kernel value by the sampled offset

  • OFFSET_TIME — shift the input timestamps by the sampled offset

ParameterType belongs on ParameterConfig (not on ParameterSpec) because it describes application mechanics, not sampling statistics.

ParameterSpec

How to draw samples. Holds the statistical description of the search space: the nominal current_value, the bounds that clip samples, the sigma for normal-distribution sampling, physical units, and optional field / coordinate_frames hints consumed by kernel-creation routines. ParameterSpec is intentionally agnostic about how any drawn value will be applied — that is ParameterType’s job.

ParameterConfig

Assembles the three concerns. A single ParameterConfig answers: “vary this kernel file (config_file), applied as this kind of change (ptype), drawing samples according to this spec (spec).” ParameterConfig is also the right place for cross-field validation (e.g. confirming that data.field is provided whenever ptype requires it).

Typical construction:

ParameterConfig(
    ptype=ParameterType.OFFSET_TIME,
    config_file=None,          # time offsets need no kernel file
    spec=ParameterSpec(
        current_value=0.0,
        bounds=[-50.0, 50.0],
        sigma=10.0,
        units="milliseconds",
        field="time_ugps",     # required for OFFSET_KERNEL / OFFSET_TIME
    ),
)

Attributes

Classes

KernelContext

Context for SPICE kernel loading during geolocation.

CalibrationData

Pre-loaded calibration data for image matching.

ImageMatchingContext

Context data needed for image matching operations.

DataConfig

Configuration for config-driven internal data loading.

ParameterType

Parameter types used in the correction configuration.

SearchStrategy

Strategy used to generate parameter sets during correction analysis.

ParameterSpec

Typed sampling specification for a single correction parameter.

ParameterConfig

A single parameter to vary during correction analysis.

GeolocationConfig

SPICE kernel paths and instrument settings for geolocation.

RequirementsConfig

Verification requirements / thresholds.

PSFSamplingConfig

Configuration for PSF sampling during image matching.

SearchConfig

Configuration for the image-matching correlation search grid.

RegridConfig

Configuration for GCP chip regridding.

CalibrationFiles

Direct paths to instrument calibration inputs.

GeolocationSetup

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

Sweep

A parameter-variation experiment run against a GeolocationSetup.

OutputConfig

Output settings for a correction run.

CorrectionInput

A single input set for the correction loop.

Functions

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.

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

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

Module Contents

curryer.correction.config.logger
class curryer.correction.config.KernelContext

Bases: NamedTuple

Context for SPICE kernel loading during geolocation.

mkrn: curryer.meta.MetaKernel
dynamic_kernels: list[pathlib.Path]
param_kernels: list[pathlib.Path]
class curryer.correction.config.CalibrationData

Bases: NamedTuple

Pre-loaded calibration data for image matching.

los_vectors: numpy.ndarray | None
optical_psfs: list | None
class curryer.correction.config.ImageMatchingContext

Bases: NamedTuple

Context data needed for image matching operations.

gcp_pairs: list[tuple]
params: list[tuple]
pair_idx: int
sci_key: str
class curryer.correction.config.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.config.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.config.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.config.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.config.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
class curryer.correction.config.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.config.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.config.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
class curryer.correction.config.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.config.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.config.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.config.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.config.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
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)
class curryer.correction.config.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.config.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
curryer.correction.config.load_setup_from_json(config_path: pathlib.Path) GeolocationSetup

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

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

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

curryer.correction.config.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].