Skip to content

MA350 Aethalometer

The MA350 is an advanced aethalometer that measures black carbon concentrations at multiple wavelengths and provides source apportionment between fossil fuel and biomass burning sources.

AeroViz.rawDataReader.script.MA350.Reader

Reader(path: Path | str, reset: bool | str = False, qc: bool | str = True, **kwargs)

Bases: AbstractReader

MA350 Aethalometer Data Reader

A specialized reader for MA350 Aethalometer data files, which measure black carbon at multiple wavelengths and provide source apportionment.

See docs/api/instruments/aethalometers/MA350.md for usage and docs/guide/reader-reference.md for the file layout, status codes and QC rules.

Attributes

nam class-attribute instance-attribute

nam = 'MA350'

BC_COLUMNS class-attribute instance-attribute

BC_COLUMNS = ['BC1', 'BC2', 'BC3', 'BC4', 'BC5']

ABS_COLUMNS class-attribute instance-attribute

ABS_COLUMNS = ['abs_375', 'abs_470', 'abs_528', 'abs_625', 'abs_880']

CAL_COLUMNS class-attribute instance-attribute

CAL_COLUMNS = ['abs_550', 'AAE', 'eBC']

LATE_QC_FLAGS class-attribute instance-attribute

LATE_QC_FLAGS = ('Invalid AAE',)

MIN_BC class-attribute instance-attribute

MIN_BC = 0

MAX_BC class-attribute instance-attribute

MAX_BC = 20000

MIN_AAE class-attribute instance-attribute

MIN_AAE = 0.7

MAX_AAE class-attribute instance-attribute

MAX_AAE = 3.0

ERROR_STATES class-attribute instance-attribute

ERROR_STATES = [1, 2, 4, 16, 32, 128, 256, 2048, 8192, 16384, 32768, 65536, 262144, 524288]

STATUS_BITS class-attribute instance-attribute

STATUS_BITS = {1: 'Power Failure', 2: 'Start up', 4: 'Tape advance', 16: 'Optical saturation', 32: 'Sample timing error', 128: 'Flow unstable', 256: 'Pump drive limit', 2048: 'System busy', 8192: 'Tape jam', 16384: 'Tape at end', 32768: 'Tape not ready', 65536: 'Tape transport not ready', 262144: 'Invalid date/time', 524288: 'Tape error'}

Methods:

_raw_reader

_raw_reader(file)

Read and parse raw MA350 Aethalometer data files.

Returns all columns from the raw file. Column selection is deferred to _QC() and _process() stages.

_QC

_QC(_df)

Perform quality control on MA350 Aethalometer raw data.

QC Rules Applied (raw data only)
  1. Status Error : Invalid instrument status codes
  2. Invalid BC : BC concentration outside 0-20000 ng/m³
  3. Insufficient : Less than 50% hourly data completeness

Note: AAE validation is done in _process() after calculation.

_process

_process(_df)

Calculate absorption coefficients and validate derived parameters.

Processing Steps
  1. Calculate absorption coefficients at each wavelength
  2. Calculate AAE (Absorption Ångström Exponent)
  3. Calculate eBC (equivalent Black Carbon)
  4. Validate AAE range and update QC_Flag

