From a3d278c38b482b6e46320b529767850fa82f7c97 Mon Sep 17 00:00:00 2001 From: praneethratna Date: Sat, 26 Aug 2023 14:35:24 +0530 Subject: [PATCH 01/11] replaced usage of xml.dom.minidom with xml.etree.ElementTree --- echopype/convert/parse_azfp.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/echopype/convert/parse_azfp.py b/echopype/convert/parse_azfp.py index 701128437..cd1705be8 100644 --- a/echopype/convert/parse_azfp.py +++ b/echopype/convert/parse_azfp.py @@ -1,5 +1,5 @@ import os -import xml.dom.minidom +import xml.etree.ElementTree as ET from collections import defaultdict from datetime import datetime as dt from struct import unpack @@ -129,10 +129,10 @@ def load_AZFP_xml(self): def get_value_by_tag_name(tag_name, element=0): """Returns the value in an XML tag given the tag name and the number of occurrences.""" - return px.getElementsByTagName(tag_name)[element].childNodes[0].data + return root.iter(tag_name).__next__().text - xmlmap = fsspec.get_mapper(self.xml_path, **self.storage_options) - px = xml.dom.minidom.parse(xmlmap.fs.open(xmlmap.root)) + et = ET.parse(self.xml_path) + root = et.getroot() # Retrieve integer parameters from the xml file for old_name, new_name in XML_INT_PARAMS.items(): From 558f3b985df97537ec3b0af93d2a474b9637a45b Mon Sep 17 00:00:00 2001 From: praneethratna Date: Sun, 17 Sep 2023 03:08:35 +0530 Subject: [PATCH 02/11] modified azfp parser and parsed all parameters --- echopype/convert/parse_azfp.py | 93 ++++++++------------- echopype/convert/set_groups_azfp.py | 26 ++++-- echopype/tests/convert/test_convert_azfp.py | 37 ++++++++ 3 files changed, 90 insertions(+), 66 deletions(-) diff --git a/echopype/convert/parse_azfp.py b/echopype/convert/parse_azfp.py index cd1705be8..d135b9147 100644 --- a/echopype/convert/parse_azfp.py +++ b/echopype/convert/parse_azfp.py @@ -1,4 +1,5 @@ import os +import re import xml.etree.ElementTree as ET from collections import defaultdict from datetime import datetime as dt @@ -11,45 +12,7 @@ from .parse_base import ParseBase FILENAME_DATETIME_AZFP = "\\w+.01A" -XML_INT_PARAMS = { - "NumFreq": "num_freq", - "SerialNumber": "serial_number", - "BurstInterval": "burst_interval", - "PingsPerBurst": "pings_per_burst", - "AverageBurstPings": "average_burst_pings", - "SensorsFlag": "sensors_flag", -} -XML_FLOAT_PARAMS = [ - # Temperature coeffs - "ka", - "kb", - "kc", - "A", - "B", - "C", - # Tilt coeffs - "X_a", - "X_b", - "X_c", - "X_d", - "Y_a", - "Y_b", - "Y_c", - "Y_d", -] -XML_FREQ_PARAMS = { - "RangeSamples": "range_samples", - "RangeAveragingSamples": "range_averaging_samples", - "DigRate": "dig_rate", - "LockOutIndex": "lockout_index", - "Gain": "gain", - "PulseLen": "pulse_length", - "DS": "DS", - "EL": "EL", - "TVR": "TVR", - "VTX0": "VTX", - "BP": "BP", -} + HEADER_FIELDS = ( ("profile_flag", "u2"), ("profile_number", "u2"), @@ -118,7 +81,7 @@ def __init__(self, file, params, storage_options={}, dgram_zarr_vars={}): self.xml_path = params # Class attributes - self.parameters = dict() + self.parameters = defaultdict(list) self.unpacked_data = defaultdict(list) self.sonar_type = "AZFP" @@ -127,25 +90,36 @@ def load_AZFP_xml(self): """Parses the AZFP XML file. """ - def get_value_by_tag_name(tag_name, element=0): - """Returns the value in an XML tag given the tag name and the number of occurrences.""" - return root.iter(tag_name).__next__().text - - et = ET.parse(self.xml_path) - root = et.getroot() - - # Retrieve integer parameters from the xml file - for old_name, new_name in XML_INT_PARAMS.items(): - self.parameters[new_name] = int(get_value_by_tag_name(old_name)) - # Retrieve floating point parameters from the xml file - for param in XML_FLOAT_PARAMS: - self.parameters[param] = float(get_value_by_tag_name(param)) - # Retrieve frequency dependent parameters from the xml file - for old_name, new_name in XML_FREQ_PARAMS.items(): - self.parameters[new_name] = [ - float(get_value_by_tag_name(old_name, ch)) - for ch in range(self.parameters["num_freq"]) - ] + xmlmap = fsspec.get_mapper(self.xml_path, **self.storage_options) + root = ET.parse(xmlmap.fs.open(xmlmap.root)).getroot() + + for child in root.iter(): + if len(child.attrib) > 0: + for key, val in child.attrib.items(): + self.parameters[child.tag + key].append(val) + + if all(char == "\n" for char in child.text): + continue + else: + try: + val = int(child.text) + except ValueError: + val = float(child.text) + + if len(child.tag) > 3 and not child.tag.startswith("VTX"): + words = re.findall("[A-Z]+[a-z]*", child.tag) + words_lower = [word.lower() for word in words] + if len(words) > 1: + self.parameters["_".join(words_lower)].append(val) + elif len(words) == 1: + self.parameters[words_lower[0]].append(val) + else: + self.parameters[child.tag].append(val) + + # Handling the case where there is only one value for each parameter + for key, val in self.parameters.items(): + if len(val) == 1: + self.parameters[key] = val[0] def _compute_temperature(self, ping_num, is_valid): """ @@ -245,7 +219,6 @@ def _test_valid_params(params): header_chunk = file.read(self.HEADER_SIZE) if header_chunk: header_unpacked = unpack(self.HEADER_FORMAT, header_chunk) - # Reading will stop if the file contains an unexpected flag if self._split_header(file, header_unpacked): # Appends the actual 'data values' to unpacked_data diff --git a/echopype/convert/set_groups_azfp.py b/echopype/convert/set_groups_azfp.py index 76da16019..04d427e77 100644 --- a/echopype/convert/set_groups_azfp.py +++ b/echopype/convert/set_groups_azfp.py @@ -81,13 +81,14 @@ def _create_unique_channel_name(self): """ serial_number = self.parser_obj.unpacked_data["serial_number"] + frequency_number = self.parser_obj.parameters["FrequencyNumber"] if serial_number.size == 1: freq_as_str = self.freq_sorted.astype(int).astype(str) # TODO: replace str(i+1) with Frequency Number from XML channel_id = [ - str(serial_number) + "-" + freq + "-" + str(i + 1) + str(serial_number) + "-" + freq + "-" + frequency_number[i] for i, freq in enumerate(freq_as_str) ] @@ -146,8 +147,7 @@ def set_sonar(self) -> xr.Dataset: "sonar_model": self.sonar_model, "sonar_serial_number": int(self.parser_obj.unpacked_data["serial_number"]), "sonar_software_name": "AZFP", - # TODO: software version is hardwired. Read it from the XML file's AZFP_Version node - "sonar_software_version": "1.4", + "sonar_software_version": "based on AZFP Matlab version 1.4", "sonar_type": "echosounder", } ds = ds.assign_attrs(sonar_attr_dict) @@ -418,7 +418,7 @@ def set_vendor(self) -> xr.Dataset: unpacked_data = self.parser_obj.unpacked_data parameters = self.parser_obj.parameters ping_time = self.parser_obj.ping_time - tdn = parameters["pulse_length"][self.freq_ind_sorted] / 1e6 + tdn = parameters["pulse_len"][self.freq_ind_sorted] / 1e6 anc = np.array(unpacked_data["ancillary"]) # convert to np array for easy slicing # Build variables in the output xarray Dataset @@ -488,6 +488,17 @@ def set_vendor(self) -> xr.Dataset: "phase": unpacked_data["phase"], "number_of_channels": unpacked_data["num_chan"], # parameters with channel dimension from XML file + "instrument_type": parameters["instrument_type"][0], + "minor": parameters["minor"], + "major": parameters["major"], + "date": parameters["date"], + "program": parameters["program"], + "cpu": parameters["cpu"], + "serial_number": parameters["serial_number"], + "board_version": parameters["board_version"], + "file_version": parameters["file_version"], + "parameter_version": parameters["parameter_version"], + "configuration_version": parameters["configuration_version"], "XML_transmit_duration_nominal": (["channel"], tdn), # tdn comes from parameters "XML_gain_correction": (["channel"], parameters["gain"][self.freq_ind_sorted]), "XML_digitization_rate": ( @@ -496,12 +507,15 @@ def set_vendor(self) -> xr.Dataset: ), "XML_lockout_index": ( ["channel"], - parameters["lockout_index"][self.freq_ind_sorted], + parameters["lock_out_index"][self.freq_ind_sorted], ), "DS": (["channel"], parameters["DS"][self.freq_ind_sorted]), "EL": (["channel"], parameters["EL"][self.freq_ind_sorted]), "TVR": (["channel"], parameters["TVR"][self.freq_ind_sorted]), - "VTX": (["channel"], parameters["VTX"][self.freq_ind_sorted]), + "VTX0": (["channel"], parameters["VTX0"][self.freq_ind_sorted]), + "VTX1": (["channel"], parameters["VTX1"][self.freq_ind_sorted]), + "VTX2": (["channel"], parameters["VTX2"][self.freq_ind_sorted]), + "VTX3": (["channel"], parameters["VTX3"][self.freq_ind_sorted]), "Sv_offset": (["channel"], Sv_offset), "number_of_samples_digitized_per_pings": ( ["channel"], diff --git a/echopype/tests/convert/test_convert_azfp.py b/echopype/tests/convert/test_convert_azfp.py index 075fde0fb..d82266eb8 100644 --- a/echopype/tests/convert/test_convert_azfp.py +++ b/echopype/tests/convert/test_convert_azfp.py @@ -10,12 +10,14 @@ from scipy.io import loadmat from echopype import open_raw import pytest +from echopype.convert.parse_azfp import ParseAZFP @pytest.fixture def azfp_path(test_path): return test_path["AZFP"] + def check_platform_required_scalar_vars(echodata): # check convention-required variables in the Platform group for var in [ @@ -172,3 +174,38 @@ def test_convert_azfp_01a_notemperature_notilt(azfp_path): assert "tilt_y" in echodata["Platform"] assert echodata["Platform"]["tilt_x"].isnull().all() assert echodata["Platform"]["tilt_y"].isnull().all() + + +def test_load_parse_azfp_xml(azfp_path): + + azfp_01a_path = azfp_path / '17082117.01A' + azfp_xml_path = azfp_path / '17030815.XML' + parseAZFP = ParseAZFP(str(azfp_01a_path), str(azfp_xml_path)) + parseAZFP.load_AZFP_xml() + expected_params = ['InstrumentTypestring', 'instrument_type', 'major', 'minor', 'date', + 'Programname', 'program', 'CPU', 'serial_number', 'board_version', + 'file_version', 'parameter_version', 'configuration_version', 'eclock', + 'digital_board_version', 'SensorsFlagPressureSensorInstalled', + 'SensorsFlagParosInstalled', 'sensors_flag', 'U0', 'Y1', 'Y2', 'Y3', 'C1', + 'C2', 'C3', 'D1', 'D2', 'T1', 'T2', 'T3', 'T4', 'T5', 'X_a', 'X_b', 'X_c', + 'X_d', 'Y_a', 'Y_b', 'Y_c', 'Y_d', 'period', 'ppm_offset', 'calibration', + 'a0', 'a1', 'a2', 'a3', 'ka', 'kb', 'kc', 'A', 'B', 'C', 'num_freq', + 'kHzunits', 'kHz', 'TVR', 'num_vtx', 'VTX0', 'VTX1', 'VTX2', 'VTX3', 'BP', + 'EL', 'DS', 'min_pulse_len', 'sound_speed', 'StartDatesvalue', 'start_date', + 'num_frequencies', 'num_phases', 'DataOutputsvalue', 'data_output', + 'Frequencyunits', 'frequency', 'PhaseNumber', 'PhaseTypesvalue', 'phase_type', + 'Durationsvalue', 'duration', 'PingPeriodunits', 'ping_period', + 'BurstIntervalunits', 'burst_interval', 'PingsPerBurstunits', + 'pings_per_burst', 'AverageBurstPingsunits', 'average_burst_pings', + 'FrequencyNumber', 'AcquireFrequencyunits', 'acquire_frequency', + 'PulseLenunits', 'pulse_len', 'DigRateunits', 'dig_rate', 'RangeSamplesunits', + 'range_samples', 'RangeAveragingSamplesunits', 'range_averaging_samples', + 'LockOutIndexunits', 'lock_out_index', 'Gainunits', 'gain', + 'StorageFormatunits', 'storage_format'] + + assert set(parseAZFP.parameters.keys()) == set(expected_params) + assert list(set(parseAZFP.parameters['InstrumentTypestring']))[0] == 'AZFP' + assert isinstance(parseAZFP.parameters['num_freq'], int) + assert isinstance(parseAZFP.parameters['pulse_len'], list) + assert parseAZFP.parameters['num_freq'] == 4 + assert parseAZFP.parameters['pulse_len'] == [300, 300, 300, 300] From 54e1068793e3ad610713be3697f1ab65a87715f4 Mon Sep 17 00:00:00 2001 From: praneethratna Date: Mon, 18 Sep 2023 23:22:36 +0530 Subject: [PATCH 03/11] refactored the parse_azfp code and added new tests --- echopype/convert/parse_azfp.py | 20 ++++--- echopype/convert/set_groups_azfp.py | 58 +++++++++++++++++--- echopype/tests/convert/test_convert_azfp.py | 61 +++++++++++++-------- 3 files changed, 101 insertions(+), 38 deletions(-) diff --git a/echopype/convert/parse_azfp.py b/echopype/convert/parse_azfp.py index d135b9147..39e316455 100644 --- a/echopype/convert/parse_azfp.py +++ b/echopype/convert/parse_azfp.py @@ -94,9 +94,10 @@ def load_AZFP_xml(self): root = ET.parse(xmlmap.fs.open(xmlmap.root)).getroot() for child in root.iter(): + camel_case_tag = self._to_camelCase(child.tag) if len(child.attrib) > 0: for key, val in child.attrib.items(): - self.parameters[child.tag + key].append(val) + self.parameters[camel_case_tag + "_" + self._to_camelCase(key)].append(val) if all(char == "\n" for char in child.text): continue @@ -107,12 +108,7 @@ def load_AZFP_xml(self): val = float(child.text) if len(child.tag) > 3 and not child.tag.startswith("VTX"): - words = re.findall("[A-Z]+[a-z]*", child.tag) - words_lower = [word.lower() for word in words] - if len(words) > 1: - self.parameters["_".join(words_lower)].append(val) - elif len(words) == 1: - self.parameters[words_lower[0]].append(val) + self.parameters[camel_case_tag].append(val) else: self.parameters[child.tag].append(val) @@ -121,6 +117,16 @@ def load_AZFP_xml(self): if len(val) == 1: self.parameters[key] = val[0] + def _to_camelCase(self, tag): + words = re.findall("[A-Z]+[a-z]*", tag) + words_lower = [word.lower() for word in words] + if len(words) > 1: + return "_".join(words_lower) + elif len(words) == 1: + return words_lower[0] + else: + return tag + def _compute_temperature(self, ping_num, is_valid): """ Compute temperature in celsius. diff --git a/echopype/convert/set_groups_azfp.py b/echopype/convert/set_groups_azfp.py index 04d427e77..f2228ef84 100644 --- a/echopype/convert/set_groups_azfp.py +++ b/echopype/convert/set_groups_azfp.py @@ -81,7 +81,7 @@ def _create_unique_channel_name(self): """ serial_number = self.parser_obj.unpacked_data["serial_number"] - frequency_number = self.parser_obj.parameters["FrequencyNumber"] + frequency_number = self.parser_obj.parameters["frequency_number"] if serial_number.size == 1: freq_as_str = self.freq_sorted.astype(int).astype(str) @@ -488,6 +488,16 @@ def set_vendor(self) -> xr.Dataset: "phase": unpacked_data["phase"], "number_of_channels": unpacked_data["num_chan"], # parameters with channel dimension from XML file + "XML_transmit_duration_nominal": ( + ["channel"], + tdn, + {"long_name": "(From XML file) Nominal bandwidth of transmitted pulse"}, + ), # tdn comes from parameters + "XML_gain_correction": ( + ["channel"], + parameters["gain"][self.freq_ind_sorted], + {"long_name": "(From XML file) Gain correction"}, + ), "instrument_type": parameters["instrument_type"][0], "minor": parameters["minor"], "major": parameters["major"], @@ -499,8 +509,6 @@ def set_vendor(self) -> xr.Dataset: "file_version": parameters["file_version"], "parameter_version": parameters["parameter_version"], "configuration_version": parameters["configuration_version"], - "XML_transmit_duration_nominal": (["channel"], tdn), # tdn comes from parameters - "XML_gain_correction": (["channel"], parameters["gain"][self.freq_ind_sorted]), "XML_digitization_rate": ( ["channel"], parameters["dig_rate"][self.freq_ind_sorted], @@ -508,14 +516,46 @@ def set_vendor(self) -> xr.Dataset: "XML_lockout_index": ( ["channel"], parameters["lock_out_index"][self.freq_ind_sorted], + { + "long_name": "(From XML file) The distance, rounded to the nearest " + "Bin Size after the pulse is transmitted that over which AZFP will " + "ignore echoes" + }, ), "DS": (["channel"], parameters["DS"][self.freq_ind_sorted]), - "EL": (["channel"], parameters["EL"][self.freq_ind_sorted]), - "TVR": (["channel"], parameters["TVR"][self.freq_ind_sorted]), - "VTX0": (["channel"], parameters["VTX0"][self.freq_ind_sorted]), - "VTX1": (["channel"], parameters["VTX1"][self.freq_ind_sorted]), - "VTX2": (["channel"], parameters["VTX2"][self.freq_ind_sorted]), - "VTX3": (["channel"], parameters["VTX3"][self.freq_ind_sorted]), + "EL": ( + ["channel"], + parameters["EL"][self.freq_ind_sorted], + {"long_name": "Sound pressure at the transducer", "units": "dB"}, + ), + "TVR": ( + ["channel"], + parameters["TVR"][self.freq_ind_sorted], + { + "long_name": "Transmit voltage response of the transducer", + "units": "dB re 1uPa/V at 1m", + }, + ), + "VTX0": ( + ["channel"], + parameters["VTX0"][self.freq_ind_sorted], + {"long_name": "Amplified voltage 0 sent to the transducer"}, + ), + "VTX1": ( + ["channel"], + parameters["VTX1"][self.freq_ind_sorted], + {"long_name": "Amplified voltage 1 sent to the transducer"}, + ), + "VTX2": ( + ["channel"], + parameters["VTX2"][self.freq_ind_sorted], + {"long_name": "Amplified voltage 2 sent to the transducer"}, + ), + "VTX3": ( + ["channel"], + parameters["VTX3"][self.freq_ind_sorted], + {"long_name": "Amplified voltage 3 sent to the transducer"}, + ), "Sv_offset": (["channel"], Sv_offset), "number_of_samples_digitized_per_pings": ( ["channel"], diff --git a/echopype/tests/convert/test_convert_azfp.py b/echopype/tests/convert/test_convert_azfp.py index d82266eb8..700be5b29 100644 --- a/echopype/tests/convert/test_convert_azfp.py +++ b/echopype/tests/convert/test_convert_azfp.py @@ -182,30 +182,47 @@ def test_load_parse_azfp_xml(azfp_path): azfp_xml_path = azfp_path / '17030815.XML' parseAZFP = ParseAZFP(str(azfp_01a_path), str(azfp_xml_path)) parseAZFP.load_AZFP_xml() - expected_params = ['InstrumentTypestring', 'instrument_type', 'major', 'minor', 'date', - 'Programname', 'program', 'CPU', 'serial_number', 'board_version', + expected_params = ['instrument_type_string', 'instrument_type', 'major', 'minor', 'date', + 'program_name', 'program', 'CPU', 'serial_number', 'board_version', 'file_version', 'parameter_version', 'configuration_version', 'eclock', - 'digital_board_version', 'SensorsFlagPressureSensorInstalled', - 'SensorsFlagParosInstalled', 'sensors_flag', 'U0', 'Y1', 'Y2', 'Y3', 'C1', - 'C2', 'C3', 'D1', 'D2', 'T1', 'T2', 'T3', 'T4', 'T5', 'X_a', 'X_b', 'X_c', - 'X_d', 'Y_a', 'Y_b', 'Y_c', 'Y_d', 'period', 'ppm_offset', 'calibration', - 'a0', 'a1', 'a2', 'a3', 'ka', 'kb', 'kc', 'A', 'B', 'C', 'num_freq', - 'kHzunits', 'kHz', 'TVR', 'num_vtx', 'VTX0', 'VTX1', 'VTX2', 'VTX3', 'BP', - 'EL', 'DS', 'min_pulse_len', 'sound_speed', 'StartDatesvalue', 'start_date', - 'num_frequencies', 'num_phases', 'DataOutputsvalue', 'data_output', - 'Frequencyunits', 'frequency', 'PhaseNumber', 'PhaseTypesvalue', 'phase_type', - 'Durationsvalue', 'duration', 'PingPeriodunits', 'ping_period', - 'BurstIntervalunits', 'burst_interval', 'PingsPerBurstunits', - 'pings_per_burst', 'AverageBurstPingsunits', 'average_burst_pings', - 'FrequencyNumber', 'AcquireFrequencyunits', 'acquire_frequency', - 'PulseLenunits', 'pulse_len', 'DigRateunits', 'dig_rate', 'RangeSamplesunits', - 'range_samples', 'RangeAveragingSamplesunits', 'range_averaging_samples', - 'LockOutIndexunits', 'lock_out_index', 'Gainunits', 'gain', - 'StorageFormatunits', 'storage_format'] - + 'digital_board_version', 'sensors_flag_pressure_sensor_installed', + 'sensors_flag_paros_installed', 'sensors_flag', 'U0', 'Y1', 'Y2', 'Y3', + 'C1', 'C2', 'C3', 'D1', 'D2', 'T1', 'T2', 'T3', 'T4', 'T5', 'X_a', 'X_b', + 'X_c', 'X_d', 'Y_a', 'Y_b', 'Y_c', 'Y_d', 'period', 'ppm_offset', + 'calibration', 'a0', 'a1', 'a2', 'a3', 'ka', 'kb', 'kc', 'A', 'B', 'C', + 'num_freq', 'hz_units', 'kHz', 'TVR', 'num_vtx', 'VTX0', 'VTX1', 'VTX2', + 'VTX3', 'BP', 'EL', 'DS', 'min_pulse_len', 'sound_speed', + 'start_date_svalue', 'start_date', 'num_frequencies', 'num_phases', + 'data_output_svalue', 'data_output', 'frequency_units', 'frequency', + 'phase_number', 'phase_type_svalue', 'phase_type', 'duration_svalue', + 'duration', 'ping_period_units', 'ping_period', 'burst_interval_units', + 'burst_interval', 'pings_per_burst_units', 'pings_per_burst', + 'average_burst_pings_units', 'average_burst_pings', 'frequency_number', + 'acquire_frequency_units', 'acquire_frequency', 'pulse_len_units', + 'pulse_len', 'dig_rate_units', 'dig_rate', 'range_samples_units', + 'range_samples', 'range_averaging_samples_units', 'range_averaging_samples', + 'lock_out_index_units', 'lock_out_index', 'gain_units', 'gain', + 'storage_format_units', 'storage_format'] assert set(parseAZFP.parameters.keys()) == set(expected_params) - assert list(set(parseAZFP.parameters['InstrumentTypestring']))[0] == 'AZFP' + assert list(set(parseAZFP.parameters['instrument_type_string']))[0] == 'AZFP' assert isinstance(parseAZFP.parameters['num_freq'], int) assert isinstance(parseAZFP.parameters['pulse_len'], list) assert parseAZFP.parameters['num_freq'] == 4 - assert parseAZFP.parameters['pulse_len'] == [300, 300, 300, 300] + expected_frequency_numbers = ['1', '2', '3', '4'] + assert len(parseAZFP.parameters['frequency_number']) == 4 + assert all(x == y for x, y in zip(parseAZFP.parameters['frequency_number'], + expected_frequency_numbers)) + expected_len_params = ['acquire_frequency', 'pulse_len', 'dig_rate', 'range_samples', + 'range_averaging_samples', 'lock_out_index', 'gain', 'storage_format'] + assert all(len(parseAZFP.parameters[x]) == 4 for x in expected_len_params) + assert all(x == 1 for x in parseAZFP.parameters['acquire_frequency']) + assert all(x == 300 for x in parseAZFP.parameters['pulse_len']) + assert all(x == 20000 for x in parseAZFP.parameters['dig_rate']) + expected_range_samples = [1752, 1752, 1764, 540] + assert all(x == y for x, y in zip(parseAZFP.parameters['range_samples'], + expected_range_samples)) + assert all(x == 4 for x in parseAZFP.parameters['range_averaging_samples']) + assert all(x == 0 for x in parseAZFP.parameters['lock_out_index']) + assert all(x == 1 for x in parseAZFP.parameters['gain']) + assert all(x == 1 for x in parseAZFP.parameters['storage_format']) + From a4b90e1e1f9d825953e472e633bfe199c9f0326d Mon Sep 17 00:00:00 2001 From: Wu-Jung Lee Date: Mon, 18 Sep 2023 11:18:29 -0700 Subject: [PATCH 04/11] small tweak in test, rename conversion function to _camel_to_snake --- echopype/convert/parse_azfp.py | 31 +++++++++++---------- echopype/tests/convert/test_convert_azfp.py | 24 ++++++++-------- 2 files changed, 28 insertions(+), 27 deletions(-) diff --git a/echopype/convert/parse_azfp.py b/echopype/convert/parse_azfp.py index 66b209140..4c40c87a2 100644 --- a/echopype/convert/parse_azfp.py +++ b/echopype/convert/parse_azfp.py @@ -85,19 +85,32 @@ def __init__(self, file, params, storage_options={}, dgram_zarr_vars={}): self.unpacked_data = defaultdict(list) self.sonar_type = "AZFP" + def _camel_to_snake(self, tag): + """ + Convert CamelCase to snake_case + """ + words = re.findall("[A-Z]+[a-z]*", tag) + words_lower = [word.lower() for word in words] + if len(words) > 1: + return "_".join(words_lower) + elif len(words) == 1: + return words_lower[0] + else: + return tag + def load_AZFP_xml(self): - """Parse XML file to get params for reading AZFP data.""" - """Parses the AZFP XML file. + """ + Parses the AZFP XML file. """ xmlmap = fsspec.get_mapper(self.xml_path, **self.storage_options) root = ET.parse(xmlmap.fs.open(xmlmap.root)).getroot() for child in root.iter(): - camel_case_tag = self._to_camelCase(child.tag) + camel_case_tag = self._camel_to_snake(child.tag) if len(child.attrib) > 0: for key, val in child.attrib.items(): - self.parameters[camel_case_tag + "_" + self._to_camelCase(key)].append(val) + self.parameters[camel_case_tag + "_" + self._camel_to_snake(key)].append(val) if all(char == "\n" for char in child.text): continue @@ -117,16 +130,6 @@ def load_AZFP_xml(self): if len(val) == 1: self.parameters[key] = val[0] - def _to_camelCase(self, tag): - words = re.findall("[A-Z]+[a-z]*", tag) - words_lower = [word.lower() for word in words] - if len(words) > 1: - return "_".join(words_lower) - elif len(words) == 1: - return words_lower[0] - else: - return tag - def _compute_temperature(self, ping_num, is_valid): """ Compute temperature in celsius. diff --git a/echopype/tests/convert/test_convert_azfp.py b/echopype/tests/convert/test_convert_azfp.py index 700be5b29..067e1d3fe 100644 --- a/echopype/tests/convert/test_convert_azfp.py +++ b/echopype/tests/convert/test_convert_azfp.py @@ -208,21 +208,19 @@ def test_load_parse_azfp_xml(azfp_path): assert isinstance(parseAZFP.parameters['num_freq'], int) assert isinstance(parseAZFP.parameters['pulse_len'], list) assert parseAZFP.parameters['num_freq'] == 4 - expected_frequency_numbers = ['1', '2', '3', '4'] assert len(parseAZFP.parameters['frequency_number']) == 4 - assert all(x == y for x, y in zip(parseAZFP.parameters['frequency_number'], - expected_frequency_numbers)) + assert parseAZFP.parameters['frequency_number'] == ['1', '2', '3', '4'] + assert parseAZFP.parameters['kHz'] == [125, 200, 455, 769] + expected_len_params = ['acquire_frequency', 'pulse_len', 'dig_rate', 'range_samples', 'range_averaging_samples', 'lock_out_index', 'gain', 'storage_format'] assert all(len(parseAZFP.parameters[x]) == 4 for x in expected_len_params) - assert all(x == 1 for x in parseAZFP.parameters['acquire_frequency']) - assert all(x == 300 for x in parseAZFP.parameters['pulse_len']) - assert all(x == 20000 for x in parseAZFP.parameters['dig_rate']) - expected_range_samples = [1752, 1752, 1764, 540] - assert all(x == y for x, y in zip(parseAZFP.parameters['range_samples'], - expected_range_samples)) - assert all(x == 4 for x in parseAZFP.parameters['range_averaging_samples']) - assert all(x == 0 for x in parseAZFP.parameters['lock_out_index']) - assert all(x == 1 for x in parseAZFP.parameters['gain']) - assert all(x == 1 for x in parseAZFP.parameters['storage_format']) + assert parseAZFP.parameters['acquire_frequency'] == [1, 1, 1, 1] + assert parseAZFP.parameters['pulse_len'] == [300, 300, 300, 300] + assert parseAZFP.parameters['dig_rate'] == [20000, 20000, 20000, 20000] + assert parseAZFP.parameters['range_samples'] == [1752, 1752, 1764, 540] + assert parseAZFP.parameters['range_averaging_samples'] == [4, 4, 4, 4] + assert parseAZFP.parameters['lock_out_index'] == [0, 0, 0, 0] + assert parseAZFP.parameters['gain'] == [1, 1, 1, 1] + assert parseAZFP.parameters['storage_format'] == [1, 1, 1, 1] From f3e6fc004dacb3592a139c30602ac958d66cecf8 Mon Sep 17 00:00:00 2001 From: praneethratna Date: Tue, 19 Sep 2023 00:23:15 +0530 Subject: [PATCH 05/11] updated parameter names --- echopype/convert/parse_azfp.py | 28 ++++++++++++++-------------- echopype/convert/set_groups_azfp.py | 8 ++++---- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/echopype/convert/parse_azfp.py b/echopype/convert/parse_azfp.py index 4c40c87a2..3c6b0aa3d 100644 --- a/echopype/convert/parse_azfp.py +++ b/echopype/convert/parse_azfp.py @@ -27,7 +27,7 @@ ("second", "u2"), # Second ("hundredths", "u2"), # Hundredths of a second ("dig_rate", "u2", 4), # Digitalization rate for each channel - ("lockout_index", "u2", 4), # Lockout index for each channel + ("lock_out_index", "u2", 4), # Lockout index for each channel ("num_bins", "u2", 4), # Number of bins for each channel ( "range_samples_per_bin", @@ -51,7 +51,7 @@ ("num_chan", "u1"), # 1, 2, 3, or 4 ("gain", "u1", 4), # gain channel 1-4 ("spare_chan", "u1"), # spare channel - ("pulse_length", "u2", 4), # Pulse length chan 1-4 uS + ("pulse_len", "u2", 4), # Pulse length chan 1-4 uS ("board_num", "u2", 4), # The board the data came from channel 1-4 ("frequency", "u2", 4), # frequency for channel 1-4 in kHz ( @@ -336,12 +336,12 @@ def _split_header(self, raw, header_unpacked): field_w_freq = ( "dig_rate", - "lockout_index", + "lock_out_index", "num_bins", "range_samples_per_bin", # fields with num_freq data "data_type", "gain", - "pulse_length", + "pulse_len", "board_num", "frequency", ) @@ -399,12 +399,12 @@ def _check_uniqueness(self): # fields with num_freq data field_w_freq = ( "dig_rate", - "lockout_index", + "lock_out_index", "num_bins", "range_samples_per_bin", "data_type", "gain", - "pulse_length", + "pulse_len", "board_num", "frequency", ) @@ -460,22 +460,22 @@ def _get_ping_time(self): self.ping_time = ping_time @staticmethod - def _calc_Sv_offset(f, pulse_length): + def _calc_Sv_offset(f, pulse_len): """Calculate the compensation factor for Sv calculation.""" # TODO: this method seems should be in echopype.process if f > 38000: - if pulse_length == 300: + if pulse_len == 300: return 1.1 - elif pulse_length == 500: + elif pulse_len == 500: return 0.8 - elif pulse_length == 700: + elif pulse_len == 700: return 0.5 - elif pulse_length == 900: + elif pulse_len == 900: return 0.3 - elif pulse_length == 1000: + elif pulse_len == 1000: return 0.3 else: - if pulse_length == 500: + if pulse_len == 500: return 1.1 - elif pulse_length == 1000: + elif pulse_len == 1000: return 0.7 diff --git a/echopype/convert/set_groups_azfp.py b/echopype/convert/set_groups_azfp.py index 34063140b..9c37eec1e 100644 --- a/echopype/convert/set_groups_azfp.py +++ b/echopype/convert/set_groups_azfp.py @@ -320,7 +320,7 @@ def set_beam(self) -> List[xr.Dataset]: del N_tmp tdn = ( - unpacked_data["pulse_length"][self.freq_ind_sorted] / 1e6 + unpacked_data["pulse_len"][self.freq_ind_sorted] / 1e6 ) # Convert microseconds to seconds range_samples_per_bin = unpacked_data["range_samples_per_bin"][ self.freq_ind_sorted @@ -508,7 +508,7 @@ def set_vendor(self) -> xr.Dataset: for ind, ich in enumerate(self.freq_ind_sorted): # TODO: should not access the private function, better to compute Sv_offset in parser Sv_offset[ind] = self.parser_obj._calc_Sv_offset( - self.freq_sorted[ind], unpacked_data["pulse_length"][ich] + self.freq_sorted[ind], unpacked_data["pulse_len"][ich] ) ds = xr.Dataset( @@ -532,9 +532,9 @@ def set_vendor(self) -> xr.Dataset: "A/D converter when digitizing the returned acoustic signal" }, ), - "lockout_index": ( + "lock_out_index": ( ["channel"], - unpacked_data["lockout_index"][self.freq_ind_sorted], + unpacked_data["lock_out_index"][self.freq_ind_sorted], { "long_name": "The distance, rounded to the nearest Bin Size after the " "pulse is transmitted that over which AZFP will ignore echoes" From 26a5c0e8e1cf4efdd47d049440b0e88e50fd1991 Mon Sep 17 00:00:00 2001 From: Emilio Mayorga Date: Mon, 18 Sep 2023 12:20:45 -0700 Subject: [PATCH 06/11] Replace camel-to-snake conversion funcitonality (#1) * Create utils camel-to-snake-case function in new misc.py, and use it in ek_raw_parsers * Replace AZFP camel-to-snake function with new utils/misc.py function --- echopype/convert/parse_azfp.py | 19 +++---------------- echopype/convert/utils/ek_raw_parsers.py | 21 +++------------------ echopype/utils/misc.py | 13 +++++++++++++ 3 files changed, 19 insertions(+), 34 deletions(-) create mode 100644 echopype/utils/misc.py diff --git a/echopype/convert/parse_azfp.py b/echopype/convert/parse_azfp.py index 3c6b0aa3d..bba2c3750 100644 --- a/echopype/convert/parse_azfp.py +++ b/echopype/convert/parse_azfp.py @@ -1,5 +1,4 @@ import os -import re import xml.etree.ElementTree as ET from collections import defaultdict from datetime import datetime as dt @@ -9,6 +8,7 @@ import numpy as np from ..utils.log import _init_logger +from ..utils.misc import camelcase2snakecase from .parse_base import ParseBase FILENAME_DATETIME_AZFP = "\\w+.01A" @@ -85,19 +85,6 @@ def __init__(self, file, params, storage_options={}, dgram_zarr_vars={}): self.unpacked_data = defaultdict(list) self.sonar_type = "AZFP" - def _camel_to_snake(self, tag): - """ - Convert CamelCase to snake_case - """ - words = re.findall("[A-Z]+[a-z]*", tag) - words_lower = [word.lower() for word in words] - if len(words) > 1: - return "_".join(words_lower) - elif len(words) == 1: - return words_lower[0] - else: - return tag - def load_AZFP_xml(self): """ Parses the AZFP XML file. @@ -107,10 +94,10 @@ def load_AZFP_xml(self): root = ET.parse(xmlmap.fs.open(xmlmap.root)).getroot() for child in root.iter(): - camel_case_tag = self._camel_to_snake(child.tag) + camel_case_tag = camelcase2snakecase(child.tag) if len(child.attrib) > 0: for key, val in child.attrib.items(): - self.parameters[camel_case_tag + "_" + self._camel_to_snake(key)].append(val) + self.parameters[camel_case_tag + "_" + camelcase2snakecase(key)].append(val) if all(char == "\n" for char in child.text): continue diff --git a/echopype/convert/utils/ek_raw_parsers.py b/echopype/convert/utils/ek_raw_parsers.py index dae9161a7..8c349a0f2 100644 --- a/echopype/convert/utils/ek_raw_parsers.py +++ b/echopype/convert/utils/ek_raw_parsers.py @@ -16,6 +16,7 @@ import numpy as np from ...utils.log import _init_logger +from ...utils.misc import camelcase2snakecase from .ek_date_conversion import nt_to_unix TCVR_CH_NUM_MATCHER = re.compile(r"\d{6}-\w{1,2}|\w{12}-\w{1,2}") @@ -706,22 +707,6 @@ def _unpack_contents(self, raw_string, bytes_read, version): :returns: None """ - def from_CamelCase(xml_param): - """ - convert name from CamelCase to fit with existing naming convention by - inserting an underscore before each capital and then lowering the caps - e.g. CamelCase becomes camel_case. - """ - idx = list(reversed([i for i, c in enumerate(xml_param) if c.isupper()])) - param_len = len(xml_param) - for i in idx: - # check if we should insert an underscore - if i > 0 and i < param_len: - xml_param = xml_param[:i] + "_" + xml_param[i:] - xml_param = xml_param.lower() - - return xml_param - def dict_to_dict(xml_dict, data_dict, parse_opts): """ dict_to_dict appends the ETree xml value dicts to a provided dictionary @@ -760,13 +745,13 @@ def dict_to_dict(xml_dict, data_dict, parse_opts): data_dict[parse_opts[k][1]] = data else: # add using the default key name wrangling - data_dict[from_CamelCase(k)] = data + data_dict[camelcase2snakecase(k)] = data else: # nothing to do with the value string data = xml_dict[k] # add the parameter to the provided dictionary - data_dict[from_CamelCase(k)] = data + data_dict[camelcase2snakecase(k)] = data header_values = struct.unpack( self.header_fmt(version), raw_string[: self.header_size(version)] diff --git a/echopype/utils/misc.py b/echopype/utils/misc.py new file mode 100644 index 000000000..e6cb8fb11 --- /dev/null +++ b/echopype/utils/misc.py @@ -0,0 +1,13 @@ +def camelcase2snakecase(camel_case_str): + """ + Convert string from CamelCase to snake_case + e.g. CamelCase becomes camel_case. + """ + idx = list(reversed([i for i, c in enumerate(camel_case_str) if c.isupper()])) + param_len = len(camel_case_str) + for i in idx: + # check if we should insert an underscore + if i > 0 and i < param_len: + camel_case_str = camel_case_str[:i] + "_" + camel_case_str[i:] + + return camel_case_str.lower() From d0241b452444e1c926ea2277ca5881f7a6ab91b1 Mon Sep 17 00:00:00 2001 From: praneethratna Date: Tue, 19 Sep 2023 01:05:54 +0530 Subject: [PATCH 07/11] fixed minor error in code --- echopype/convert/parse_azfp.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/echopype/convert/parse_azfp.py b/echopype/convert/parse_azfp.py index bba2c3750..f1ef32bb6 100644 --- a/echopype/convert/parse_azfp.py +++ b/echopype/convert/parse_azfp.py @@ -94,7 +94,10 @@ def load_AZFP_xml(self): root = ET.parse(xmlmap.fs.open(xmlmap.root)).getroot() for child in root.iter(): - camel_case_tag = camelcase2snakecase(child.tag) + if len(child.tag) > 3 and not child.tag.startswith("VTX"): + camel_case_tag = camelcase2snakecase(child.tag) + else: + camel_case_tag = child.tag if len(child.attrib) > 0: for key, val in child.attrib.items(): self.parameters[camel_case_tag + "_" + camelcase2snakecase(key)].append(val) @@ -107,10 +110,7 @@ def load_AZFP_xml(self): except ValueError: val = float(child.text) - if len(child.tag) > 3 and not child.tag.startswith("VTX"): - self.parameters[camel_case_tag].append(val) - else: - self.parameters[child.tag].append(val) + self.parameters[camel_case_tag].append(val) # Handling the case where there is only one value for each parameter for key, val in self.parameters.items(): From 393f05cabdc0f3156723c6def15fefb68f740f04 Mon Sep 17 00:00:00 2001 From: praneethratna Date: Tue, 19 Sep 2023 01:16:37 +0530 Subject: [PATCH 08/11] minor change in test_convert_azfp.py --- echopype/tests/convert/test_convert_azfp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/echopype/tests/convert/test_convert_azfp.py b/echopype/tests/convert/test_convert_azfp.py index 067e1d3fe..4b8f411b6 100644 --- a/echopype/tests/convert/test_convert_azfp.py +++ b/echopype/tests/convert/test_convert_azfp.py @@ -190,7 +190,7 @@ def test_load_parse_azfp_xml(azfp_path): 'C1', 'C2', 'C3', 'D1', 'D2', 'T1', 'T2', 'T3', 'T4', 'T5', 'X_a', 'X_b', 'X_c', 'X_d', 'Y_a', 'Y_b', 'Y_c', 'Y_d', 'period', 'ppm_offset', 'calibration', 'a0', 'a1', 'a2', 'a3', 'ka', 'kb', 'kc', 'A', 'B', 'C', - 'num_freq', 'hz_units', 'kHz', 'TVR', 'num_vtx', 'VTX0', 'VTX1', 'VTX2', + 'num_freq', 'kHz_units', 'kHz', 'TVR', 'num_vtx', 'VTX0', 'VTX1', 'VTX2', 'VTX3', 'BP', 'EL', 'DS', 'min_pulse_len', 'sound_speed', 'start_date_svalue', 'start_date', 'num_frequencies', 'num_phases', 'data_output_svalue', 'data_output', 'frequency_units', 'frequency', From 945a7c5cf8235b423434a23034fee63cf30cde5e Mon Sep 17 00:00:00 2001 From: praneethratna Date: Tue, 19 Sep 2023 01:31:15 +0530 Subject: [PATCH 09/11] fixed failing tests --- echopype/calibrate/api.py | 4 ++-- echopype/calibrate/cal_params.py | 2 +- echopype/calibrate/calibrate_azfp.py | 2 +- echopype/echodata/sensor_ep_version_mapping/v05x_to_v06x.py | 5 ++++- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/echopype/calibrate/api.py b/echopype/calibrate/api.py index 01fa7630a..c2c34c137 100644 --- a/echopype/calibrate/api.py +++ b/echopype/calibrate/api.py @@ -153,7 +153,7 @@ def compute_Sv(echodata: EchoData, **kwargs) -> xr.Dataset: - for EK60 echosounder, allowed parameters include: `"sa_correction"`, `"gain_correction"`, `"equivalent_beam_angle"` - for AZFP echosounder, allowed parameters include: - `"EL"`, `"DS"`, `"TVR"`, `"VTX"`, `"equivalent_beam_angle"`, `"Sv_offset"` + `"EL"`, `"DS"`, `"TVR"`, `"VTX0"`, `"equivalent_beam_angle"`, `"Sv_offset"` Passing in calibration parameters for other echosounders are not currently supported. @@ -242,7 +242,7 @@ def compute_TS(echodata: EchoData, **kwargs): - for EK60 echosounder, allowed parameters include: `"sa_correction"`, `"gain_correction"`, `"equivalent_beam_angle"` - for AZFP echosounder, allowed parameters include: - `"EL"`, `"DS"`, `"TVR"`, `"VTX"`, `"equivalent_beam_angle"`, `"Sv_offset"` + `"EL"`, `"DS"`, `"TVR"`, `"VTX0"`, `"equivalent_beam_angle"`, `"Sv_offset"` Passing in calibration parameters for other echosounders are not currently supported. diff --git a/echopype/calibrate/cal_params.py b/echopype/calibrate/cal_params.py index 0c861c9e9..06736cfd3 100644 --- a/echopype/calibrate/cal_params.py +++ b/echopype/calibrate/cal_params.py @@ -29,7 +29,7 @@ "impedance_transceiver", # z_er "receiver_sampling_frequency", ), - "AZFP": ("EL", "DS", "TVR", "VTX", "equivalent_beam_angle", "Sv_offset"), + "AZFP": ("EL", "DS", "TVR", "VTX0", "equivalent_beam_angle", "Sv_offset"), } EK80_DEFAULT_PARAMS = { diff --git a/echopype/calibrate/calibrate_azfp.py b/echopype/calibrate/calibrate_azfp.py index bc2f848f0..1299be8bb 100644 --- a/echopype/calibrate/calibrate_azfp.py +++ b/echopype/calibrate/calibrate_azfp.py @@ -63,7 +63,7 @@ def _cal_power_samples(self, cal_type, **kwargs): # TODO: take care of dividing by zero encountered in log10 spreading_loss = 20 * np.log10(self.range_meter) absorption_loss = 2 * self.env_params["sound_absorption"] * self.range_meter - SL = self.cal_params["TVR"] + 20 * np.log10(self.cal_params["VTX"]) # eq.(2) + SL = self.cal_params["TVR"] + 20 * np.log10(self.cal_params["VTX0"]) # eq.(2) # scaling factor (slope) in Fig.G-1, units Volts/dB], see p.84 a = self.cal_params["DS"] diff --git a/echopype/echodata/sensor_ep_version_mapping/v05x_to_v06x.py b/echopype/echodata/sensor_ep_version_mapping/v05x_to_v06x.py index 8053aa94a..ea9fa6bf0 100644 --- a/echopype/echodata/sensor_ep_version_mapping/v05x_to_v06x.py +++ b/echopype/echodata/sensor_ep_version_mapping/v05x_to_v06x.py @@ -700,7 +700,10 @@ def _rearrange_azfp_attrs_vars(ed_obj, sensor): "DS", "EL", "TVR", - "VTX", + "VTX0", + "VTX1", + "VTX2", + "VTX3", "Sv_offset", "number_of_samples_digitized_per_pings", "number_of_digitized_samples_averaged_per_pings", From 6f5bf1ee3ec0f9a28c23b2e1692ee8dee554475b Mon Sep 17 00:00:00 2001 From: praneethratna Date: Tue, 19 Sep 2023 01:47:51 +0530 Subject: [PATCH 10/11] fixed failing tests related to attribute name --- echopype/calibrate/range.py | 2 +- echopype/echodata/sensor_ep_version_mapping/v05x_to_v06x.py | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/echopype/calibrate/range.py b/echopype/calibrate/range.py index aa93a6061..b3c049c5f 100644 --- a/echopype/calibrate/range.py +++ b/echopype/calibrate/range.py @@ -60,7 +60,7 @@ def compute_range_AZFP(echodata: EchoData, env_params: Dict, cal_type: str) -> x # Notation below follows p.86 of user manual N = vend["number_of_samples_per_average_bin"] # samples per bin f = vend["digitization_rate"] # digitization rate - L = vend["lockout_index"] # number of lockout samples + L = vend["lock_out_index"] # number of lockout samples # keep this in ref of AZFP matlab code, # set to 1 since we want to calculate from raw data diff --git a/echopype/echodata/sensor_ep_version_mapping/v05x_to_v06x.py b/echopype/echodata/sensor_ep_version_mapping/v05x_to_v06x.py index ea9fa6bf0..8053aa94a 100644 --- a/echopype/echodata/sensor_ep_version_mapping/v05x_to_v06x.py +++ b/echopype/echodata/sensor_ep_version_mapping/v05x_to_v06x.py @@ -700,10 +700,7 @@ def _rearrange_azfp_attrs_vars(ed_obj, sensor): "DS", "EL", "TVR", - "VTX0", - "VTX1", - "VTX2", - "VTX3", + "VTX", "Sv_offset", "number_of_samples_digitized_per_pings", "number_of_digitized_samples_averaged_per_pings", From 1aaa73ff33f4fb6730851083496ff78e37561ac4 Mon Sep 17 00:00:00 2001 From: praneethratna Date: Tue, 19 Sep 2023 02:02:49 +0530 Subject: [PATCH 11/11] fixed minor bug --- echopype/calibrate/cal_params.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/echopype/calibrate/cal_params.py b/echopype/calibrate/cal_params.py index 06736cfd3..5d020cc9b 100644 --- a/echopype/calibrate/cal_params.py +++ b/echopype/calibrate/cal_params.py @@ -352,7 +352,7 @@ def get_cal_params_AZFP(beam: xr.DataArray, vend: xr.DataArray, user_dict: dict) out_dict[p] = beam[p] # has only channel dim # Params from Vendor_specific group - elif p in ["EL", "DS", "TVR", "VTX", "Sv_offset"]: + elif p in ["EL", "DS", "TVR", "VTX0", "Sv_offset"]: out_dict[p] = vend[p] # these params only have the channel dimension return out_dict