From 51a6f851c8719cb4633ff8cfa6fa71cecc42f197 Mon Sep 17 00:00:00 2001 From: Lloyd Izard <76954858+LOCEANlloydizard@users.noreply.github.com> Date: Mon, 29 Jun 2026 21:52:11 -0700 Subject: [PATCH] Fix channel-frequency mapping in compute_MVBS --- echopype/commongrid/api.py | 29 +++++++++++-- .../tests/commongrid/test_commongrid_api.py | 41 +++++++++++++++++++ 2 files changed, 66 insertions(+), 4 deletions(-) diff --git a/echopype/commongrid/api.py b/echopype/commongrid/api.py index c5fcc4ddc..546d98b15 100644 --- a/echopype/commongrid/api.py +++ b/echopype/commongrid/api.py @@ -183,9 +183,18 @@ def compute_MVBS( prov_dict = echopype_prov_attrs(process_type="processing") prov_dict["processing_function"] = "commongrid.compute_MVBS" ds_MVBS = ds_MVBS.assign_attrs(prov_dict) - ds_MVBS["frequency_nominal"] = ds_Sv["frequency_nominal"] # re-attach frequency_nominal - ds_MVBS["channel"] = ds_Sv["channel"] # re-attach channel + # Preserve the channel order returned by compute_raw_MVBS and align + # frequency_nominal to that order. + freq = ds_Sv["frequency_nominal"] + + if "ping_time" in freq.dims: + freq = freq.isel(ping_time=0, drop=True) + + if "channel" in ds_MVBS.dims: + ds_MVBS["frequency_nominal"] = freq.sel(channel=ds_MVBS["channel"]) + else: + ds_MVBS["frequency_nominal"] = freq ds_MVBS = insert_input_processing_level(ds_MVBS, input_ds=ds_Sv) return ds_MVBS @@ -259,7 +268,12 @@ def compute_MVBS_index_binning(ds_Sv, range_sample_num=100, ping_num=100): prov_dict = echopype_prov_attrs(process_type="processing") prov_dict["processing_function"] = "commongrid.compute_MVBS_index_binning" ds_MVBS = ds_MVBS.assign_attrs(prov_dict) - ds_MVBS["frequency_nominal"] = ds_Sv["frequency_nominal"] # re-attach frequency_nominal + freq = ds_Sv["frequency_nominal"] + + if "ping_time" in freq.dims: + freq = freq.isel(ping_time=0, drop=True) + + ds_MVBS["frequency_nominal"] = freq ds_MVBS = insert_input_processing_level(ds_MVBS, input_ds=ds_Sv) @@ -387,8 +401,15 @@ def compute_NASC( # Set ping time binning information ds_NASC["ping_time"] = (["distance"], raw_NASC["ping_time"].data, ds_Sv["ping_time"].attrs) + freq = ds_Sv["frequency_nominal"] + + if "ping_time" in freq.dims: + freq = freq.isel(ping_time=0, drop=True) - ds_NASC["frequency_nominal"] = ds_Sv["frequency_nominal"] # re-attach frequency_nominal + if "channel" in ds_NASC.dims: + ds_NASC["frequency_nominal"] = freq.sel(channel=ds_NASC["channel"]) + else: + ds_NASC["frequency_nominal"] = freq # Attach attributes _set_var_attrs( diff --git a/echopype/tests/commongrid/test_commongrid_api.py b/echopype/tests/commongrid/test_commongrid_api.py index 63ce2ebc2..0b7367314 100644 --- a/echopype/tests/commongrid/test_commongrid_api.py +++ b/echopype/tests/commongrid/test_commongrid_api.py @@ -91,6 +91,37 @@ def test__groupby_x_along_channels(request, range_var, lat_lon): assert f"{range_var}_bins" in sv_mean.dims +@pytest.mark.unit +def test_compute_MVBS_preserves_channel_frequency_mapping_with_2d_frequency_nominal( + mock_Sv_dataset_regular, +): + ds_Sv = mock_Sv_dataset_regular.copy() + + # Force AZFP-like metadata: frequency_nominal(ping_time, channel) + freq_values = xr.DataArray( + np.tile(np.array([120000.0, 200000.0]), (ds_Sv.sizes["ping_time"], 1)), + dims=("ping_time", "channel"), + coords={ + "ping_time": ds_Sv["ping_time"], + "channel": ds_Sv["channel"], + }, + ) + ds_Sv["frequency_nominal"] = freq_values + + ds_MVBS = ep.commongrid.compute_MVBS( + ds_Sv, + range_bin="2m", + ping_time_bin="1s", + ) + + expected_freq = ds_Sv["frequency_nominal"].isel(ping_time=0).sel( + channel=ds_MVBS["channel"] + ) + + assert ds_MVBS["frequency_nominal"].dims == ("channel",) + assert np.array_equal(ds_MVBS["frequency_nominal"].values, expected_freq.values) + + # NASC Tests @pytest.mark.integration @pytest.mark.parametrize("compute_mvbs", [True, False]) @@ -300,6 +331,16 @@ def test_compute_MVBS(test_data_samples): ping_time_bin = "20s" ds_MVBS = ep.commongrid.compute_MVBS(Sv, ping_time_bin=ping_time_bin) assert ds_MVBS is not None + freq = Sv["frequency_nominal"] + + if "ping_time" in freq.dims: + freq = freq.isel(ping_time=0, drop=True) + + expected_freq = freq.sel(channel=ds_MVBS["channel"]) + assert np.array_equal( + ds_MVBS["frequency_nominal"].values, + expected_freq.values, + ) # Test to see if ping_time was resampled correctly expected_ping_time = (