Raw format

  • File pattern: *.csv.
  • Native frequency: 1 minute (the meta['freq'] fallback; the grid actually found in each file is reported as df.attrs['raw_freq']).
  • Data structure:
    • Datetime index from the Date / time local column
    • BC measurements at multiple wavelengths
    • Source apportionment data (biomass / fossil-fuel split)
    • Derived parameters (the vendor's own AAE and Delta-C)

Parse recipe

  • read_csv(parse_dates=['Date / time local'], index_col='Date / time local').
  • Column renames (keys are matched exactly, double space before the unit included): UV BCcBC1, Blue BCcBC2, Green BCcBC3, Red BCcBC4, IR BCcBC5, Biomass BCc (ng/m^3)BB mass, Fossil fuel BCc (ng/m^3)FF mass, Delta-C (ng/m^3)Delta-C, AAEAAE_ref, BB (%)BB.
  • Rows with an unparseable or duplicated timestamp are removed.
  • Every other source column is kept at L1 and through to the output.

Quirks

  • The Status column's presence has never been verified against a real export (there is no fixture); check_status_columns warns at runtime if the firmware labels it differently.

Measurement Parameters

The MA350 provides black carbon measurements at five wavelengths:

Column Wavelength Description
BC1 375 nm UV channel
BC2 470 nm Blue channel
BC3 528 nm Green channel
BC4 625 nm Red channel
BC5 880 nm Near-IR channel (reference)

Status & error codes

The Status column is tested in bitwise mode — a row is an error when any non-whitelisted code in ERROR_STATES matches it bitwise; see status modes. A missing Status column is logged as a warning and the rule then passes every row.

Codes treated as errors:

  • 1 — Power Failure
  • 2 — Start up
  • 4 — Tape advance
  • 16 — Optical saturation
  • 32 — Sample timing error
  • 128 — Flow unstable
  • 256 — Pump drive limit
  • 2048 — System busy
  • 8192 — Tape jam
  • 16384 — Tape at end
  • 32768 — Tape not ready
  • 65536 — Tape transport not ready
  • 262144 — Invalid date/time
  • 524288 — Tape error

Every error code has a named row in the register below, so nothing is treated as an error without a name.

Status Condition Register

The Status column is a bitfield: the instrument OR-sums every active condition and reports the sum, so one value can mean several things at once. Reader.STATUS_BITS carries this table, and it is what turns a raw status into df.attrs['status_conditions'] — a named condition instead of a number.

Bit Decimal Condition
19 524288 Tape error
18 262144 Invalid date/time
16 65536 Tape transport not ready
15 32768 Tape not ready
14 16384 Tape at end
13 8192 Tape jam
11 2048 System busy
8 256 Pump drive limit
7 128 Flow unstable
5 32 Sample timing error
4 16 Optical saturation
2 4 Tape advance
1 2 Start up
0 1 Power Failure

To stop treating one condition as an error, whitelist its decimal value:

RawDataReader('MA350', path, ignored_status_errors=[1])  # ignore Power Failure

QC rules

Rule Condition Severity
Status Error any non-whitelisted ERROR_STATES code set in Status error
Invalid BC any of BC1–BC5 ≤ 0 or > 20 000 ng/m³ error
Insufficient an hour holds < 50 % of the BC points it could have held, given how much of that hour the read covers advisory
Invalid AAE AAE < 0.7 or AAE > 3.0 — added in _process, after AAE has been computed error

Invalid AAE cannot be a _QC rule because AAE does not exist until _process has derived it; it is raised there through update_qc_flag and counted into the same QC summary. The upper bound of 3.0 is deliberately loose so that BrC-rich biomass-burning and dust episodes, where bulk AAE runs 2–3, survive. Insufficient is advisory because a sparse hour's readings are fine in themselves — only an hourly average would misrepresent it; promote it with flag_severity={'Insufficient': 'error'}.

Output

The processed data contains the following columns:

Column Unit Description
BC1-BC5 ng/m³ Black carbon at 5 wavelengths
abs_375-880 Mm⁻¹ Absorption coefficients (abs_375, abs_470, abs_528, abs_625, abs_880)
abs_550 Mm⁻¹ Interpolated absorption at 550nm
AAE - Absorption Angstrom Exponent (positive by convention), computed by AeroViz
eBC ng/m³ Equivalent black carbon (= BC5, 880 nm)
AAE_ref - The vendor's own AAE from the raw file, preserved separately
BB mass ng/m³ Biomass-burning BC from the instrument's source apportionment
FF mass ng/m³ Fossil-fuel BC from the instrument's source apportionment
Delta-C ng/m³ The instrument's native Delta-C (UV − IR)
BB % Biomass-burning fraction
every other source column as in the raw file Status, flows, … are kept alongside the derived columns

How the derived columns are made (_absCoe in core/pre_process.py):

  • abs_λ = BC_λ × MAE_λ × 10⁻³, where MAE is the published σ_ATN divided by C = 1.3 (see the cross-section note below).
  • AAE is the slope of a log-log fit over all five bands, sign-flipped so it is positive.
  • abs_550 is not measured: it is extrapolated from the nearest measured band as abs(λ₂) = abs(λ₁)·(λ₂/λ₁)^(−AAE).
  • A row whose BC channels are all 0, or where any channel is NaN, gets NaN in every derived column.

Files written per read are listed in RawDataReader Reference §1.

Cross section

AethLabs publish σ_ATN — the attenuation cross section (24.069, 19.070, 17.028, 14.091, 10.120 m²/g at 375–880 nm). BC × σ_ATN gives the attenuation coefficient, not absorption: the filter scatters light multiple times and inflates it by a factor C. AeroViz divides by C = 1.3 for the MA-series, so abs_* is a true absorption coefficient.

This matters when stations are compared. Uncorrected, the MA350 reads about 30% higher than an AE33 measuring the same air — invisible at a single site. After the correction the three aethalometers agree at every shared wavelength (MA350 375 nm = 18.5 vs AE33 370 nm = 18.5 m²/g, 880 nm = 7.8 vs 7.8), which test_ma350_cross_section_is_absorption_not_attenuation pins.

AAE is unaffected: it is the slope of a log-log fit, and dividing every band by the same constant only shifts the intercept.

Notes

  • Provides real-time source apportionment
  • Distinguishes between fossil fuel and biomass burning
  • Multi-wavelength measurement capability (5 channels)
  • Advanced data quality control with QCFlagBuilder