2.7. pycsamt.airborne#

Format-neutral airborne electromagnetic survey containers: flight lines and datasets built from EMTF documents, a technology/format registry, native-I/O extension points, structural QC, and the AirborneSite/AirborneSites Sites-shaped read path. AFMAG, ZTEM, and MobileMT map decoded scientific arrays onto this common model rather than each inventing its own containers.

See also

Airborne EM Guide for narrative, runnable examples built page by page (data model, site view, technology/ format registry, and structural QC).

Format-neutral airborne electromagnetic survey containers.

The package provides common survey/navigation objects, a technology registry, native-I/O extension points, and structural QC. Technology subpackages map decoded scientific arrays into this model; native vendor readers remain real-data driven and are not guessed from literature descriptions.

class pycsamt.airborne.NavigationTrack(sample_ids, latitude=None, longitude=None, easting=None, northing=None, terrain_elevation=None, platform_elevation=None, clearance=None, heading=None, pitch=None, roll=None, timestamps=None, crs=None, datum='WGS84', attrs=<factory>)#

Bases: CoreObject

Sample-aligned navigation and attitude information for one line.

The class intentionally contains only concepts that are broadly common across airborne EM systems. Technology-specific fields should be kept in attrs until genuine delivery data justify a stable public field.

Parameters:
  • sample_ids (sequence of str-like) – Ordered identifiers defining the common sample axis.

  • latitude (array-like, optional) – Geographic coordinates in decimal degrees. They must be supplied together when used. NaN values are allowed for individual missing samples, while finite values are range-checked.

  • longitude (array-like, optional) – Geographic coordinates in decimal degrees. They must be supplied together when used. NaN values are allowed for individual missing samples, while finite values are range-checked.

  • easting (array-like, optional) – Projected coordinates. They must be supplied together when used.

  • northing (array-like, optional) – Projected coordinates. They must be supplied together when used.

  • terrain_elevation (array-like, optional) – Elevations on a common vertical datum, normally metres.

  • platform_elevation (array-like, optional) – Elevations on a common vertical datum, normally metres.

  • clearance (array-like, optional) – Explicit receiver/platform clearance above ground. If absent, clearance_values derives it from platform minus terrain where both elevations are available.

  • heading (array-like, optional) – Attitude channels in source-system angular convention. They are retained without imposing a proprietary sign convention.

  • pitch (array-like, optional) – Attitude channels in source-system angular convention. They are retained without imposing a proprietary sign convention.

  • roll (array-like, optional) – Attitude channels in source-system angular convention. They are retained without imposing a proprietary sign convention.

  • timestamps (sequence, optional) – Sample-aligned acquisition times. Values are preserved as supplied.

  • crs (str, optional) – Coordinate reference system label for projected coordinates.

  • datum (str, default "WGS84") – Geographic datum label.

  • attrs (dict) – Extension point for system-specific navigation metadata.

sample_ids: Any#
latitude: Any | None = None#
longitude: Any | None = None#
easting: Any | None = None#
northing: Any | None = None#
terrain_elevation: Any | None = None#
platform_elevation: Any | None = None#
clearance: Any | None = None#
heading: Any | None = None#
pitch: Any | None = None#
roll: Any | None = None#
timestamps: Any | None = None#
crs: str | None = None#
datum: str | None = 'WGS84'#
attrs: dict[str, Any]#
validate()#

Validate object state.

Subclasses can override this hook. The base implementation intentionally accepts all states.

Return type:

None

property n_samples: int#

Number of navigation samples.

property has_geographic_coordinates: bool#

Whether latitude/longitude arrays are present.

property has_projected_coordinates: bool#

Whether easting/northing arrays are present.

property clearance_values: ndarray | None#

Return explicit or safely derived clearance values.

Explicit clearance always takes precedence. When it is unavailable, the difference platform_elevation - terrain_elevation is returned without changing the stored metadata.

property bbox: BBox | None#

Tight geographic bounding box over finite coordinates.

index_of(sample_id)#

Return the navigation index for sample_id.

Parameters:

sample_id (str)

Return type:

int

class pycsamt.airborne.AirborneEMRecord(sample_id, emtf=None, fields=<factory>, quality=<factory>, attrs=<factory>)#

Bases: CoreObject

One sample-aligned airborne EM scientific record.

Parameters:
  • sample_id (str) – Identifier matching one entry of the owning line’s navigation.sample_ids. Stripped and required to be non-empty.

  • emtf (EMTF, optional) – Transfer-function payload for this sample. EMTF is reused directly rather than introduced as a parallel matrix class, so MobileMT, ZTEM, AFMAG, and future passive systems share one scientific representation instead of duplicating it.

  • fields (dict, optional) – Auxiliary decoded scalar/array fields with no stronger scientific type yet, for example a processed apparent conductivity vector. Content is technology-defined.

  • quality (dict, optional) – Sample-level quality flags or scores. Content is technology-defined.

  • attrs (dict, optional) – Free-form extension metadata.

Raises:
  • ValueError – If sample_id is empty after stripping.

  • TypeError – If emtf is supplied and is not an EMTF instance.

Examples

