Skip to content

ADD: NEXRAD Level 3 (NIDS) radial-product reader#392

Open
mgrover1 wants to merge 2 commits into
openradar:mainfrom
mgrover1:feat/nexrad-level3
Open

ADD: NEXRAD Level 3 (NIDS) radial-product reader#392
mgrover1 wants to merge 2 commits into
openradar:mainfrom
mgrover1:feat/nexrad-level3

Conversation

@mgrover1

@mgrover1 mgrover1 commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

Adds an xarray backend (engine="nexradlevel3") and open_nexradlevel3_datatree for NEXRAD Level 3 (NIDS) radial products. Level 3 is available on AWS in real time and archived back to ~2020 (s3://unidata-nexrad-level3), so this opens a large, previously unreadable dataset to the xradar stack.

Coverage

27 message codes across the three radial packet formats:

  • Digital radial (packet 16): super-res reflectivity/velocity/spectrum width (153/154/155), legacy digital 94/99, dual-pol ZDR/CC/KDP (159/161/163), hydro class (165), digital accumulations (170/172-175)
  • Run-length encoded (packet AF1F): legacy reflectivity/velocity/spectrum width (19/20/25/27/28/30), storm-relative velocity (56), legacy accumulations (78-80)
  • Generic data packet (packet 28, XDR): DPR precip rate (176), hybrid hydro class (177) — via a small inline XDR unpacker, no new dependency

Deferred products (VIL/EET special encodings, TDWR, legacy hybrid variants) raise a clear NotImplementedError naming the product; follow-up tracking issue to come. A multi-tilt volume of the same product (e.g. N0B-N3B) assembles into a DataTree ordered by fixed angle, following the IMD multi-file precedent.

Deliberate divergences from Py-ART

Each verified against the ICD (2620001) and real data:

What This PR Py-ART
azimuth coord ray centers (start + width/2) ray start angles
range coord bin centers at the true ICD bin size edges at packet "range scale" — that field is actually floor(1000*cos(elevation)), so ranges drift up to ~580 m by the last bin
units uniform m/s (VRADH/WRADH/SRMV) and mm (ACCUM/RATE) legacy velocity in knots, precip in inches
legacy 16-level thresholds per-flag sign/scale incl. the x0.01 flag flags[0] applied to all levels
below-threshold vs range-folded flag counts read from PDB halfwords 36-38; RF exposed as a <moment>_range_folded mask fixed raw < 1/raw < 2 masks — on a real DPR file this marks ~92% of bins (genuine zero rain rate) as missing

model.py gains SRMV/ACCUM/HCLASS (and RATE) in the canonical moment names; the two test_io.py expectation updates reflect real moments in existing sample files that the canonical set previously missed.

Verification

  • 37 synthetic unit tests against byte-built files with exact ground truth (every decode scheme, packet format, mask semantics, metadata field, bz2 round-trip, error paths)
  • 11 real-file integration tests with pinned statistics (skip-guarded until the open-radar-data samples land — companion data PR to follow: 12 LOT products, ~0.9 MB, one coherent scene)
  • All 12 real LOT products cross-checked against Py-ART 2.2.5 and MetPy 1.7.1: decoded values bit-identical after the documented unit conversions
  • Example notebook opens live AWS data, plots a georeferenced PPI, and assembles a 4-tilt volume

Performance (M-series laptop, warm, mean of 20)

Case xradar Py-ART MetPy
N0B super-res (720x1840) 27 ms 31 32
N0S legacy RLE 4.7 ms 6.0 3.4
DPR (XDR) 14 ms 76 24
60-file mixed batch, per file 14 ms 20 13

Known limitation

Level 3 carries no per-ray times, so every ray holds the volume scan start time: multi-tilt volumes export to CfRadial2 but not CfRadial1 (documented in the docstring).

Adds engine='nexradlevel3' and open_nexradlevel3_datatree for NEXRAD
Level 3 radial products (packets 16/AF1F/28) with values decoded to
physical units, range-folded bins exposed as a separate mask, and
SRMV/ACCUM/HCLASS added to the canonical moment names.
@codecov

codecov Bot commented Jul 17, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.63%. Comparing base (79ba495) to head (9c8826c).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #392      +/-   ##
==========================================
+ Coverage   94.20%   94.63%   +0.43%     
==========================================
  Files          29       30       +1     
  Lines        6417     6934     +517     
==========================================
+ Hits         6045     6562     +517     
  Misses        372      372              
Flag Coverage Δ
notebooktests 0.00% <0.00%> (ø)
unittests 94.63% <100.00%> (+0.43%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

21 new synthetic tests covering malformed-file warnings/errors, the
packet-16 ragged fallback, XDR parameters/multi-component/unknown-code
paths, symbology offset override, surface-product geometry, RF mask
variable, flag attrs, CF valid_min/max, reindex and drop_variables;
drop two unused helpers. nexrad_level3.py: 100% line coverage from
synthetic tests alone.
@mgrover1
mgrover1 requested a review from aladinor July 23, 2026 02:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant