Skip to content

canvodpy API reference

Umbrella package of canVODpy: one supported surface, and the command-line tool on top of it. See API levels.

Stateful orchestrator with factory integration and structured logging. What the command-line tool builds internally.

User-friendly wrapper around GnssResearchSite.

Provides a clean, modern API for site management while using the proven GnssResearchSite implementation internally.

Parameters:

Name Type Description Default
name str

Site name from configuration (e.g., "ExampleSite")

required

Attributes:

Name Type Description
name str

Site name

receivers dict

All configured receivers

active_receivers dict

Only active receivers

vod_analyses dict

Configured VOD analysis pairs

gnss_store MyIcechunkStore

Access to RINEX data store

vod_store MyIcechunkStore

Access to VOD results store

Examples:

>>> site = Site("ExampleSite")
>>> print(site.receivers)
{'canopy_01': {...}, 'reference_01': {...}}
>>> # Create pipeline
>>> pipeline = site.pipeline()
>>> # Access stores
>>> site.gnss_store.list_groups()

receivers property

Get all configured receivers.

active_receivers property

Get only active receivers.

vod_analyses property

Get configured VOD analysis pairs.

vod property

Lazy VOD computation helper.

gnss_store property

Access RINEX data store.

vod_store property

Access VOD results store.

gnss_store_path property

Where the site's GNSS store is, from the settings; read-only.

A local directory, or the s3:// URL of a store on S3.

vod_store_path property

Where the site's VOD store is, from the settings; read-only.

A local directory, or the s3:// URL of a store on S3.

pipeline(keep_vars=None, aux_agency=None, n_workers=None, dry_run=False, max_memory_gb=None, cpu_affinity=None, nice_priority=None, threads_per_worker=None, on_group_written=None, label=None, on_discovery_start=None, on_discovery_end=None, on_incomplete_day=None, on_days_without_vod=None)

Create a processing pipeline for this site.

All parameters default to None, which means "read from config". Explicit values override the config.

Reader format is configured per-receiver via reader_format in the sites: section of config/canvod-settings.yaml (default: "auto").

Parameters:

Name Type Description Default
keep_vars list[str]

RINEX variables to keep. Default: from config.

None
aux_agency str

Analysis center for auxiliary data (COD, ESA, GFZ, JPL). Default: from config.

None
n_workers int

Number of parallel workers. Default: from config.

None
dry_run bool

If True, simulate processing without execution.

False
max_memory_gb float

Memory budget of the run in GB: the workers and the days per unit are sized to fit it. Default: from config; without a value there, the memory available when the run starts.

None
threads_per_worker int

Threads per worker process. Default: from config.

None
on_group_written Callable[[str], None]

Called with the receiver-group name each time a group's data for one day finishes writing to Icechunk. Lets a caller drive its own progress display (e.g. the CLI's per-site/receiver rows) without this class owning any display itself.

None
label str

Free-text label of the run, stored in the run history (for example to group the repetitions of a benchmark).

None
on_discovery_start Callable[[str, Path], None]

Called with the receiver name and its data folder when the run starts finding that receiver's files.

None
on_discovery_end Callable[[str, int, int, float], None]

Called with the receiver name, the number of files found, the number of days and the elapsed seconds when the run has found them.

None
on_incomplete_day Callable[[str, str, int, int], None]

Called with (receiver, YYYYDOY, files_found, files_expected) for each receiver-day to process that has fewer files than its file length gives for a whole day. Today is not checked.

None
on_days_without_vod Callable[[str, list[str]], None]

Called with a receiver and the days in the range on which it has no files while its pair has: those days are not processed and get no VOD.

None

Returns:

Type Description
Pipeline

Configured pipeline for this site

Examples:

>>> site = Site("ExampleSite")
>>> pipeline = site.pipeline(n_workers=8)
>>> data = pipeline.process_date("2025001")

User-friendly wrapper around PipelineOrchestrator.

Provides a clean API for processing workflows while using proven orchestrator logic internally.

All parameters default to None, which means "read from the processing: section of config/canvod-settings.yaml". Explicit values override the config.

Reader format is configured per-receiver via reader_format in the sites: section (default: "auto").

Parameters:

Name Type Description Default
site Site or str

Site object or site name

required
keep_vars list[str]

RINEX variables to keep. Default: from config.

None
aux_agency str

Analysis center for auxiliary data. Default: from config.

None
n_workers int