>>> from pycsamt.airborne import AirborneEMRecord
>>> record = AirborneEMRecord(sample_id=" S001 ")
>>> record.sample_id
'S001'
>>> record.transfer_function_names
()
sample_id: str#
emtf: EMTF | None = None#
fields: dict[str, Any]#
quality: dict[str, Any]#
attrs: dict[str, Any]#
validate()#

Normalize identifier/dict fields and check the EMTF type.

Return type:

None

property transfer_function_names: tuple[str, ...]#

Transfer-function names available for this sample.

class pycsamt.airborne.AirborneEMLine(line_id, navigation, records=<factory>, attrs=<factory>)#

Bases: CoreObject

One airborne flight line with navigation and sparse EM records.

Parameters:
  • line_id (str) – Flight-line identifier. Stripped and required to be non-empty.

  • navigation (NavigationTrack) – Sample-aligned navigation/attitude track defining the line’s common sample axis. Every record’s sample_id must appear in navigation.sample_ids.

  • records (dict of str to AirborneEMRecord, optional) – Records keyed by sample_id. The mapping key must equal record.sample_id for every entry.

  • attrs (dict, optional) – Free-form extension metadata.

Raises:

Notes

Records are keyed by navigation sample_id and may be sparse. This is deliberate: a missing or rejected EM sample should not require deleting the corresponding navigation point, nor fabricating a transfer function to fill the gap.

Examples

>>> from pycsamt.airborne import AirborneEMLine, NavigationTrack
>>> nav = NavigationTrack(sample_ids=("S1", "S2"))
>>> line = AirborneEMLine(line_id="L001", navigation=nav)
>>> line.n_samples, line.n_records
(2, 0)
>>> line.missing_sample_ids
('S1', 'S2')
line_id: str#
navigation: NavigationTrack#
records: dict[str, AirborneEMRecord]#
attrs: dict[str, Any]#
validate()#

Normalize the identifier and re-attach incoming records.

Return type:

None

property n_samples: int#

Number of navigation samples on the line.

property n_records: int#

Number of EM records currently attached to the line.

property bbox: BBox | None#

Geographic bounding box when navigation coordinates exist.

property missing_sample_ids: tuple[str, ...]#

Navigation samples that currently have no EM record.

property transfer_function_names: tuple[str, ...]#

Sorted union of transfer-function names present on this line.

add_record(record, *, replace=False)#

Attach one record after verifying navigation alignment.

Parameters:
  • record (AirborneEMRecord) – Record whose sample_id must already exist on navigation.

  • replace (bool, default False) – Whether to overwrite an existing record for the same sample instead of raising.

Returns:

self, to support call chaining.

Return type:

AirborneEMLine

Raises:
add_emtf(sample_id, emtf, *, fields=None, quality=None, attrs=None, replace=False)#

Build and attach one AirborneEMRecord from an EMTF.

Convenience wrapper around add_record() for the common case of attaching a decoded EMTF response without constructing the record explicitly.

Parameters:
Returns:

The record that was attached.

Return type:

AirborneEMRecord

get_record(sample_id)#

Return a record by sample identifier, or None when absent.

Raises:

KeyError – If sample_id is not a known navigation sample.

Parameters:

sample_id (str)

Return type:

AirborneEMRecord | None

record_at(index)#

Return the record aligned with navigation index index.

Parameters:

index (int)

Return type:

AirborneEMRecord | None

iter_records()#

Iterate records in navigation order, skipping missing samples.

Return type:

Iterator[AirborneEMRecord]

class pycsamt.airborne.AirborneEMDataset(name, lines=<factory>, survey=None, instrument=None, method='AEM', attrs=<factory>)#

Bases: CoreObject

Format-neutral collection of airborne EM flight lines.

Parameters:
  • name (str) – Survey/dataset name. Stripped and required to be non-empty.

  • lines (dict of str to AirborneEMLine, optional) – Flight lines keyed by line_id. The mapping key must equal line.line_id for every entry.

  • survey (SurveyMeta, optional) – Survey-level metadata.

  • instrument (InstrumentMeta, optional) – System/instrument metadata.

  • method (str, default "AEM") – Survey method label, upper-cased on construction (for example "AEM").

  • attrs (dict, optional) – Free-form extension metadata.

Raises:
  • ValueError – If name or method is empty, or a line mapping key does not match line.line_id.

  • TypeError – If survey, instrument, or an entry of lines has the wrong type.

Notes

The dataset is intentionally an organisational layer above EMTF. It does not define a MobileMT, ZTEM, or AFMAG file schema, and it inherits CoreObject rather than MTBase: aggregating flight lines is not itself electromagnetic arithmetic, so this class should not carry MTBase’s numeric EM utilities (those belong to the EMTF/ TransferFunction objects it holds). Technology adapters populate this object rather than introducing separate transfer-function mathematics.

Examples

>>> from pycsamt.airborne import AirborneEMDataset
>>> dataset = AirborneEMDataset(name="survey-001")
>>> dataset.method, dataset.n_lines
('AEM', 0)
name: str#
lines: dict[str, AirborneEMLine]#
survey: SurveyMeta | None = None#
instrument: InstrumentMeta | None = None#
method: str = 'AEM'#
attrs: dict[str, Any]#
validate()#

