Skip to content
26 changes: 20 additions & 6 deletions mhkit/dolfyn/io/rdi.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ def read_rdi(

if len(dss) == 2:
warnings.warn(
"\nTwo profiling configurations retrieved from file" "\nReturning first."
"\nTwo profiling configurations retrieved from file\nReturning first."
)

# Close handler
Expand Down Expand Up @@ -1038,12 +1038,26 @@ def finalize(self, dat, cfg):
lib._pop(dat, nm)

# Need to figure out how to differentiate burst mode from averaging mode
if (
("source_program" in cfg)
and (cfg["source_program"].lower() in ["vmdas", "winriver", "winriver2"])
) or ("sentinelv" in cfg["inst_model"].lower()):
cfg["fs"] = round(1 / np.median(np.diff(dat["coords"]["time"])), 2)
calculate_sample_rate_from_time_diff = (
cfg.get("source_program", "").lower() in ["vmdas", "winriver", "winriver2"]
or cfg["sec_between_ping_groups"] == 0
)

if calculate_sample_rate_from_time_diff:
# Use median-based calculation for burst mode operation
time_diffs = np.diff(dat["coords"]["time"])
if cfg["sec_between_ping_groups"] == 0:
warnings.warn(
"mhkit.dolfyn: sec_between_ping_groups is zero, likely indicating burst mode operation. "
"Using median time difference to estimate sample rate, but the actual sample rate "
"may be variable and non-uniform if operating in burst mode. This could introduce "
"artifacts in downstream spectral analysis, filtering, or other time-series "
"processing that assumes constant sampling intervals. "
"Per issue #408: https://github.com/MHKiT-Software/MHKiT-Python/issues/408"
)
cfg["fs"] = round(1 / np.median(time_diffs), 2)
else:
# Standard calculation for averaging mode
cfg["fs"] = 1 / (cfg["sec_between_ping_groups"] * cfg["pings_per_ensemble"])

# Save configuration data as attributes
Expand Down
43 changes: 43 additions & 0 deletions mhkit/tests/dolfyn/test_read_adp.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import unittest
import pytest
import os
import numpy as np
from unittest.mock import patch

make_data = False
load = tb.load_netcdf
Expand Down Expand Up @@ -87,6 +89,47 @@ def test_io_rdi(self):
assert_allclose(td_transect, dat_transect, atol=1e-6)
assert_allclose(td_senb5, dat_senb5, atol=1e-6)

def test_rdi_sec_btw_ping_division_by_zero(self):
"""Test fix for issue #408: RDI burst mode division by zero

Issue #408 reported that RDI Pinnacle 45 in continuous burst mode
sets sec_between_ping_groups=0 while pings_per_ensemble=1, causing
ZeroDivisionError in sampling rate calculation.
"""
# First verify normal operation with a regular RDI file
td_rdi_normal = read("RDI_test01.000", nens=10)

# Verify normal file has valid fs (not NaN)
assert not np.isnan(td_rdi_normal.attrs["fs"])
assert td_rdi_normal.attrs["fs"] > 0

# Now test the warning condition mode by patching the RDI reader
import mhkit.dolfyn.io.rdi as rdi_module

original_finalize = rdi_module._RDIReader.finalize

def mock_finalize_sec_btw_ping(self, data, cfg):
# Force config reported in issue #408
cfg["sec_between_ping_groups"] = 0
cfg["pings_per_ensemble"] = 1
return original_finalize(self, data, cfg)

# Test scenario with patching
with patch.object(
rdi_module._RDIReader, "finalize", mock_finalize_sec_btw_ping
):
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always")

# Read the same file but with reported config
td_rdi_burst = read("RDI_test01.000", nens=10)

# Check that warning was issued
assert len(w) > 0

# Check that fs exists and is valid
assert td_rdi_burst.attrs["fs"] > 0

def test_io_nortek(self):
nens = 100
with pytest.warns(UserWarning):
Expand Down
1 change: 1 addition & 0 deletions mhkit/tests/dolfyn/test_read_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,5 +115,6 @@ def test_read_warnings(self):
sig.read_signature(exdt("AWAC_test01.wpr"))
with self.assertRaises(IOError):
read(rfnm("AWAC_test01.nc"))

with self.assertRaises(Exception):
save_netcdf(tp.dat_rdi, "test_save.fail")
5 changes: 4 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -132,4 +132,7 @@ zip-safe = false
include-package-data = true

[tool.setuptools.dynamic]
version = {attr = "mhkit.__version__"}
version = {attr = "mhkit.__version__"}

[tool.pytest.ini_options]
asyncio_default_fixture_loop_scope = "function"
Loading