oceanarray 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. oceanarray/__init__.py +20 -0
  2. oceanarray/_version.py +1 -0
  3. oceanarray/cli.py +1343 -0
  4. oceanarray/clock_offset.py +675 -0
  5. oceanarray/config/OS1_sensor_attrs.yaml +49 -0
  6. oceanarray/config/OS1_var_names.yaml +5 -0
  7. oceanarray/config/OS1_vocab_attrs.yaml +84 -0
  8. oceanarray/config/legacy/project_RAPID.yaml +69 -0
  9. oceanarray/config/legacy/rodb_keys.txt +206 -0
  10. oceanarray/config/legacy/rodb_keys.yaml +1012 -0
  11. oceanarray/config/logging.yaml +28 -0
  12. oceanarray/legacy/__init__.py +47 -0
  13. oceanarray/legacy/caldip.py +753 -0
  14. oceanarray/legacy/convertOS.py +527 -0
  15. oceanarray/legacy/find_deployment.py +489 -0
  16. oceanarray/legacy/mooring_rodb.py +429 -0
  17. oceanarray/legacy/process_rodb.py +369 -0
  18. oceanarray/legacy/rodb.py +330 -0
  19. oceanarray/logger.py +192 -0
  20. oceanarray/logsheet/__init__.py +60 -0
  21. oceanarray/logsheet/_columns.py +97 -0
  22. oceanarray/logsheet/_config.py +245 -0
  23. oceanarray/logsheet/_firmware.py +43 -0
  24. oceanarray/logsheet/_latex.py +128 -0
  25. oceanarray/logsheet/_render.py +126 -0
  26. oceanarray/logsheet/builders/__init__.py +13 -0
  27. oceanarray/logsheet/builders/caldip_download.py +195 -0
  28. oceanarray/logsheet/builders/caldip_setup.py +188 -0
  29. oceanarray/logsheet/builders/deployment_setup.py +218 -0
  30. oceanarray/logsheet/builders/mooring_download.py +243 -0
  31. oceanarray/logsheet/builders/recovery.py +157 -0
  32. oceanarray/logsheet/data/column_defs.yaml +686 -0
  33. oceanarray/logsheet/data/latex_templates/_base_instrument.tex.j2 +42 -0
  34. oceanarray/logsheet/data/latex_templates/caldip_download.tex.j2 +47 -0
  35. oceanarray/logsheet/data/latex_templates/caldip_setup.tex.j2 +70 -0
  36. oceanarray/logsheet/data/latex_templates/deployment_setup.tex.j2 +97 -0
  37. oceanarray/logsheet/data/latex_templates/mooring_download.tex.j2 +54 -0
  38. oceanarray/logsheet/data/latex_templates/recovery.tex.j2 +103 -0
  39. oceanarray/mooring_level.py +1368 -0
  40. oceanarray/oceanarray.mplstyle +18 -0
  41. oceanarray/parameters.py +270 -0
  42. oceanarray/plotter.py +942 -0
  43. oceanarray/plotters/__init__.py +97 -0
  44. oceanarray/plotters/_animation.py +261 -0
  45. oceanarray/plotters/_current.py +705 -0
  46. oceanarray/plotters/_diagnostic.py +667 -0
  47. oceanarray/plotters/_helpers.py +57 -0
  48. oceanarray/plotters/_primitives.py +101 -0
  49. oceanarray/plotters/_section.py +16 -0
  50. oceanarray/plotters/_timeseries.py +11 -0
  51. oceanarray/rapid_interp.py +739 -0
  52. oceanarray/read_rbr_hex.py +541 -0
  53. oceanarray/readers.py +303 -0
  54. oceanarray/report/__init__.py +5 -0
  55. oceanarray/report/_array.py +386 -0
  56. oceanarray/report/_grid.py +539 -0
  57. oceanarray/report/_html_helpers.py +1004 -0
  58. oceanarray/report/_instrument.py +853 -0
  59. oceanarray/report/_mooring.py +1385 -0
  60. oceanarray/report/_plots.py +4284 -0
  61. oceanarray/report/_stack.py +941 -0
  62. oceanarray/stage1.py +1959 -0
  63. oceanarray/stage2.py +1160 -0
  64. oceanarray/stage3.py +2494 -0
  65. oceanarray/time_gridding.py +958 -0
  66. oceanarray/tools.py +400 -0
  67. oceanarray/transports.py +1 -0
  68. oceanarray/utilities.py +538 -0
  69. oceanarray/validation.py +389 -0
  70. oceanarray/writers.py +99 -0
  71. oceanarray-0.1.0.dist-info/METADATA +503 -0
  72. oceanarray-0.1.0.dist-info/RECORD +76 -0
  73. oceanarray-0.1.0.dist-info/WHEEL +5 -0
  74. oceanarray-0.1.0.dist-info/entry_points.txt +2 -0
  75. oceanarray-0.1.0.dist-info/licenses/LICENSE +21 -0
  76. oceanarray-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,503 @@