Normalize identifier/method fields and re-attach lines.

Return type:

None

property line_ids: tuple[str, ...]#

Flight-line identifiers in insertion order.

property n_lines: int#

Number of flight lines.

property n_samples: int#

Total number of navigation samples across all lines.

property n_records: int#

Total number of attached EM records across all lines.

property transfer_function_names: tuple[str, ...]#

Sorted union of transfer-function types in the dataset.

property bbox: BBox | None#

Geographic bounding box over all lines with finite coordinates.

add_line(line, *, replace=False)#

Attach one flight line.

Parameters:
  • line (AirborneEMLine) – Flight line to attach.

  • replace (bool, default False) – Whether to overwrite an existing line with the same line_id instead of raising.

Returns:

self, to support call chaining.

Return type:

AirborneEMDataset

Raises:
get_line(line_id)#

Return a line by identifier, or None when absent.

Parameters:

line_id (str)

Return type:

AirborneEMLine | None

iter_lines()#

Iterate flight lines in insertion order.

Return type:

Iterator[AirborneEMLine]

iter_records()#

Iterate (line_id, record) pairs in navigation order.

Return type:

Iterator[tuple[str, AirborneEMRecord]]

emtf_records()#

Return all non-empty EMTF records keyed by line/sample ID.

Returns:

Mapping from (line_id, sample_id) to the attached EMTF. Records with no EMTF payload are omitted rather than represented with a placeholder.

Return type:

dict of (str, str) to EMTF

inspect()#

Return the common airborne inspection summary lazily.

Returns:

Compact scientific inventory; see pycsamt.airborne.qc.inspect_airborne().

Return type:

AirborneInspection

Notes

The import is deferred to avoid a hard import-time dependency between pycsamt.airborne.base and pycsamt.airborne.qc, which itself imports this module.

qc()#

Return the common structural airborne QC report lazily.

Returns:

Structural/metadata completeness report; see pycsamt.airborne.qc.assess_airborne_qc().

Return type:

AirborneQCReport

class pycsamt.airborne.AirborneSite(record, *, line_id=None, technology=None, coords=None)#

Bases: CoreObject

One airborne flight-line sample, read directly from its EMTF.

Unlike Site, every numeric accessor here reads straight from the wrapped AirborneEMRecord’s EMTF document – there is no EDI bridge to materialize and no impedance requirement to satisfy.

Parameters:
  • record (AirborneEMRecord) – The sample this site wraps.

  • line_id (str, optional) – Owning flight-line identifier, for provenance/grouping. None when unknown (e.g. a bare, line-less record).

  • technology (str, optional) – Technology label ("ztem", "mobilemt", "afmag", …), the explicit differentiator against ground MT. Falls back to the record’s EMTF.subtype when not given explicitly; see technology.

  • coords ((float, float, float), optional) – Explicit (lat, lon, elev) override, normally supplied by the constructing classmethod (AirborneSites.from_line()/from_dataset) from the parent line’s NavigationTrack. When omitted, coords falls back to the record’s own EMTF.site.location, then to (nan, nan, nan).

Raises:

TypeError – If record is not an AirborneEMRecord.

See also

AirborneSites

Ordered collection of these.

pycsamt.site.base.Site

The ground-MT, EDI-shaped counterpart.

classmethod from_xml(source, *, line_id=None, technology=None)#

Build one site directly from an EMTF-XML file or document.

Parameters:
Return type:

AirborneSite

Notes

The sample identifier is resolved from document.site.site_id, then document.station, then the source file’s stem, in that order.

property record: AirborneEMRecord#

The wrapped AirborneEMRecord.

property emtf: EMTF | None#

The underlying EMTF.

property tf: EMTF | None#

Alias for emtf, matching Site.tf’s naming.

property sample_id: str#

Navigation sample identifier (stable; independent of any richer name resolved from metadata).

property line_id: str | None#

Owning flight-line identifier, or None if unknown.

property technology: str | None#

Technology label ("ztem", "mobilemt", …).

Explicit at construction, else the record’s EMTF.subtype, else None.

property name: str#

Station identifier resolved from metadata, else sample_id.

Resolution order: EMTF.site.site_id, EMTF.station, sample_id.

property station: str#

Alias for name, matching Site.name’s role.

property coords: tuple[float, float, float]#

(lat, lon, elev) in decimal degrees and metres.

Explicit constructor override first, then the record’s own EMTF.site.location, then (nan, nan, nan) – never a fabricated position.

property freq: ndarray | None#

Frequency vector [Hz], or None if unknown.

property tipper: ndarray | None#

Tipper array, shape (nf, 1, 2), or None if absent.

property z: ndarray | None#

Impedance array, shape (nf, 2, 2), or None if absent (the normal case for ZTEM/AFMAG/MobileMT).

property admittance: ndarray | None#

MobileMT admittance array, shape (nf, 3, 2).

None for any record without an attached mobilemt_admittance transfer function – there is no analogue of this accessor on Site, since a 3x2 admittance cannot be represented by Site.z’s 2x2 shape.