Number of parallel loky worker processes. Default: from config (processing.n_max_threads).

None
dry_run bool

If True, simulate without execution.

False
max_memory_gb float

Memory budget of the run in GB: the workers and the days per unit are sized to fit it (ADR 0021). Default: from config; without a value there, the memory available when the run starts.

None
threads_per_worker int

Threads per loky worker process. Default: from config.

None
on_group_written Callable[[str], None]

Called with the receiver-group name each time a group's data for one day finishes writing to Icechunk. Lets a caller drive its own progress display without this class owning any display itself.

None
label str

Free-text label of the run, stored in the run history.

None
on_discovery_start Callable[[str, Path], None]

Called with the receiver name and its data folder when the run starts finding that receiver's files.

None
on_discovery_end Callable[[str, int, int, float], None]

Called with the receiver name, the number of files found, the number of days and the elapsed seconds when the run has found them.

None
on_incomplete_day Callable[[str, str, int, int], None]

Called with (receiver, YYYYDOY, files_found, files_expected) for each receiver-day to process that has fewer files than its file length gives for a whole day. Today is not checked.

None
on_days_without_vod Callable[[str, list[str]], None]

Called with a receiver and the days in the range on which it has no files while its pair has: those days are not processed and get no VOD.

None
A
required
history
required
with
required

Examples:

>>> # From site object
>>> site = Site("ExampleSite")
>>> pipeline = Pipeline(site)
>>> # Or directly from name
>>> pipeline = Pipeline("ExampleSite")
>>> # Process single date
>>> data = pipeline.process_date("2025001")
>>> # Process range
>>> for date, datasets in pipeline.process_range("2025001", "2025007"):
...     print(f"Processed {date}")

failed_receiver_days property

Receiver-days of this run that failed.

(receiver, YYYYDOY) pairs, empty while every receiver-day is written or skipped. canvodpy run exits with code 3 when a run has any; the log says why each one failed.

close()

End the run and release orchestrator resources.

loky's reusable executor is shared and left running.

process_date(date)

Process RINEX data for one date.

Parameters:

Name Type Description Default
date str

Date in YYYYDOY format (e.g., "2025001" for Jan 1, 2025)

required

Returns:

Type Description
dict[str, Dataset]

Processed datasets for each receiver

Examples:

>>> pipeline = Pipeline("ExampleSite")
>>> data = pipeline.process_date("2025001")
>>> print(data.keys())
dict_keys(['canopy_01', 'canopy_02', 'reference_01'])
>>> # Access individual receiver
>>> canopy_data = data['canopy_01']
>>> print(canopy_data.dims)
Dimensions: (epoch: 2880, sv: 32, ...)

process_range(start, end)

Process RINEX data for a date range.

Parameters:

Name Type Description Default
start str

Start date (YYYYDOY)

required
end str

End date (YYYYDOY)

required

Yields:

Type Description
tuple[str, dict[str, Dataset]]

(date_key, datasets) for each processed date

Examples:

>>> pipeline = Pipeline("ExampleSite")
>>> for date, datasets in pipeline.process_range("2025001", "2025007"):
...     print(f"Processed {date}: {len(datasets)} receivers")
Processed 2025001: 3 receivers
Processed 2025002: 3 receivers
...

run(start=None, end=None, *, update_to_latest=False, vod_analyses=None, calculator='tau_omega')

Store the days from start to end, then compute their VOD.

The run of canvodpy run (ADR 0021). The GNSS phase stores the days in units of days and reads nothing back; after each unit, the VOD phase computes the VOD of its days in the same worker pool, from the GNSS store, and commits once (VodComputer.compute_days). Meanwhile, the GNSS phase works on the next unit. A day that failed gets no VOD and stays open for the next run.

Parameters:

Name Type Description Default
start str

First and last day (YYYYDOY). start is needed unless update_to_latest; without end the run goes to the last day with files.

None
end str

First and last day (YYYYDOY). start is needed unless update_to_latest; without end the run goes to the last day with files.

None
update_to_latest bool

Process the days from the last stored day on instead of start/end (:meth:update_range); with nothing new, the run yields nothing.

False
vod_analyses sequence of str

The VOD analyses to compute; all the site's by default. An empty sequence computes no VOD.

None
calculator str

The registered VOD calculator.

"tau_omega"

Yields:

Type Description
RunUnit

Per unit, once its VOD is committed: the days stored and what the VOD phase did. A failing VOD phase is logged and reported in RunUnit.vod_error; the run goes on.

