Instruments#
UATAQ instruments as classes.
Each instrument class is a subclass of the Instrument abstract base class and implements methods for reading and parsing data files.
The Instrument class provides a common interface for all instrument classes and defines abstract methods that must be implemented by each subclass.
- uataq.instruments.GroupSelection = str | collections.abc.Mapping[str, str] | None#
a name, a per-instrument mapping, or None for automatic selection. See
Instrument.resolve_group().- Type:
How a caller picks a research group
- uataq.instruments.ReadPlan#
which group to read each portion of a time range from. See
Instrument.plan_reads().- Type:
A read plan
- class uataq.instruments.Instrument(SID, name, loggers, config)[source]#
Bases:
objectAbstract base class for instrument objects.
- group_dates#
When each group’s archive holds this instrument’s data, for the groups whose archive covers less than the whole installation (config
group_dates). A group not listed covers the whole installation.
- read_data(group: str, lvl: str, time_range: TimeRange, num_processes: int, file_pattern: str) pd.DataFrame[source]#
Read and parse group data files for the given level and time range using multiple processes.
- resolve_group(group=None)[source]#
Pick which research group’s data to read this instrument from.
- Parameters:
group (str | Mapping[str, str] | None) – A group name, used as given; a mapping of instrument name to group name, from which this instrument’s entry is used (instruments the mapping does not name fall back to automatic selection); or None to select automatically.
- Returns:
The group name.
- Return type:
- Raises:
InvalidGroupError – If no registered groupspace operates this instrument.
Notes
Automatic selection reads the configured operators of this instrument: the default group when it is one of them, otherwise the sole operator, otherwise the first configured. It is a configuration lookup, not a search of the archive – it does not check whether that group actually holds data for a given time range, and it ignores
group_dates.plan_reads()is the time-aware version thatuataq.sites.Site.read_data()uses.
- plan_reads(group=None, time_range=None)[source]#
Plan which group to read each part of a time range from.
- Parameters:
group (str | Mapping[str, str] | None) – As for
resolve_group(). A name, or a mapping entry naming this instrument, reads the whole range from that group.time_range (TimeRange | TimeRangeTypes) – The requested time range. Default None is the whole installation.
- Returns:
(group, portion)pairs in time order. The portions are half-open, do not overlap, and lie within the requested range clipped toactive_range. Usually a single pair.- Return type:
- Raises:
InactiveInstrumentError – If the requested range misses the installation entirely.
InvalidGroupError – If no registered groupspace operates this instrument.
ReaderError – If no group’s archive covers any of the requested range.
Notes
With no group named, a range that crosses a
group_datesboundary is split there. Each portion goes to the most preferred group whose window covers it – the default group, then the configured order, as inresolve_group()– and portions no group covers are skipped. An instrument withoutgroup_datesgets[(resolve_group(group), clipped range)], as before.
- get_datafiles(group, lvl, time_range, pattern=None)[source]#
Get data files for the given level and time range from the groupspace.
- Parameters:
- Returns:
A list of data files.
- Return type:
- property active_range: TimeRange#
When this instrument was installed at the site and when it was removed.
An instrument still installed has no stop. Built from the site configuration’s
installation_date/removal_date.
- clip_to_active(time_range)[source]#
Narrow a requested time range to when this instrument was installed.
- Parameters:
time_range (TimeRange | TimeRangeTypes) – The requested time range.
- Returns:
The requested range intersected with
active_range. Never wider than what was asked for.- Return type:
TimeRange
- Raises:
InactiveInstrumentError – If the requested range does not overlap the active range at all. Both are half-open, so a range that only touches it – stopping at the installation date, or starting at the removal date – does not overlap it.
Notes
Without this, a request that reaches past a swap reads the replacement instrument’s files as though they were this one’s: research groups reuse a file name across an instrument change (the horel group calls both MetOne models
esampler), so the file name cannot distinguish them, but the installation and removal dates can.
- standardize_data(group, data)[source]#
Standardize the data across research groups.
Rename columns, convert units, map values, etc. as needed.
- Parameters:
group (str) – The research group whose data to standardize.
data (pandas.DataFrame) – The data to standardize.
- Returns:
The standardized data.
- Return type:
- read_data(group, lvl=None, time_range=None, num_processes=1, file_pattern=None)[source]#
Read and parse data files for the given level and time range.
Uses multiple processes if specified.
- Parameters:
group (str) – The research group whose data to read.
lvl (str) – The level of the data to read.
time_range (TimeRange | TimeRangeTypes) – The time range to read data. Default is None which reads all available data.
num_processes (int | 'max') – The number of processes to use for parallelization.
file_pattern (str) – A string pattern to filter the file paths.
- Returns:
A concatenated DataFrame containing the parsed data from files.
- Return type:
- uataq.instruments.configure_instrument(SID, name, config, loggers=None)[source]#
Configure an instrument object based on the given configuration settings.
- Parameters:
- Returns:
An instrument object configured with the given settings.
- Return type:
- Raises:
ValueError – If the instrument model is not found in the catalog.
ValueError – If no loggers are found for the instrument at the site.
- class uataq.instruments.InstrumentEnsemble(SID, configs, loggers=None)[source]#
Bases:
objectContainer for an ensemble of instruments at a site.
- class uataq.instruments.SensorMixin[source]#
Bases:
objectMixin for instrument objects that measure a pollutant.
- pollutants(tuple)#
- Type:
Tuple of pollutants measured by the instrument.
- class uataq.instruments.BB_205(SID, name, loggers, config)[source]#
Bases:
Instrument,SensorMixin2B Technologies Model 205 ozone monitor (UV absorption).
- class uataq.instruments.BB_405(SID, name, loggers, config)[source]#
Bases:
Instrument,SensorMixin2B Technologies Model 405 nm NO/NO2/NOx monitor.
- class uataq.instruments.CR1000(SID, name, loggers, config)[source]#
Bases:
InstrumentCampbell Scientific CR1000 datalogger.
Not a sensor itself: it records housekeeping such as battery voltage and enclosure temperature.
- class uataq.instruments.GPS(SID, name, loggers, config)[source]#
Bases:
InstrumentGPS receiver providing position, and speed and course where logged.
Recorded speed is given as
Speed_m_s(lin’s NMEA knots are converted; horel logs m/s). Receivers logging onlyGPGGAsentences record neither speed nor course; both are then estimated from the positions (uataq.gps.estimate_speed_course()) and flagged inSpeed_Estimated/Course_Estimated.- estimate_window: int = 1#
Samples on each side of the centered difference used to estimate speed and course from positions. See
uataq.gps.estimate_speed_course().
- estimate_max_gap: str = '60s'#
Longest interval between consecutive fixes that an estimate may span.
- read_data(group, lvl=None, time_range=None, num_processes=1, file_pattern=None, estimate_motion=True)[source]#
Read GPS data, with speed in m/s and course in degrees.
Extends
Instrument.read_data(). Recorded speed isSpeed_m_s(lin’s files hold NMEA knots, converted here; horel’s are already m/s), and course isCourse_deg.horel data is indexed by the CR1000 logger’s clock, which runs ahead of GPS time by a drifting 1-20 s. Where the receiver’s time of day was logged (
Instrument_Time, horel’s rawGTIM), the true time of each fix is added asGPS_Time_UTC. lin’sTime_UTCis already GPS time.Receivers logging only
GPGGAsentences record neither, in which case both are estimated from the positions (seeuataq.gps.estimate_speed_course()) and the boolean columnsSpeed_Estimated/Course_Estimatedmark every value that came from positions rather than from the receiver. Recorded values are never overwritten.- Parameters:
estimate_motion (bool) – Fill missing speed and course from the positions. Default True.
- Return type:
DataFrameOnly reachable through the instrument object –uataq.read_data()does not forward it.
:param See
Instrument.read_data()for the other parameters.:
- classmethod estimate_motion(data)[source]#
Fill missing
Speed_m_s/Course_degfrom the positions.Adds
Speed_EstimatedandCourse_Estimated, which are True exactly where the value was derived from positions rather than recorded by the receiver. Returnsdataunchanged if it has no positions or no DatetimeIndex to difference against.- Return type:
- class uataq.instruments.LGR_NO2(SID, name, loggers, config)[source]#
Bases:
Instrument,SensorMixinLos Gatos Research NO2 analyzer (cavity-enhanced absorption).
- class uataq.instruments.LGR_UGGA(SID, name, loggers, config)[source]#
Bases:
Instrument,SensorMixinLos Gatos Research Ultraportable Greenhouse Gas Analyzer.
Measures CO2 and CH4 by off-axis ICOS.
- class uataq.instruments.Licor_6262(SID, name, loggers, config)[source]#
Bases:
Instrument,SensorMixinLI-COR LI-6262 infrared CO2/H2O gas analyzer.
- class uataq.instruments.Licor_7000(SID, name, loggers, config)[source]#
Bases:
Licor_6262LI-COR LI-7000 infrared CO2/H2O gas analyzer.
Parsed like the LI-6262.
- class uataq.instruments.Magee_AE33(SID, name, loggers, config)[source]#
Bases:
Instrument,SensorMixinMagee Scientific AE33 aethalometer, measuring black carbon.
- class uataq.instruments.MetOne_ES405(SID, name, loggers, config)[source]#
Bases:
Instrument,SensorMixinMet One E-Sampler ES-405, reporting PM1, PM2.5, PM4 and PM10.
Replaced the ES-642 at several sites. The horel group names both models
esampleron disk, so only the configured installation and removal dates separate them – seeInstrument.clip_to_active().
- class uataq.instruments.MetOne_ES642(SID, name, loggers, config)[source]#
Bases:
Instrument,SensorMixinMet One E-Sampler ES-642, reporting PM2.5 only.
Superseded by the ES-405 at several sites; see
MetOne_ES405.
- class uataq.instruments.Teledyne_T200(SID, name, loggers, config)[source]#
Bases:
Instrument,SensorMixinTeledyne API T200 chemiluminescence NO/NO2/NOx analyzer.
- class uataq.instruments.Teledyne_T300(SID, name, loggers, config)[source]#
Bases:
Instrument,SensorMixinTeledyne API T300 gas-filter-correlation CO analyzer.
- class uataq.instruments.Teledyne_T400(SID, name, loggers, config)[source]#
Bases:
Instrument,SensorMixinTeledyne API T400 UV-absorption ozone analyzer.
- class uataq.instruments.Teledyne_T500u(SID, name, loggers, config)[source]#
Bases:
Instrument,SensorMixinTeledyne API T500U CAPS NO2 analyzer.
- class uataq.instruments.Teom_1400ab(SID, name, loggers, config)[source]#
Bases:
Instrument,SensorMixinThermo/R&P TEOM 1400ab tapered-element oscillating microbalance.
Measures PM2.5 mass.
- uataq.instruments.catalog: dict[str, type[Instrument]] = {'2b_205': <class 'uataq.instruments.BB_205'>, '2b_405': <class 'uataq.instruments.BB_405'>, 'cr1000': <class 'uataq.instruments.CR1000'>, 'gps': <class 'uataq.instruments.GPS'>, 'lgr_no2': <class 'uataq.instruments.LGR_NO2'>, 'lgr_ugga': <class 'uataq.instruments.LGR_UGGA'>, 'licor_6262': <class 'uataq.instruments.Licor_6262'>, 'licor_7000': <class 'uataq.instruments.Licor_7000'>, 'magee_ae33': <class 'uataq.instruments.Magee_AE33'>, 'metone_es405': <class 'uataq.instruments.MetOne_ES405'>, 'metone_es642': <class 'uataq.instruments.MetOne_ES642'>, 'teledyne_t200': <class 'uataq.instruments.Teledyne_T200'>, 'teledyne_t300': <class 'uataq.instruments.Teledyne_T300'>, 'teledyne_t400': <class 'uataq.instruments.Teledyne_T400'>, 'teledyne_t500u': <class 'uataq.instruments.Teledyne_T500u'>, 'teom_1400ab': <class 'uataq.instruments.Teom_1400ab'>}#
Instrument catalog