property interstation_tensor: ndarray | None#

Tensor AFMAG/AirMt interstation magnetic TF, shape (nf, 3, 2).

None for any record without an attached interstation_transfer_functions transfer function. Deliberately a separate accessor from admittance (also (nf, 3, 2)-shaped): AirMt’s tensor relates ground-reference magnetic fields to airborne magnetic fields (Hx,Hy -> Hx,Hy,Hz), while MobileMT’s admittance relates ground electric fields to airborne magnetic fields (Ex,Ey -> Hx,Hy,Hz) – physically different responses that happen to share a matrix shape, so this module keeps them under different names rather than one generic “3x2 tensor” accessor that would blur which is which. See pycsamt.airborne.afmag’s module docstring for why the original-comparator generation (afmag_tilt_deg) is kept separate again from this one.

property afmag_tilt_deg: ndarray | None#

Original comparator AFMAG scalar tilt/deflection, shape (nf,).

None for any record without an attached afmag_tilt transfer function. Unlike interstation_tensor or a ground tipper, this is a single real number per frequency – the historical comparator has no polarization-ellipse decomposition available at all, only a line-direction deflection (see pycsamt.airborne.afmag’s module docstring).

property afmag_amplification_parameter: ndarray | None#

AirMt rotation-invariant amplification parameter, shape (nf,).

None for any record without an attached airmt_amplification_parameter transfer function; see pycsamt.airborne.afmag.compute_airmt_amplification_parameter() for the formula that derives it from interstation_tensor.

property quality: dict[str, Any]#

Sample-level quality flags/scores (technology-defined).

property fields: dict[str, Any]#

Auxiliary decoded fields (technology-defined).

For MobileMT, this is where a vendor-delivered native apparent_conductivity vector lives when present; see pycsamt.airborne.mobilemt.MOBILEMT_APPARENT_CONDUCTIVITY_FIELD.

property site_meta: Any#

pycsamt.metadata.SiteMeta (via emtf).

property site_layout: Any#

pycsamt.metadata.SiteLayout channel geometry.

property provenance: Any#

pycsamt.metadata.ProvenanceMeta creator/submitter info.

property processing: Any#

pycsamt.metadata.ProcessingMeta processing/software info.

property copyright: Any#

pycsamt.metadata.CopyrightInfo release/citation info.

property quality_meta: Any#

pycsamt.metadata.TransferFunctionQuality QC rating.

has_component(comp)#

Whether comp exists and has at least one finite value.

Parameters:

comp (str) – "tip"/"tx"/"ty"/"tipper" for the tipper; "admittance"/"y" for the MobileMT admittance; "interstation_tensor"/"ti" for the AirMt tensor; "afmag_tilt"/"tilt" for the original-comparator AFMAG scalar; "amplification_parameter"/"ap" for the AirMt derived parameter; anything else is looked up against z.

Return type:

bool

to_dataframe(kind='tipper')#

Export this site’s data to a tidy pandas.DataFrame.

Parameters:

kind ({"tipper", "admittance", "z"}, default "tipper")

Returns:

Indexed by frequency (name "f"). Columns depend on kind: Tx, Ty; Yxx, Yxy, Yyx, Yyy, Yhzx, Yhzy; or Zxx, Zxy, Zyx, Zyy.

Return type:

pandas.DataFrame

Raises:

ValueError – If kind is not recognized.

summary()#

Summarize identity, geometry, and data coverage.

Returns:

Keys: name, line_id, sample_id, technology, nfreq, lat, lon, elev, tipper, admittance (booleans).

Return type:

dict

to_xml(target=None, **kwargs)#

Serialize this site’s document to EMTF XML.

Parameters:
  • target (str or pathlib.Path, optional) – Destination path. If None, the XML is returned as a string.

  • **kwargs – Forwarded to write_xml()/to_xml.

Return type:

str or Any

Raises:

ValueError – If this site has no attached EMTF document.

class pycsamt.airborne.AirborneSites(items)#

Bases: CoreObject

Ordered collection of AirborneSite objects.

The airborne counterpart of Sites. See the module docstring for why order here is simply preserved navigation order rather than something to infer.

Parameters:

items (AirborneSite, AirborneEMRecord, EMTF, str, Path, or iterable) – A single item, or an iterable of them. Raw AirborneEMRecord/EMTF/path items are coerced via AirborneSite.from_xml()-style construction with no line context; prefer from_line()/from_dataset() when that context is available.

Raises:

TypeError – If an item cannot be coerced to AirborneSite.

See also

ensure_asites

Flexible entry-point coercion, including directories of EMTF-XML files and duplicate-name policy.

pycsamt.site.base.Sites

The ground-MT counterpart.

classmethod from_xml_dir(path, *, recursive=True, pattern='*.xml', line_id=None, strict=False)#

Read every EMTF-XML file under path into one container.