1
+ Metadata-Version: 2.4
2
+ Name: oceanarray
3
+ Version: 0.1.0
4
+ Summary: Multi-stage processing pipeline for moored oceanographic observations: raw instrument files to QC-flagged CF-compliant NetCDF with automated HTML reports.
5
+ Maintainer-email: Eleanor Frajka-Williams <eleanorfrajka@gmail.com>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2025 Eleanor Frajka-Williams
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Documentation, https://ocean-uhh.github.io/oceanarray
29
+ Project-URL: Homepage, https://github.com/ocean-uhh/oceanarray
30
+ Project-URL: Repository, https://github.com/ocean-uhh/oceanarray
31
+ Project-URL: Issue Tracker, https://github.com/ocean-uhh/oceanarray/issues
32
+ Keywords: oceanography,mooring,ADCP,CTD,NetCDF,QC,oceanographic-data,AMOC
33
+ Classifier: Development Status :: 4 - Beta
34
+ Classifier: Intended Audience :: Science/Research
35
+ Classifier: Topic :: Scientific/Engineering :: Oceanography
36
+ Classifier: License :: OSI Approved :: MIT License
37
+ Classifier: Programming Language :: Python :: 3 :: Only
38
+ Classifier: Programming Language :: Python :: 3.9
39
+ Classifier: Programming Language :: Python :: 3.10
40
+ Classifier: Programming Language :: Python :: 3.11
41
+ Classifier: Programming Language :: Python :: 3.12
42
+ Requires-Python: >=3.9
43
+ Description-Content-Type: text/markdown
44
+ License-File: LICENSE
45
+ Requires-Dist: numpy>=1.23
46
+ Requires-Dist: pandas>=2.0
47
+ Requires-Dist: xarray>=2023.12.0
48
+ Requires-Dist: netcdf4>=1.6
49
+ Requires-Dist: scipy>=1.10
50
+ Requires-Dist: matplotlib>=3.7
51
+ Requires-Dist: gsw>=3.6.16
52
+ Requires-Dist: ppigrf
53
+ Requires-Dist: ioos_qc
54
+ Requires-Dist: dolfyn
55
+ Requires-Dist: jinja2>=3.0
56
+ Requires-Dist: packaging>=21.0
57
+ Dynamic: license-file
58
+
59
+ # OceanArray
60
+
61
+ Python tools for processing moored oceanographic array observations from raw instrument files to quality-controlled, CF-compliant NetCDF.
62
+
63
+ ## Overview
64
+
65
+ OceanArray implements a multi-stage processing pipeline for mooring data, controlled by YAML configuration files for reproducible processing:
66
+
67
+ - **Instrument-level** (Stages 1–3): convert raw files → standardise → clock-correct → QC-flag
68
+ - **Mooring-level** (Stack + Grid): combine instruments → common time axis → pressure grid
69
+ - **HTML report**: self-contained mooring recovery summary from YAML + processed files
70
+
71
+ ## Installation
72
+
73
+ ```bash
74
+ pip install oceanarray
75
+ ```
76
+
77
+ Then install `seasenselib` (required for reading raw instrument files; available on PyPI
78
+ but needs `--no-deps` to avoid version conflicts):
79
+
80
+ ```bash
81
+ pip install pyrsktools pycnv
82
+ pip install seasenselib --no-deps
83
+ ```
84
+
85
+ ### From source (conda)
86
+
87
+ ```bash
88
+ git clone https://github.com/ocean-uhh/oceanarray.git
89
+ cd oceanarray
90
+ conda create -n oceanarray python=3.11
91
+ conda activate oceanarray
92
+ pip install -r requirements-dev.txt
93
+ pip install -e .
94
+ pip install pyrsktools pycnv && pip install seasenselib --no-deps
95
+ ```
96
+
97
+ ### From source (venv)
98
+
99
+ ```bash
100
+ git clone https://github.com/ocean-uhh/oceanarray.git
101
+ cd oceanarray
102
+ python3.11 -m venv venv
103
+ source venv/bin/activate # Windows: venv\Scripts\activate
104
+ pip install -r requirements-dev.txt
105
+ pip install -e .
106
+ pip install pyrsktools pycnv && pip install seasenselib --no-deps
107
+ ```
108
+
109
+ > **Python version**: Python 3.9–3.12 is supported. Python 3.13+ has not been
110
+ > tested with all dependencies and is not recommended.
111
+
112
+ ## Quick start
113
+
114
+ ```bash
115
+ RAW=/path/to/cruise/raw # raw instrument files: $RAW/{mooring}/{instrument}/file
116
+ PROC=/path/to/cruise/proc # processed output root
117
+
118
+ # Instrument-level: stages 1 and 2 (default)
119
+ oceanarray process dsG3_1_2026 --raw-dir $RAW --proc-dir $PROC
120
+
121
+ # All three instrument-level stages (stage 3 adds QC flags and velocity rotation)
122
+ oceanarray process dsG3_1_2026 --raw-dir $RAW --proc-dir $PROC --stage 1 2 3
123
+
124
+ # Single instrument rerun
125
+ oceanarray process dsG3_1_2026 --raw-dir $RAW --proc-dir $PROC --stage 3 --serial 7507 --force
126
+
127
+ # Mooring-level: stack all instruments onto 60 s time grid
128
+ oceanarray stack dsG3_1_2026 --proc-dir $PROC
129
+
130
+ # Mooring-level: vertically interpolate onto pressure grid
131
+ oceanarray grid dsG3_1_2026 --proc-dir $PROC
132
+
133
+ # Generate main HTML mooring report (fast — no per-instrument pages)
134
+ oceanarray report dsG3_1_2026 --raw-dir $RAW --proc-dir $PROC
135
+
136
+ # Also generate per-instrument pages (slow)
137
+ oceanarray report dsG3_1_2026 --raw-dir $RAW --proc-dir $PROC --instruments
138
+
139
+ # Also generate gridded and stacked report pages
140
+ oceanarray report dsG3_1_2026 --raw-dir $RAW --proc-dir $PROC --grid --stack
141
+
142
+ # One specific instrument page only
143
+ oceanarray report dsG3_1_2026 --raw-dir $RAW --proc-dir $PROC --serial 7507
144
+
145
+ # Validate mooring YAML configuration
146
+ oceanarray validate dsG3_1_2026.mooring.yaml
147
+
148
+ # Run the complete pipeline in one command (stages 1–3, stack, grid, all reports)
149
+ oceanarray run dsG3_1_2026 --raw-dir $RAW --proc-dir $PROC --dp 10
150
+ ```
151
+
152
+ ## Recommended first-processing workflow
153
+
154
+ Processing a mooring for the first time has a natural checkpoint: you need to
155
+ verify that the deployment start and end times in the YAML are correct before
156
+ stage 2 trims the records. The sequence below catches problems early and
157
+ avoids reprocessing the full mooring unnecessarily.
158
+
159
+ ### Step 1 — convert raw files to CF-NetCDF
160
+
161
+ ```bash
162
+ oceanarray process dsG3_1_2026 --raw-dir $RAW --proc-dir $PROC --stage 1
163
+ ```
164
+
165
+ Stage 1 reads each raw instrument file and writes `_stage1.nc` with no
166
+ modifications — the full record exactly as recorded by the instrument.
167
+ Check the console output for any read errors or missing files.
168
+
169
+ ### Step 2 — inspect the summary report
170
+
171
+ ```bash
172
+ oceanarray report dsG3_1_2026 --raw-dir $RAW --proc-dir $PROC --instruments
173
+ ```
174
+
175
+ The summary page shows which stage 1 files exist and how many records each
176
+ instrument has. Open the per-instrument pages to look at time series and
177
+ check for obvious pre-deployment records (instrument left running on deck,
178
+ depth hovering near zero) that will need to be trimmed by stage 2.
179
+
180
+ ### Step 3 — set deployment start/end times in the YAML
181
+
182
+ Edit `dsG3_1_2026.mooring.yaml` and set `deployment_time` and `recovery_time`
183
+ (or per-instrument `deployment_start` / `deployment_end` overrides) to the
184
+ correct times identified from the data. Stage 2 will trim to this window.
185
+
186
+ If you are unsure about one instrument, process it alone first:
187
+
188
+ ```bash
189
+ oceanarray process dsG3_1_2026 --raw-dir $RAW --proc-dir $PROC --stage 2 --serial 7507
190
+ oceanarray report dsG3_1_2026 --raw-dir $RAW --proc-dir $PROC --serial 7507 --force
191
+ ```
192
+
193
+ Iterate on the YAML until the trimmed record looks correct for each instrument.
194
+
195
+ ### Step 4 — complete the pipeline
196
+
197
+ Once deployment times are confirmed for all instruments:
198
+
199
+ ```bash
200
+ oceanarray process dsG3_1_2026 --raw-dir $RAW --proc-dir $PROC --stage 2 3
201
+ oceanarray stack dsG3_1_2026 --proc-dir $PROC
202
+ oceanarray grid dsG3_1_2026 --proc-dir $PROC --dp 10
203
+ oceanarray report dsG3_1_2026 --raw-dir $RAW --proc-dir $PROC --instruments --stack --grid
204
+ ```
205
+
206
+ ### Subsequent reruns
207
+
208
+ Once settings are finalised (deployment window, QC thresholds, clock offsets)
209
+ the full pipeline can be rerun in a single command:
210
+
211
+ ```bash
212
+ oceanarray run dsG3_1_2026 --raw-dir $RAW --proc-dir $PROC --dp 10 --force
213
+ ```
214
+
215
+ `oceanarray run` executes all stages in order (1 → 2 → 3 → stack → grid →
216
+ all reports). Each step runs regardless of earlier failures; check the exit
217
+ code and log files in `processing_logs/` if something looks wrong.
218
+
219
+ ## Processing pipeline
220
+
221
+ ### Instrument-level
222
+
223
+ | Stage | Module | Input | Output | What it does |
224
+ |-------|--------|-------|--------|--------------|
225
+ | 1 | `stage1.py` | raw instrument file | `_stage1.nc` | Format conversion to CF-NetCDF; normalises pressure units (`db` → `dbar`), conductivity names, ITS-90 temperature scale annotation; for Nortek Aquadopps: reads BEAM→XYZ transformation matrix from header and stores it as `nortek_transformation_matrix`; applies T-matrix to produce instrument-frame XYZ velocities |
226
+ | 2 | `stage2.py` | `_stage1.nc` | `_stage2.nc` | Clock corrections; trim to deployment window |
227
+ | 3 | `stage3.py` | `_stage2.nc` | `_stage3.nc` | Normalises conductivity to mS/cm; derives practical salinity via `gsw.SP_from_C`; pressure interpolation for instruments without a sensor; QARTOD gross-range + spike QC on T, C, S, P; **Aquadopp only**: XYZ → ENU rotation using heading/pitch/roll + magnetic declination (via `ppigrf`), producing `east_velocity`, `north_velocity`, `up_velocity`, `current_speed`, `current_direction`; tilt QC on velocity |
228
+
229
+ Stage 3 writes `_stage3.nc` for **all** instruments. Instruments that already have pressure receive QC flags only; those lacking pressure additionally receive interpolated pressure (flagged `pressure_qc = 8`).
230
+
231
+ Thresholds applied during stage 3 are stored as attributes on each `*_qc` variable (e.g. `qc_gross_range_fail_min`) so the report can display exactly what was used without re-reading the YAML.
232
+
233
+ **Aquadopp coordinate transformation (stage 1 → stage 3):**
234
+
235
+ Stage 1 reads the instrument-specific BEAM→XYZ transformation matrix `T` from the Nortek header and stores it as a scalar variable `nortek_transformation_matrix` (shape 3×3, flattened). It applies `T` to the raw beam velocities to produce `velocity_x/y/z` in the instrument frame (XYZ).
236
+
237
+ Stage 3 then rotates XYZ → ENU using the per-sample heading, pitch, and roll recorded by the instrument:
238
+
239
+ ```
240
+ ENU = H(heading, declination) @ P(pitch) @ XYZ
241
+ ```
242
+
243
+ where `H` is the horizontal rotation matrix (heading − 90° + magnetic declination) and `P` is the pitch tilt matrix. Magnetic declination is computed by `ppigrf` at the deployment midpoint (lat/lon/date from YAML) and stored in `magnetic_declination` as a global attribute. If `ppigrf` is unavailable or the position is unknown, declination defaults to 0° with a warning. The resulting `east_velocity` and `north_velocity` are in true geographic (ENU) coordinates. The attribute `nortek_coordinate_system` tracks the current frame (`BEAM` → `XYZ` → `ENU`).
244
+
245
+ ### Mooring-level
246
+
247
+ | Command | Output | What it does |
248
+ |---------|--------|--------------|
249
+ | `oceanarray stack` | `{mooring}_stack.nc` | Resample all instruments to a common time axis (default 60 s); stack into a single file with an `N_LEVELS` dimension ordered deep-first; compute potential density; Aquadopp velocity/orientation stored unmasked with `velocity_flag` (worst of east/north/up QC); `tilt_from_pressure` computed for each Aquadopp from the nearest reference instrument ≥10 m above |
250
+ | `oceanarray grid` | `{mooring}_grid.nc` | Linearly interpolate stacked data onto a regular pressure grid |
251
+
252
+ ### Report
253
+
254
+ `oceanarray report` generates self-contained HTML pages (all figures embedded as base64 PNGs — no external dependencies, open offline, printable via Ctrl+P).
255
+
256
+ By default only the main mooring summary is generated (fast). Use flags to opt in to the slower pages:
257
+
258
+ | Flag | Page generated | Speed |
259
+ |------|---------------|-------|
260
+ | *(none)* | `{mooring}_report.html` — mooring summary | fast |
261
+ | `--instruments` | `{mooring}_{serial}_report.html` per instrument | slow |
262
+ | `--grid` | `{mooring}_grid_report.html` — T/S pcolormesh | moderate |
263
+ | `--stack` | `{mooring}_stack_report.html` — pressure & T time series | moderate |
264
+ | `--serial SN [SN ...]` | per-instrument page(s) for listed serial(s) only | moderate |
265
+
266
+ **Mooring summary** (`{mooring}_report.html`):
267
+ 1. Header card (cruise, ship, deployment/recovery times, location, water depth)
268
+ 2. Mooring diagram — embedded inline if `{mooring}_diagram.pdf` is found alongside the YAML
269
+ 3. Processing pipeline badges per instrument (Raw → Read → Stage 1 → 2 → 3 → Stack → Grid)
270
+ 4. Instrument summary table (first/last sample, N records, YAML Δt, observed Δt, variable presence) — S/N links to per-instrument page
271
+ 5. Clock correction table
272
+ 6. Sensor calibration metadata (from `SENSOR_*` variables in stage 2 NC files)
273
+ 7. QC flag summary — per-instrument × per-variable percentage breakdown with colour-coded stacked bars (OceanSITES flag colours)
274
+
275
+ **Gridded data report** (`{mooring}_grid_report.html`, requires `--grid`):
276
+ - Variable coverage table
277
+ - Temperature pcolormesh (20 discrete levels, RdYlBu_r)
278
+ - Practical salinity pcolormesh (20 discrete levels, YlGnBu_r; blue = fresh)
279
+ - Potential density pcolormesh and contourf (BuPu) with iso-density contour lines (default 27.7 and 27.8 kg m⁻³); depth of isopycnals over the full deployment and a 3-day zoom
280
+ - Current speed pcolormesh (plasma) and current direction (0–360° true, hsv colormap) — requires Aquadopp ENU data from stage 3
281
+ - Up velocity pcolormesh (RdBu_r, symmetric)
282
+ - N² buoyancy frequency squared (log₁₀ scale)
283
+ - T-S heat map (log₁₀ count per bin, half-page width)
284
+ - Temperature power spectrum (Welch PSD, one line per depth level)
285
+
286
+ **Stacked data report** (`{mooring}_stack_report.html`, requires `--stack`):
287
+ - Instrument table (type, serial [linked to per-instrument page], HAB, approximate depth)
288
+ - Pressure, temperature, salinity time series (all instruments overlaid)
289
+ - East, north, and up velocity time series (Aquadopp instruments; velocities stored unmasked; `velocity_flag` = worst of east/north/up QC)
290
+ - Aquadopp tilt panels — one panel per Aquadopp: time series of |pitch|, |roll|, and pressure-derived tilt (`arccos(ΔP / rope_length)` using the nearest instrument ≥10 m above with valid pressure); scatter plot of |pitch|/|roll| vs. pressure tilt with 1:1 line and 20°/30° threshold lines
291
+ - T-S diagram (two panels: scatter coloured by pressure + 2-D count heatmap)
292
+ - Current rose diagrams (ENU, good/suspect/bad QC split)
293
+ - Adjacent instrument spacing histogram
294
+ - Variable coverage table (at page end)
295
+
296
+ **Per-instrument pages** (`{mooring}_{serial}_report.html`, requires `--instruments` or `--serial`):
297
+ - Processing history (the NC `history` attribute, one row per stage)
298
+ - Full deployment time series with QC flag markers (+ suspect/bad, · interpolated); velocity panels centred on zero
299
+ - First 48 h and last 48 h window zooms
300
+ - T-S diagram (two panels: scatter coloured by pressure with QC overlays, and 2-D count heatmap)
301
+ - Current rose diagrams (ENU frame; good/suspect/bad split by QARTOD flag)
302
+ - Data value histograms — one panel per variable; heading fixed to 0–360°; velocity panels centred on zero; battery excluded
303
+ - QC flag breakdown table with stacked bars
304
+ - NetCDF variable table (all time-series variables: dims, N, units, long name, standard name, QC companion flag)
305
+ - Scalar metadata table (InstrDepth, serial_number, coordinate system, transformation matrix, etc.)
306
+ - Global attributes table
307
+
308
+ ## Supported instrument types
309
+
310
+ | Instrument | File types | Variables |
311
+ |------------|------------|-----------|
312
+ | Sea-Bird SBE37 MicroCAT | `sbe-cnv`, `sbe-asc`, `sbe-ascii` | T, C, P |
313
+ | Nortek Aquadopp | `nortek-aqd`, `nortek-ascii`, `nortek-csv` | U, V, W, P, T |
314
+ | RBR Solo / Duet | `rbr-rsk`, `rbr-dat` | T (Solo), T+C (Duet) |
315
+ | RDI WorkHorse ADCP | `rdi-raw` | U, V, W (all bins) |
316
+
317
+ ## Configuration
318
+
319
+ Each mooring is described by a `{mooring}.mooring.yaml` file placed in `proc/{mooring}/`.
320
+ The same YAML is shared with the `moordiag` package; fields used only by `moordiag` (`year`, `status`, `label`, `image`, `clamp_id`, hardware entries without `instrument`) are silently ignored by `oceanarray`.
321
+
322
+ **Required top-level fields:** `name`, `waterdepth`, `deployment_time`, `recovery_time`, `directory`
323
+
324
+ **Required per-instrument fields:** `instrument` (sets subdirectory), `serial`, `file_type`, `filename`
325
+
326
+ `oceanarray` processes only `clamp` entries that have an `instrument` key; hardware entries (e.g. shackles, floats) used by `moordiag` are skipped.
327
+
328
+ **Location fields** — report uses the first available in priority order: `seabed_latitude/longitude` → `deployment_latitude/longitude` → `planned_latitude/longitude` → `latitude/longitude`
329
+
330
+ ```yaml
331
+ name: dsG3_1_2026
332
+ waterdepth: 992
333
+ seabed_latitude: "65 29.84 N" # best position (triangulated); or use deployment_ or planned_
334
+ seabed_longitude: "029 24.60 W"
335
+ deployment_cruise: MSM142 # used in report header; if absent, 'cruise' is used
336
+ deployment_ship: MS Merian
337
+ deployment_time: "2026-05-07T17:05:00"
338
+ recovery_cruise: OdB # if absent, deployment_cruise is repeated
339
+ recovery_ship: Odon de Buen
340
+ recovery_time: "2026-07-10T17:45:00"
341
+
342
+ # QC overrides apply at mooring level (all instruments) or per instrument in clamp.
343
+ # qc_ranges, qc_spike, and tilt_qc can appear at either level.
344
+ qc_ranges:
345
+ temperature:
346
+ fail_span: [-2.5, 12.0]
347
+ suspect_span: [-1.0, 10.0]
348
+ pressure:
349
+ fail_span: [-5.0, 1050.0]
350
+ suspect_span: [-0.5, 1020.0]
351
+ salinity:
352
+ fail_span: [0.0, 50.0]
353
+ suspect_span: [0.0, 35.5]
354
+
355
+ clamp:
356
+ - instrument: microcat # sets raw file subdirectory (basedir/raw/microcat/…)
357
+ serial: 7507
358
+ hab: 412.3 # height above bottom (m); used for depth and ordering
359
+ file_type: sbe-cnv
360
+ filename: 7507_recovery.cnv
361
+ sample_interval_seconds: 15 # optional; used in report
362
+ # clock_offset = total correction at deployment; clock_drift_seconds = total at recovery.
363
+ # Stage 2 ramps linearly between the two (positive = instrument was slow/behind UTC).
364
+ clock_offset: 0 # instrument correctly set at deployment
365
+ # Option B — two timestamps at recovery give the total correction at recovery
366
+ computer_clock_at_recovery: '20260710T19:12:30' # compact ISO or "HH:MM:SS"
367
+ instrument_clock_at_recovery: '20260710T19:12:39' # computer − instrument = −9 s
368
+
369
+ - instrument: aquadopp
370
+ serial: 14321
371
+ hab: 26.5
372
+ file_type: nortek-ascii
373
+ filename: 14321_recovery.dat
374
+ sample_interval_seconds: 120
375
+ qc_spike: # instrument-level override (also valid at mooring level)
376
+ east_velocity: {suspect_threshold: 0.3, fail_threshold: 1.0}
377
+
378
+ - instrument: microcat
379
+ serial: 5367
380
+ hab: 716
381
+ file_type: sbe-ascii
382
+ filename: 5367_recovery.asc
383
+ sample_interval_seconds: 60
384
+ computer_clock_at_recovery: '20260710T18:43:30'
385
+ instrument_clock_at_recovery: '20260710T18:44:03'
386
+ ```
387
+
388
+ ## QC flags (QARTOD / OceanSITES Reference Table 2)
389
+
390
+ | Flag | Meaning |
391
+ |------|---------|
392
+ | 1 | Good data |
393
+ | 3 | Suspect (outside `suspect_span`, spike detected, or tilt 20–30°) |
394
+ | 4 | Bad (outside `fail_span`, or tilt > 30°) |
395
+ | 8 | Interpolated (pressure assigned from neighbouring instrument) |
396
+ | 9 | Missing value |
397
+
398
+ Default thresholds live in `oceanarray/parameters.py` (`QC_GROSS_RANGE`, `QC_SPIKE`, `QC_TILT`). All thresholds are in the units stored in the NC variable (degC, mS/cm, PSU, dbar, m/s). Stage 3 normalises conductivity to mS/cm before applying QC, so thresholds are always compared against values in the right unit.
399
+
400
+ Override priority (highest wins): **instrument-level** → **mooring-level** → **package defaults**
401
+
402
+ | YAML key | Level | Purpose |
403
+ |----------|-------|---------|
404
+ | `qc_ranges` | mooring or instrument | Gross-range fail/suspect spans per variable |
405
+ | `qc_spike` | mooring or instrument | Spike test thresholds per variable |
406
+ | `tilt_qc` | mooring or instrument | Roll thresholds for Aquadopp velocity flagging |
407
+
408
+ Instrument-level values override mooring-level values for that instrument only; other instruments continue to use the mooring-level setting.
409
+
410
+ **Default gross-range spans** (global ocean; override per mooring or instrument via `qc_ranges`):
411
+
412
+ | Variable | Suspect span | Fail span | Units | Notes |
413
+ |----------|-------------|-----------|-------|-------|
414
+ | `temperature` | −2 to 35 | −2.5 to 40 | °C | |
415
+ | `conductivity` | 0 to 65 | 0 to 75 | mS/cm | stage 3 converts S/m → mS/cm first |
416
+ | `salinity` | 2 to 40 | 0 to 40 | PSU | derived from T/C/P via gsw |
417
+ | `pressure` | −0.5 to 7000 | −5 to 7000 | dbar | override per instrument for known depth |
418
+ | `east/north_velocity` | −5 to 5 | −5 to 5 | m/s | |
419
+ | `up_velocity` | −2 to 2 | −5 to 5 | m/s | |
420
+
421
+ **Default spike thresholds** (60–120 s sampling; override via `qc_spike`):
422
+
423
+ | Variable | Suspect | Fail | Notes |
424
+ |----------|---------|------|-------|
425
+ | `temperature` | 2.0 °C | 6.0 °C | |
426
+ | `conductivity` | 2.0 mS/cm | 5.0 mS/cm | catches biofouling (fish in cell) low spikes |
427
+ | `salinity` | 0.5 PSU | 2.0 PSU | catches T/C timing mismatches on unpumped sensors |
428
+ | `pressure` | 10.0 dbar | 50.0 dbar | |
429
+ | velocity | — | — | omitted: burst-mode Aquadopps produce false positives at burst boundaries |
430
+
431
+ **Tilt QC (Aquadopp):** stage 3 flags all velocity variables (`east/north/up_velocity`) when pitch or roll exceeds the tilt thresholds. The primary path uses `pitch_qc` and `roll_qc` already computed by the gross-range step and merges them (worst flag wins). The fallback path (when neither `_qc` flag exists) computes `max(|pitch|, |roll|)` and compares against the thresholds. Default: suspect at 20°, bad at 30°. Override example:
432
+
433
+ ```yaml
434
+ - instrument: aquadopp
435
+ serial: 14321
436
+ hab: 26.5
437
+ tilt_qc:
438
+ suspect_threshold: 15 # degrees (applied to pitch and roll)
439
+ fail_threshold: 25
440
+ ```
441
+
442
+ ## Python API
443
+
444
+ > **Note**: the CLI (`oceanarray process`, `stack`, `grid`, `report`) is the primary
445
+ > interface and is stable. The Python API is available but may change between minor
446
+ > versions while the package is in beta.
447
+
448
+ ```python
449
+ from oceanarray.stage1 import MooringProcessor
450
+ from oceanarray.stage2 import Stage2Processor
451
+ from oceanarray.stage3 import Stage3Processor
452
+ from oceanarray.mooring_level import MooringStacker, MooringGridder
453
+
454
+ base = '/path/to/data'
455
+ mooring = 'dsG3_1_2026'
456
+
457
+ MooringProcessor(base).process_mooring(mooring)
458
+ Stage2Processor(base).process_mooring(mooring)
459
+ Stage3Processor(base).process_mooring(mooring)
460
+ MooringStacker(base).stack(mooring)
461
+ MooringGridder(base).grid(mooring)
462
+ ```
463
+
464
+ ## Project structure
465
+
466
+ ```
467
+ oceanarray/
468
+ ├── oceanarray/
469
+ │ ├── stage1.py # Raw → CF-NetCDF conversion
470
+ │ ├── stage2.py # Clock corrections + deployment trim
471
+ │ ├── stage3.py # Pressure interpolation + QARTOD QC
472
+ │ ├── mooring_level.py # Stack (N_LEVELS×time) and Grid (pressure×time)
473
+ │ ├── report.py # HTML mooring recovery report generator
474
+ │ ├── parameters.py # Package defaults (QC thresholds, colormaps, …)
475
+ │ ├── plotters.py # Visualisation
476
+ │ ├── clock_offset.py # Clock drift analysis
477
+ │ ├── time_gridding.py # Multi-instrument time gridding
478
+ │ ├── readers.py # Low-level format readers
479
+ │ ├── writers.py # NetCDF writers
480
+ │ ├── logger.py # Processing log system
481
+ │ └── validation.py # YAML and file format validation
482
+ ├── tests/
483
+ ├── notebooks/
484
+ └── docs/
485
+ ```
486
+
487
+ ## Testing
488
+
489
+ ```bash
490
+ pytest # full suite
491
+ pytest tests/test_stage1.py -v
492
+ pytest --cov=oceanarray # with coverage
493
+ ```
494
+
495
+ ## Documentation
496
+
497
+ ```bash
498
+ cd docs && make html
499
+ ```
500
+
501
+ ## License
502
+
503
+ MIT License
@@ -0,0 +1,76 @@
1
+ oceanarray/__init__.py,sha256=LZ0R_b4JEU7FoRYn530xxW0xH1bEuFd92aNCOcOozSE,438
2
+ oceanarray/_version.py,sha256=L6zbQIZKsAP-Knhm6fBcQFPoVdIDuejxze60qX23jiw,21
3
+ oceanarray/cli.py,sha256=Chmg9wxjbEDhHmnCBMABcnpeRktrA4U4WVAI6oaA_lc,47524
4
+ oceanarray/clock_offset.py,sha256=qerJFxnITaEqtExthstm7QzthVBh0qDcr39trjcLy4w,22085
5
+ oceanarray/logger.py,sha256=CsSjH0424_C5NZjYy4lNn393wR2PkFASdebRp8SqPLQ,6008
6
+ oceanarray/mooring_level.py,sha256=hNYkS-plD2FgzmWRX25COIZLN4hddUfmJ4gxpoMuylY,54310
7
+ oceanarray/oceanarray.mplstyle,sha256=TVcv5xb7NAzjkm8Ax0FssiIK6qt7YpuzeRmFCwwon9A,373
8
+ oceanarray/parameters.py,sha256=7vVTPaxILAHvgjbhqPMuH27q3zf84qwDu4LT1JjU0PE,12819
9
+ oceanarray/plotter.py,sha256=ju0qwAxsdeJFElPS5NxsccIeZMZINbUK8wKOkEmwm_Q,32364
10
+ oceanarray/rapid_interp.py,sha256=BDKvMe5Oj_uv68JQlPCG_AEvqExJHSNMLAbV3Zatkn8,25017
11
+ oceanarray/read_rbr_hex.py,sha256=rLqyGlFPFHPWc4knpE6fAiouH9a90_-P5BMKkEzMItM,19143
12
+ oceanarray/readers.py,sha256=OgcxFmnNbayec3i3FjRmsSrif164JQxbqXczLQnQZyw,10239
13
+ oceanarray/stage1.py,sha256=Lr_b_pV49ONCcyn-8MSabVks3jgX8mkef1nWPXo4VX4,84171
14
+ oceanarray/stage2.py,sha256=7-q3we3sB5NExtVaeegBTolwS6eS9NHGJyaYmYSLflA,47150
15
+ oceanarray/stage3.py,sha256=weuvdIM6EztU7KabII2Dq3TPJpTDI3ul2spE9jk9KKo,101048
16
+ oceanarray/time_gridding.py,sha256=M5rXG26qFo82meYJOjl2K8UFxVH6zf4ng-gBf5FuLcs,36301
17
+ oceanarray/tools.py,sha256=nL-C5rN_GyaCAL0DIotd2K-MoYdXmgQJb0edBjHbU9A,12793
18
+ oceanarray/transports.py,sha256=IHOcvp9wwUpDjbfuIpyNmClLlFCvpv3jWOtarnoFqus,86
19
+ oceanarray/utilities.py,sha256=3diU0iM8-VXk42ZsRaq1PGjFoIKfPtXbttIopdwyXAE,17096
20
+ oceanarray/validation.py,sha256=Vi9t8VUmEYPmkklQfL_YjwPE43nNp-n6R_CsGizX-9E,15008
21
+ oceanarray/writers.py,sha256=UdUzTJKV4_Zz3wMlNvQKS5k66e9g06l8ypauo3q9VSc,3360
22
+ oceanarray/config/OS1_sensor_attrs.yaml,sha256=jGs_RcHdagizUUUNo-1fssnhzUuEBJGWZsSRTfApnTU,1656
23
+ oceanarray/config/OS1_var_names.yaml,sha256=yMh0LTY_GoBGCsok172Quoo7q29WTwuMpvV03jlqwJw,64
24
+ oceanarray/config/OS1_vocab_attrs.yaml,sha256=hofGON2m9DkDbJt5mVdxgu1Y7XrRONxkzHyU9RvRA_U,2426
25
+ oceanarray/config/logging.yaml,sha256=B9NP7G1Pn7egLP-A5SXxEmvFKYpr8pkd1-8wJxZ3zAg,1143
26
+ oceanarray/config/legacy/project_RAPID.yaml,sha256=4z9BDBtF9t4M1ou7XFREAZG6eWj6B6Ya4RZiBL-T_8o,4618
27
+ oceanarray/config/legacy/rodb_keys.txt,sha256=yPmQPDdpAB86MpxbQWVPqkvE1-cjQod9WG1iWBwf2Uk,13994
28
+ oceanarray/config/legacy/rodb_keys.yaml,sha256=Z66EmoYF4-6hakOksXxM_6BhhiJEMc7yoEYGCJjRO5E,17012
29
+ oceanarray/legacy/__init__.py,sha256=NhAmVDqIb3-x5Qb41-Ekj8lbn-7Adb_rVyADt6KNfkI,1267
30
+ oceanarray/legacy/caldip.py,sha256=1eCDTzE2tI1Zo8gZLt-j9pubxlROZ7Go_jcvVs9LJxM,27163
31
+ oceanarray/legacy/convertOS.py,sha256=cOZBamcZPQw51yraZFqIJ5krzMgqlD28D1O_XTTATkg,16756
32
+ oceanarray/legacy/find_deployment.py,sha256=k_8xx5ItrFi0iIqbTLRvfZqRwKKjucurSGrmwl_oMlw,17519
33
+ oceanarray/legacy/mooring_rodb.py,sha256=JPQtCLJESSMBynaDUR3urcAmQxcSeTuO_q3rUgE94lg,14414
34
+ oceanarray/legacy/process_rodb.py,sha256=WtriygJs_s08MtSlERBQvA90KGYBl_mDqGWXiPF_2r8,11261
35
+ oceanarray/legacy/rodb.py,sha256=3qh1N2nbjW1BuqcB54_7FTAU4U553FPIdIc0yqH5v_c,11228
36
+ oceanarray/logsheet/__init__.py,sha256=KZ-NGxYPhCpHi7x2vhEWi-NMKqTyRmbbgyK2f5hc1sY,1721
37
+ oceanarray/logsheet/_columns.py,sha256=1VLNhVvj8UBCyvENZfM6KG7gLQ_aDthM9enDH7d8-Z0,3098
38
+ oceanarray/logsheet/_config.py,sha256=igkpUAvhdLZGuTnv1vgwUUnd1lvXmjCOE1NXZFY8Bg0,8241
39
+ oceanarray/logsheet/_firmware.py,sha256=NbYIDkpcX5DG0K_n3pcTiZAIUfklGL_hpJaqENp4kjs,1356
40
+ oceanarray/logsheet/_latex.py,sha256=20owFL0Wkh1rf0GCs9w2GMydl0p-Id-csuQSPJe-joc,3938
41
+ oceanarray/logsheet/_render.py,sha256=qZ_wb5uZXd1TKY6Hzkdl5i77N337zqJ9vPivbNBhflM,3422
42
+ oceanarray/logsheet/builders/__init__.py,sha256=UbIoZLSYfboncdHw7Fo9kYSeg9wpWR9J7Q2yOSRW87A,391
43
+ oceanarray/logsheet/builders/caldip_download.py,sha256=BCCgFV9Hr7tBfBDpUi639-BoE7D-XbONVi_66C4AtEY,6918
44
+ oceanarray/logsheet/builders/caldip_setup.py,sha256=a8IYlTzQJykjTaGJAptga2IETeEfu4ZdaN5cyZaZ0qM,6587
45
+ oceanarray/logsheet/builders/deployment_setup.py,sha256=616xeGcW54YVR8-o-_PL17UMWHU3lkCcuDxTSqigkxg,7538
46
+ oceanarray/logsheet/builders/mooring_download.py,sha256=WWyxnhSkxL_itm42wCSpmaN6T8Sd94RQRphj3QM2uUk,9101
47
+ oceanarray/logsheet/builders/recovery.py,sha256=NKUIt42oIgGHGCtTEb_R11e3ejPzARVLA7ddC6cbzgY,5361
48
+ oceanarray/logsheet/data/column_defs.yaml,sha256=49SIRnRRX-TP65IvnroEUxBYt1sw1pHEt8AN8JiPYHE,19584
49
+ oceanarray/logsheet/data/latex_templates/_base_instrument.tex.j2,sha256=ckK2DJs9PiswTZoWtYMAaaBGDIyGXHIKmomGBjZV-Ao,1116
50
+ oceanarray/logsheet/data/latex_templates/caldip_download.tex.j2,sha256=eh3PaSRYg7fVxBSnX7z8vERx34Q63tIUBB4lOPWLc6E,1601
51
+ oceanarray/logsheet/data/latex_templates/caldip_setup.tex.j2,sha256=I15DYwl7jT7dthoIhsQz5aCL_b4S16p_VEbmAhNFOTQ,2388
52
+ oceanarray/logsheet/data/latex_templates/deployment_setup.tex.j2,sha256=NnxNqs0U-zT8xJ7XJGNC9fbe6WoqasCv6xHnBz_GRSY,2906
53
+ oceanarray/logsheet/data/latex_templates/mooring_download.tex.j2,sha256=_ZiiGnnwM_fk8caLW_Gj2AuwvbVOvatFoiXZUH0c83w,1878
54
+ oceanarray/logsheet/data/latex_templates/recovery.tex.j2,sha256=3toat-u5PahMonzJGrW-vQCzvwY4I5DB_e8mkCokG2Y,2684
55
+ oceanarray/plotters/__init__.py,sha256=EEQikZtN7bTgfZR4EftDwZZBTGhQ9eyAeUtOPfKuoek,3476
56
+ oceanarray/plotters/_animation.py,sha256=bgrNYBnbl3G9IlHFSXw_UcEeKrngvJBPjNCRxbEJ5Jg,8914
57
+ oceanarray/plotters/_current.py,sha256=JwB_2IaHBOnboSXvGTH5YfIR_Ke6gQuNbQFafu23cy0,23446
58
+ oceanarray/plotters/_diagnostic.py,sha256=DAbaexeXVkmC1cQUE9TQ5NBGmQuTma17l_kB1W1dWB4,22382
59
+ oceanarray/plotters/_helpers.py,sha256=rgGj4SeUJ5AX9-ZWMoiea_KUQMp24ZcC0vThqrhAgvI,1798
60
+ oceanarray/plotters/_primitives.py,sha256=4s4B4KeW8MNe-lgTptAq5np6WjGoFyMhY7A9xvbubxM,3531
61
+ oceanarray/plotters/_section.py,sha256=NmPI2_Flw3k2tSTUmMmTEtwvCIYpcoWoJ9De0l6XfKE,725
62
+ oceanarray/plotters/_timeseries.py,sha256=2HZ8-u_GBBgiBnPm2gC7Kpq8b1cO8fasWops6WGOF-U,484
63
+ oceanarray/report/__init__.py,sha256=IV-Gk_RJXSCPHOkZk4R0BF2-Vo0OEdpoFmFQM4dH9uI,111
64
+ oceanarray/report/_array.py,sha256=drVTl6-02ZCKu8jY4AJ9_dsY4yt79wrLPW46ZsnE-SA,13800
65
+ oceanarray/report/_grid.py,sha256=_4aDrIhbluotGzlU9Mj7jLbvaE5EUJDbMLtGoYpVYrw,25638
66
+ oceanarray/report/_html_helpers.py,sha256=uFMqMw1qZQECow8HtNgr1sBVcQrD6UYYKwPnNpRQE0s,36263
67
+ oceanarray/report/_instrument.py,sha256=G7DG_sSF9Q_36UDNgKZotrB5c1mjuVHY1KjuCgVNDo4,37604
68
+ oceanarray/report/_mooring.py,sha256=qzN1bMeaKahwT7swt6g8GORZDRS3CMfunNmTqIS1TpA,58468
69
+ oceanarray/report/_plots.py,sha256=Pb327bM8fnDHx3bsy6GlHd9qpnxZx7yBm5E_orlQhjw,161814
70
+ oceanarray/report/_stack.py,sha256=XAOQDjEeW0m-3WHLpH9ioAWnpQnPYyHxqPIQ89ApIvQ,41560
71
+ oceanarray-0.1.0.dist-info/licenses/LICENSE,sha256=Mas8UNj-qZ0qpZpfCtA1_mp434PAB0jDSmo_IFO2p0M,1080
72
+ oceanarray-0.1.0.dist-info/METADATA,sha256=c60r7qFFeDpynL6uQDn8k_Fr1eO_t_Q7d9hwxufBPKQ,24036
73
+ oceanarray-0.1.0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
74
+ oceanarray-0.1.0.dist-info/entry_points.txt,sha256=zIrjplb6dO3r-i8f7xw5vKndO2yiotWwhDOIuvNjYrs,51
75
+ oceanarray-0.1.0.dist-info/top_level.txt,sha256=t2jEvKxFeOfd2Cc6XovtLiwQIMcniqI5bKzPEHa2eCU,11
76
+ oceanarray-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (83.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ oceanarray = oceanarray.cli:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Eleanor Frajka-Williams
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ oceanarray