Bridging IoT Acquisition and the Data Model#

The IoT layer processes raw arrays and telemetry with numpy only and stays independent of the heavier science API, which keeps import pycsamt.iot cheap on a field gateway. The pycsamt.iot.bridge module is the one place the two worlds meet. Every science-API import (seg, site) is lazy, so pulling in the bridge does not force the geospatial stack on a node that only needs telemetry.

The bridge works in both directions.

Forward: acquisition to the data model#

The edge already forms per-window impedance estimates (see assess_impedance_stability()). impedance_to_z() turns them into a pycsamt.z.z.Z – the impedance-tensor container the processing and inversion flow consumes – deriving an absolute error from the spread across windows:

import numpy as np
from pycsamt.iot import impedance_to_z, z_to_edi

freq = np.logspace(4, 0, 12)
z_windows = ...            # shape (n_windows, n_freq) complex, from the edge
z = impedance_to_z(z_windows, freq, station="S01", method="amt")

z_to_edi() (and the in-memory build_edifile()) writes a preliminary EDI from that tensor, so a field node can emit an .edi on site:

path = z_to_edi(z, station="S01", lat=6.5, lon=3.4, elevation=120.0,
                savepath="edi_out", method="amt")

For a whole session, to_edifiles() builds one EDI per station, enriched with the station geometry the session recorded, and to_sites_collection() returns a pycsamt.site.base.Sites collection ready to feed straight to pycsamt.pipeline.Pipeline.run():

sites = session.to_sites_collection({"S01": z1, "S02": z2})
# sites can now be handed to pycsamt.pipeline.Pipeline.run(sites)

When the optional geospatial stack is available, z_to_site() returns a single EDI-backed pycsamt.site.base.Site.

Consistent QC via emtools#

Field-side edge QC and downstream processing QC should not be two independent notions of a “good” station. emtools_qc() routes the sites the IoT layer produced through the same pycsamt.emtools coherence/skew/SNR quality control the processing flow uses:

from pycsamt.iot import emtools_qc

table = emtools_qc(session, {"S01": z1, "S02": z2})       # full QC table
flags = emtools_qc(session, {"S01": z1}, flags=True)      # pass/flag verdicts

It also accepts an already-built Sites collection or any EDI source, so the same QC can be re-run on an archived survey.

Note

For a raw time series rather than pre-computed impedance, use pycsamt.ts.ts_to_z() / pycsamt.ts.ts_to_edi(), which run the spectral estimation for you. The bridge starts one step later, from the impedance the edge has already estimated.

Reverse: seeding a re-occupation from an EDI survey#

The bridge also reads an existing survey to plan a follow-up campaign. field_session_from_edis() returns a FieldSession with every station’s recorded geometry and channels, plus a sensor node per station, ready to re-occupy:

from pycsamt.iot import field_session_from_edis, edi_survey_table

session = field_session_from_edis("survey_2024/", survey_id="REOCCUPY",
                                  method="amt")
session.n_stations, session.n_devices

edi_survey_table("survey_2024/")   # station geometry + frequency coverage

deployment_from_edis() is a lighter counterpart that returns just a DeploymentConfig inventory, and read_edi_survey() yields the raw per-station summary records.

Sources may be a directory of .edi files, a glob pattern, a single file or EDIFile, or any iterable mixing these.