Parameters:
  • path (str or pathlib.Path) – A single EMTF-XML file, or a directory to search.

  • recursive (bool, default True) – Search subdirectories too (Path.rglob) instead of only the top level (Path.glob).

  • pattern (str, default "*.xml") – Glob pattern used when path is a directory.

  • line_id (str, optional) – Forwarded to every AirborneSite.from_xml() call.

  • strict (bool, default False) – If True, a file that fails to parse raises instead of being skipped, and an empty result raises too.

Returns:

Sites in sorted-filename order.

Return type:

AirborneSites

Raises:

ValueError – If strict and nothing could be read.

classmethod from_line(line, *, technology=None)#

Build a container from one already-constructed flight line.

Parameters:
  • line (AirborneEMLine) – Records are visited via iter_records(), i.e. in navigation order, skipping samples with no attached record.

  • technology (str, optional) – Forwarded to every AirborneSite; falls back to line.attrs["technology"] when not given.

Return type:

AirborneSites

Raises:

TypeError – If line is not an AirborneEMLine.

classmethod from_dataset(dataset, *, technology=None)#

Flatten every line of a dataset into one container.

Parameters:
Return type:

AirborneSites

Raises:

TypeError – If dataset is not an AirborneEMDataset.

by_index(i)#

Retrieve by zero-based index.

Parameters:

i (int)

Return type:

AirborneSite

get(name)#

Safe lookup by case-insensitive name; None if absent.

Parameters:

name (str)

Return type:

AirborneSite | None

as_list()#

The underlying list of AirborneSite objects.

Return type:

list[AirborneSite]

to_emtf_list()#

The underlying EMTF documents, in site order.

Return type:

list[Any]

property technologies: tuple[str, ...]#

Sorted, deduplicated AirborneSite.technology values present in this container.

property line_ids: tuple[str, ...]#

Sorted, deduplicated AirborneSite.line_id values present in this container.

select(names=None, predicate=None)#

Filter by explicit names or by a boolean predicate.

Parameters:
  • names (sequence of str, optional) – Case-insensitive names to retain; takes precedence over predicate.

  • predicate (callable, optional) – predicate(site) -> bool.

Returns:

A new container; a shallow copy when neither argument is given.

Return type:

AirborneSites

map(fn)#

Apply fn(site) -> Any to every site; collect results.

Parameters:

fn (Any)

Return type:

list[Any]

closest(lat, lon, tol=None)#

Nearest site to a target coordinate (great-circle distance).

Parameters:
  • lat (float) – Target location in decimal degrees.

  • lon (float) – Target location in decimal degrees.

  • tol (float, optional) – Maximum allowed distance in metres; farther than that returns None.

Returns:

None if every site lacks finite coordinates, or the nearest is farther than tol.

Return type:

AirborneSite or None

write_xml(outdir, **kwargs)#

Write one EMTF-XML file per site into a directory.

Parameters:
Returns:

Paths written, named "{name}.xml".

Return type:

list of pathlib.Path

pycsamt.airborne.ensure_asites(obj, *, recursive=True, on_dup='replace', strict=False, verbose=0)#

Normalize arbitrary airborne input to an AirborneSites.

The single entry-point coercion for airborne-aware emtools functions, mirroring the role ensure_sites() plays for the rest of emtools.

Parameters:
Return type:

AirborneSites

Raises:

ValueError – If obj is None; if on_dup is invalid; or, in strict mode, if nothing could be resolved.

class pycsamt.airborne.AirborneTechnologyDefinition(name, label, family, aliases=<factory>, primary_tf_names=<factory>, reference_required=False, infer_from_tf=False, description='')#

Bases: PyCSAMTObject

Describe one scientific airborne-EM technology contract.

A technology definition is intentionally free of file-format knowledge: it only records how to recognize the technology from an already-built AirborneEMDataset / EMTF object. A concrete native delivery is described separately by AirborneFormatDefinition.

Parameters:
  • name (str) – Canonical technology key, for example "mobilemt". Passed through normalize_key().

  • label (str) – Human-readable display name, for example "MobileMT".

  • family (str) – Broad measurement family shared by related technologies, for example "natural_field_airborne_em". Also normalized.

  • aliases (tuple of str, optional) – Alternate keys accepted for lookup, for example ("mobile_mt",).

  • primary_tf_names (tuple of str, optional) – Transfer-function names that are unique enough to this technology to justify inference; see infer_from_tf.

  • reference_required (bool, default False) – Whether a fixed ground reference station is scientifically required for this technology’s response. Used by assess_airborne_qc() to decide whether a missing reference-station is a QC issue.

  • infer_from_tf (bool, default False) – Whether identify_airborne_technologies() may infer this technology purely from a matching entry of primary_tf_names, when nothing else identifies it. Deliberately False for tipper-only technologies (ZTEM), because standard tipper T is not unique to one technology.

  • description (str, default "") – Short human-readable description.

Raises:

ValueError – If name, label, or family is empty after normalization/stripping.

name: str#
label: str#
family: str#
aliases: tuple[str, ...]#
primary_tf_names: tuple[str, ...]#
reference_required: bool = False#
infer_from_tf: bool = False#
description: str = ''#
class pycsamt.airborne.AirborneFormatDefinition(name, technology, reader=None, writer=None, detector=None, extensions=<factory>, aliases=<factory>, description='')#

Bases: PyCSAMTObject

Describe one concrete native airborne delivery format.

A format definition binds one technology to a concrete on-disk (or stream) representation. It stays empty of reader/writer until a genuine sample or authoritative specification exists to validate against; see the module-level docstring and register_airborne_format().

Parameters:
  • name (str) – Canonical format key, unique across all technologies.

  • technology (str) – Owning technology key; must already be registered via register_airborne_technology() before this format is registered.

  • reader (callable, optional) – reader(source, **kwargs) -> AirborneEMDataset. None means the format is not yet readable.

  • writer (callable, optional) – writer(dataset, target, **kwargs) -> Any. None means the format is not yet writable.

  • detector (callable, optional) – detector(source) -> bool used for content-aware format detection, ahead of extension-based matching.

  • extensions (tuple of str, optional) – File extensions used as a detection hint when no detector matches; normalized to a leading dot and lowercased.

  • aliases (tuple of str, optional) – Alternate keys accepted for lookup.

  • description (str, default "") – Short human-readable description.

Raises:

ValueError – If name or technology is empty after normalization.

name: str#
technology: str#
reader: Callable[[...], Any] | None = None#
writer: Callable[[...], Any] | None = None#
detector: Callable[[Any], bool] | None = None#
extensions: tuple[str, ...]#
aliases: tuple[str, ...]#
description: str = ''#
property readable: bool#

Whether a native reader has been registered.

property writable: bool#

Whether a native writer has been registered.

exception pycsamt.airborne.AirborneRegistryError#

Bases: ValueError

Base exception for airborne technology/format registry errors.

exception pycsamt.airborne.AirborneTechnologyAmbiguityError#

Bases: AirborneRegistryError

Raised when an object contains more than one airborne technology.

exception pycsamt.airborne.AirborneFormatDetectionError#

Bases: AirborneRegistryError

Raised when native airborne format detection is ambiguous.

pycsamt.airborne.register_airborne_technology(definition, *, replace=False)#

Register one scientific technology definition.

Parameters:
  • definition (AirborneTechnologyDefinition) – Technology contract to register.

  • replace (bool, default False) – Whether to overwrite an existing registration under the same canonical name, forwarded to register().

Returns:

The same definition instance, for convenient chaining.

Return type:

AirborneTechnologyDefinition

Raises:
pycsamt.airborne.get_airborne_technology(name)#

Return a technology definition by canonical name or alias.

Returns None rather than raising when name is unregistered, since callers such as _technology_from_text() use this for best-effort inference over untrusted attrs/subtype values.

Parameters:

name (str)

Return type:

AirborneTechnologyDefinition | None

pycsamt.airborne.list_airborne_technologies()#

Return registered technologies in registration order.

Return type:

tuple[AirborneTechnologyDefinition, …]

pycsamt.airborne.identify_airborne_technologies(obj)#

Return canonical technologies explicitly or safely identified.

Parameters:

obj (AirborneEMDataset, AirborneEMLine, AirborneEMRecord, or EMTF) – Object to inspect; see _collect_object_technologies() for exactly what is walked and in what priority.

Returns:

Zero or more canonical technology names, in registration order. Zero means obj carries no explicit or safely inferable technology tag; more than one means obj genuinely mixes technologies (see detect_airborne_technology() to turn that into an error instead).

Return type:

tuple of str

Notes

Only response types that are unique to one technology are inferred from transfer-function names. Standard tipper T and interstation TI are intentionally not enough by themselves to identify ZTEM or AirMt.

pycsamt.airborne.detect_airborne_technology(obj, *, strict=True)#

Return one canonical technology, or report/ignore mixed content.

Parameters:
Returns:

The single identified technology; None if none was identified, or if more than one was identified and strict is False.

Return type:

str or None

Raises:

AirborneTechnologyAmbiguityError – If more than one technology is identified and strict is True.

pycsamt.airborne.register_airborne_format(definition, *, replace=False)#

Register a concrete native airborne file/delivery format.

Parameters:
Returns:

The registered definition. This may be a new instance with technology rewritten to the owning definition’s canonical name when definition was constructed with an alias.

Return type:

AirborneFormatDefinition

Raises:
pycsamt.airborne.get_airborne_format(name)#

Return a native format definition by name or alias, or None.

Parameters:

name (str)

Return type:

AirborneFormatDefinition | None

pycsamt.airborne.list_airborne_formats(*, technology=None)#

Return registered native formats, optionally for one technology.

Parameters:

technology (str, optional) – Canonical name or alias to filter by. None returns every registered format across all technologies.

Returns:

Matching formats in registration order.

Return type:

tuple of AirborneFormatDefinition

Raises:

AirborneRegistryError – If technology is supplied and is not registered.

pycsamt.airborne.detect_airborne_format(source)#

Detect a registered native format using detectors, then extensions.

Parameters:

source (Any) – Candidate to identify, typically a path or an open stream. Passed to each registered detector, and to _extension_of() when no detector matches.

Returns:

The canonical format name, or None if nothing matched.

Return type:

str or None

Raises:

AirborneFormatDetectionError – If more than one registered format matches source, whether by detector or by extension.