Raises:

Type Description
ValueError

If neither start nor update_to_latest is given, or update_to_latest comes with start or end.

NothingStoredError

With update_to_latest, if nothing is stored for the site yet.

Examples:

>>> with Site("ExampleSite").pipeline() as pipeline:
...     for unit in pipeline.run("2025001", "2025007"):
...         print(sorted(unit.days), unit.vod.written)

preview(start=None, end=None, *, update_to_latest=False)

Preview processing plan without execution.

Parameters:

Name Type Description Default
start str

YYYYDOY string to start from (inclusive). None previews every available date.

None
end str

YYYYDOY string to end at (inclusive). None previews every available date.

None
update_to_latest bool

Preview the days a run with update_to_latest processes (:meth:update_range); with nothing new, the plan has no dates.

False

Returns:

Type Description
dict

Summary of dates, receivers, and files

Raises:

Type Description
ValueError

If update_to_latest comes with start or end.

NothingStoredError

With update_to_latest, if nothing is stored for the site yet.

Examples:

>>> pipeline = Pipeline("ExampleSite")
>>> plan = pipeline.preview(start="2025001", end="2025003")
>>> print(f"Total files: {plan['total_files']}")

update_range()

The days a run with update_to_latest processes.

It starts on the earliest last stored day in the site's GNSS store, counting only the receivers with new files: after their own last stored day, or on it but not in the store's log book. A day stored while its files were still arriving thus gets the rest, and a retired receiver doesn't hold the start back. It ends on the latest day with files.

Returns:

Type Description
tuple[str, str] | None

The first and last day (YYYYDOY); None if no receiver has new files.

Raises:

Type Description
NothingStoredError

If nothing is stored for the site yet.

Examples:

>>> with Site("ExampleSite").pipeline(dry_run=True) as pipeline:
...     pipeline.update_range()
('2026293', '2026298')

Factories

Bases: ComponentFactory

Factory for GNSS data readers.

All registered readers must inherit from GNSSDataReader ABC.

Supports two creation modes:

  • By name: ReaderFactory.create("rinex3", fpath=path)
  • By file: ReaderFactory.create_from_file(path) — detects RINEX v2, RINEX v3 and NMEA from the file's content (not SBF).

Examples:

>>> from canvodpy import ReaderFactory
>>> reader = ReaderFactory.create("rinex3", fpath="station.25o")
>>> reader = ReaderFactory.create_from_file("station.25o")

create_from_file(fpath, **kwargs) classmethod

Auto-detect the file format and create the appropriate reader.

Detects NMEA (a .nmea extension or a first line starting with $), and otherwise RINEX v2 or v3 from the version in the first header line; a RINEX v3 file with signal-strength observables only gets the stripped RINEX v3 reader. SBF files are not detected: create their reader by name.

Parameters:

Name Type Description Default
fpath str or Path

Path to the GNSS data file.

required
**kwargs Any

Additional parameters forwarded to the reader constructor.

{}

Returns:

Type Description
GNSSDataReader

Instantiated reader for the detected format.

Raises:

Type Description
FileNotFoundError

If fpath does not exist.

ValueError

If the format cannot be determined or no reader is registered for the detected format.

detect_reader(fpath) classmethod

Name of the registered reader for the file at fpath.

Decided as in :meth:create_from_file: NMEA by its .nmea extension or its sentences, RINEX by the version in the first header line.

Raises:

Type Description
FileNotFoundError

If fpath does not exist.

ValueError

If the format cannot be determined or no reader is registered for the detected format.

Bases: ComponentFactory

Factory for grid builders.

All registered builders must inherit from BaseGridBuilder ABC.

Examples:

>>> from canvod.grids import EqualAreaBuilder
>>> GridFactory.register("equal_area", EqualAreaBuilder)
>>> grid = GridFactory.create(
...     "equal_area",
...     angular_resolution=5.0,
...     cutoff_theta=75.0,
... )

Bases: ComponentFactory

Factory for VOD calculators.

All registered calculators must inherit from VODCalculator ABC.

Examples:

>>> from canvod.vod import TauOmegaZerothOrder
>>> VODFactory.register("tau_omega", TauOmegaZerothOrder)
>>> calc = VODFactory.create(
...     "tau_omega",
...     canopy_ds=canopy,
...     sky_ds=sky,
... )
>>> vod = calc.calculate_vod()

Configuration