Notes

Detectors have priority. Extensions are only hints and are used when they map to exactly one registered format. No built-in vendor formats are registered merely from published system descriptions.

exception pycsamt.airborne.AirborneIOError#

Bases: RuntimeError

Raised when no defensible native airborne I/O path is available.

See the module docstring for why this is a RuntimeError rather than a ValueError.

pycsamt.airborne.read_airborne(source, *, format=None, technology=None, **kwargs)#

Read one verified native airborne delivery into the common dataset.

Parameters:
  • source (Any) – Delivery to read: typically a path, though the concrete type accepted depends on the registered reader. Passing an existing AirborneEMDataset is an intentional no-op when format is not explicitly requested, so pipeline code can call this uniformly whether it already has a dataset or a raw delivery.

  • format (str, optional) – Explicit registered format name or alias. When omitted, content/extension-based detection selects the format.

  • technology (str, optional) – Restrict resolution to one technology’s formats; see _resolved_format().

  • **kwargs – Forwarded to the selected format’s registered reader.

Returns:

The dataset produced by the resolved reader, or source itself when it already was one and format was omitted.

Return type:

AirborneEMDataset

Raises:

AirborneIOError – If no reader can be resolved for source (see _resolved_format()), or if the resolved format has no registered reader, or if that reader does not return an AirborneEMDataset.

pycsamt.airborne.write_airborne(dataset, target, *, format=None, technology=None, **kwargs)#

Write a dataset through a verified native airborne writer.

Parameters:
  • dataset (AirborneEMDataset) – Dataset to serialize.

  • target (Any) – Output destination. When format is omitted, target must be a path/string with an extension that resolves unambiguously via detect_airborne_format().

  • format (str, optional) – Explicit registered format name or alias.

  • technology (str, optional) – Restrict resolution to one technology’s formats; see _resolved_format().

  • **kwargs – Forwarded to the selected format’s registered writer.

Returns:

Whatever the resolved writer returns; not constrained by this dispatcher.

Return type:

Any

Raises:
  • TypeError – If dataset is not an AirborneEMDataset.

  • AirborneIOError – If format is omitted and cannot be inferred from target, if no writer can otherwise be resolved (see _resolved_format()), or if the resolved format has no registered writer.

pycsamt.airborne.available_airborne_readers(*, technology=None)#

Return native formats with a registered reader.

Parameters:

technology (str, optional) – Restrict to one technology’s formats; None lists across all registered technologies.

Returns:

Canonical format names currently readable. Empty until a native reader has been registered for at least one format; see the module docstring.

Return type:

tuple of str

Raises:

AirborneIOError – If technology is given but not registered.

pycsamt.airborne.available_airborne_writers(*, technology=None)#

Return native formats with a registered writer.

Parameters:

technology (str, optional) – Restrict to one technology’s formats; None lists across all registered technologies.

Returns:

Canonical format names currently writable. Empty until a native writer has been registered for at least one format; see the module docstring.

Return type:

tuple of str

Raises:

AirborneIOError – If technology is given but not registered.

class pycsamt.airborne.AirborneQCIssue(code, severity, message, line_id=None, sample_id=None)#

Bases: PyCSAMTObject

One structural/metadata QC finding.

Parameters:
  • code (str) – Short machine-readable finding code, for example "missing_reference_station". Lower-cased on construction.

  • severity ({"info", "warning", "error"}) – Finding severity. "error" reflects an internally inconsistent scientific state (for example a non-positive frequency axis), not merely incomplete or sparse data; see assess_airborne_qc().

  • message (str) – Human-readable description. Stripped on construction.

  • line_id (str, optional) – Flight line the finding applies to, when scoped to one line.

  • sample_id (str, optional) – Sample the finding applies to, when scoped to one record.

Raises:

ValueError – If code is empty, or severity is not one of "info", "warning", "error".

code: str#
severity: str#
message: str#
line_id: str | None = None#
sample_id: str | None = None#
class pycsamt.airborne.AirborneInspection(object_type, technologies=<factory>, n_lines=0, n_samples=0, n_records=0, transfer_function_names=<factory>, bbox=None, attrs=<factory>)#

Bases: CoreObject

Compact scientific inventory of an airborne object.

Returned by inspect_airborne() for a dataset, line, record, or bare EMTF; the fields below are filled in as far as they are meaningful for that object_type (for example a single record leaves n_lines/bbox at their defaults).

Parameters:
  • object_type ({"dataset", "line", "record", "emtf"}) – Kind of object the inventory describes.

  • technologies (tuple of str, optional) – Canonical technologies identified on the object; see identify_airborne_technologies().

  • n_lines (int, default 0) – Counts meaningful at and above the object’s own level.

  • n_samples (int, default 0) – Counts meaningful at and above the object’s own level.

  • n_records (int, default 0) – Counts meaningful at and above the object’s own level.

  • transfer_function_names (tuple of str, optional) – Sorted union of transfer-function names present.

  • bbox (BBox, optional) – Geographic bounding box, when applicable and available.

  • attrs (dict, optional) – Object-type-specific extra fields (for example sample_id for a record, or name/method for a dataset).

object_type: str#
technologies: tuple[str, ...]#
n_lines: int = 0#
n_samples: int = 0#
n_records: int = 0#
transfer_function_names: tuple[str, ...]#
bbox: Any | None = None#
attrs: dict[str, Any]#
class pycsamt.airborne.AirborneQCReport(technologies, metrics, line_metrics, issues=<factory>)#

Bases: CoreObject

Common structural QC report for an airborne dataset.

Returned by assess_airborne_qc().

Parameters:
  • technologies (tuple of str) – Canonical technologies identified across the dataset.

  • metrics (dict) – Dataset-level scalar metrics (coverage fractions, counts); see assess_airborne_qc() for the exact keys.

  • line_metrics (dict of str to dict) – Per-line metrics keyed by line_id; see _line_metrics() for the exact keys.

  • issues (tuple of AirborneQCIssue, optional) – Individual findings, most to least specific in scope (per-sample, then per-line, then dataset-wide) in the order they were raised.

technologies: tuple[str, ...]#
metrics: dict[str, Any]#
line_metrics: dict[str, dict[str, Any]]#
issues: tuple[AirborneQCIssue, ...]#
property status: str#

Return "error", "warning", or "pass".

The worst severity present in issues, or "pass" when there are none.

property errors: tuple[AirborneQCIssue, ...]#

Return only the "error"-severity issues.

property warnings: tuple[AirborneQCIssue, ...]#

Return only the "warning"-severity issues.

pycsamt.airborne.inspect_airborne(obj)#

Return a compact inventory for dataset, line, record, or EMTF object.

Parameters:

obj (AirborneEMDataset, AirborneEMLine, AirborneEMRecord, or EMTF) – Object to summarize.

Returns:

Inventory populated as far as meaningful for obj’s type.

Return type:

AirborneInspection

Raises:

TypeError – If obj is none of the supported types.

pycsamt.airborne.assess_airborne_qc(dataset)#

Assess common structural completeness and metadata consistency.

Parameters:

dataset (AirborneEMDataset) – Dataset to assess.

Returns:

Dataset-level and per-line metrics plus individual findings. See AirborneQCReport.metrics for the exact dataset-level keys this function populates (record/EMTF coverage fractions, valid-frequency and finite-response fractions, primary/derived transfer-function counts, variance/covariance coverage fractions, and reference-metadata coverage).

Return type:

AirborneQCReport

Raises:

TypeError – If dataset is not an AirborneEMDataset.

Notes

The report is intentionally descriptive. A missing record, missing covariance, or absent coordinates can be important without being a universal processing failure, so only internally inconsistent scientific states – currently just a non-positive or non-finite frequency axis on an attached EMTF – are classified as "error". Everything else that is merely incomplete or sparse is reported at "info"/"warning" severity; see AirborneQCIssue.

2.7.1. Core Containers#

pycsamt.airborne.base

Format-neutral scientific containers for airborne EM surveys.

pycsamt.airborne.navigation

Navigation and attitude containers for airborne EM surveys.

2.7.2. Site View#

pycsamt.airborne.site

Airborne station containers: the non-EDI counterpart of pycsamt.site.

2.7.3. Technology and Format Registry#

pycsamt.airborne.registry

Technology and native-format registries for airborne EM data.

pycsamt.airborne.io

Common native-I/O dispatcher for airborne EM deliveries.

2.7.4. Quality Control#

pycsamt.airborne.qc

Shared structural QC and inspection for airborne EM datasets.

2.7.5. Shared Validation Helpers#

pycsamt.airborne.validation

Shared parameter validation and normalization for pycsamt.airborne.

2.7.6. AFMAG Adapter#

pycsamt.airborne.afmag

AFMAG-family scientific adapter contracts.

pycsamt.airborne.afmag.adapter

Scientific adapter contracts for historical and tensor AFMAG products.

pycsamt.airborne.afmag.base

AFMAG-family metadata built on the common airborne model.

pycsamt.airborne.afmag.datatypes

AFMAG-family derived EMTF datatype registration.

pycsamt.airborne.afmag.constants

Stable scientific constants for AFMAG-family adapter contracts.

2.7.7. ZTEM Adapter#

pycsamt.airborne.ztem

ZTEM scientific adapter contract.

pycsamt.airborne.ztem.adapter

Array-to-scientific-object adapter contract for ZTEM products.

pycsamt.airborne.ztem.base

ZTEM-specific metadata that reuses the common airborne/EMTF model.

pycsamt.airborne.ztem.constants

Stable scientific constants for the ZTEM adapter layer.

2.7.8. MobileMT Adapter#

pycsamt.airborne.mobilemt

MobileMT scientific adapter contract.

pycsamt.airborne.mobilemt.adapter

Array-to-scientific-object adapter contract for MobileMT products.

pycsamt.airborne.mobilemt.base

MobileMT-specific metadata that does not duplicate EMTF mathematics.

pycsamt.airborne.mobilemt.datatypes

MobileMT transfer-function datatype registration.

pycsamt.airborne.mobilemt.constants

Stable scientific constants for the MobileMT adapter layer.