Skip to content

AZFP ULS6 beta version, ULS5 fixes#1581

Open
dash-uvic wants to merge 12 commits into
echostack-org:mainfrom
dash-uvic:main
Open

AZFP ULS6 beta version, ULS5 fixes#1581
dash-uvic wants to merge 12 commits into
echostack-org:mainfrom
dash-uvic:main

Conversation

@dash-uvic

Copy link
Copy Markdown
Contributor

ULS5 Bug fixes:

  • fixed recursive bug for corrupted files
  • fixed RepeatedField not applying correctly
    ULS6 updates:
  • Added AZFP Nano (single channel) compatibility
  • Added compatibility for beta version of ULS6 standard
  • Refractored code to inherit ULS5 as a base class
  • Added additional tests for ULS6 to include Paros sensors

asldash and others added 2 commits November 21, 2025 11:58
…earlier .azfp extensions. Fixed RepeatedField bug and recursive bug loading corrupted files. Added support for single-channel AZFP models. Added additional tests for AZFP6 and latest .asp6 data files
@leewujung

leewujung commented Dec 2, 2025

Copy link
Copy Markdown
Member

I haven't gotten around to upload the test data to GitHub release assets just yet. Traveling tomorrow, so will try Wednesday...

@leewujung leewujung mentioned this pull request Dec 6, 2025
@leewujung

Copy link
Copy Markdown
Member

Hey @dash-uvic : I got the test data you sent me as a staged asset in #1582. It’s merged so if you pull from main now you should have all the right files.

@leewujung leewujung closed this Dec 6, 2025
@leewujung leewujung reopened this Dec 6, 2025
@codecov-commenter

codecov-commenter commented Dec 6, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.03748% with 35 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.06%. Comparing base (6cf6cee) to head (6a7519b).
⚠️ Report is 37 commits behind head on main.

Files with missing lines Patch % Lines
echopype/convert/parse_uls6.py 89.43% 26 Missing ⚠️
echopype/convert/parse_azfp.py 92.06% 5 Missing ⚠️
echopype/convert/parse_uls5.py 97.22% 4 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1581      +/-   ##
==========================================
+ Coverage   85.58%   86.06%   +0.48%     
==========================================
  Files          79       79              
  Lines        6998     7084      +86     
==========================================
+ Hits         5989     6097     +108     
+ Misses       1009      987      -22     
Flag Coverage Δ
integration 80.98% <89.94%> (+0.34%) ⬆️
unit 61.09% <52.29%> (+0.67%) ⬆️
unittests 85.96% <94.03%> (+0.48%) ⬆️

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

☔ View full report in Codecov by Sentry.
📢 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.

@leewujung

Copy link
Copy Markdown
Member

@dash-uvic: I pulled the update assets in and the current tests all passed. Are there additional tests or code you want to add?

@leewujung leewujung left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dash-uvic : Thanks for the PR! I went through the changes quickly, and have one more substantial suggestion and a couple small ones below.

I think it'd be better from the maintenance point of view to have a parent class parse_AZFP and have 2 child classes, one parse_ULS5 and the other parse_ULS6. What you have under the current parse_AZFP (largely from existing code) can largely stay in this parent class, with both child classes being pretty slim with only the differences on the header and XLM handling.

Small comments:

  • I don't see this constant used in the ULS6 code: FILENAME_DATETIME_AZFP = "\\w+_\\w+.[azfp|aps6]". Could you confirm? This is linked to the parent class suggestion above. It'll be cleaner to have all shared constants in the parent class and only the differences showing up in the child class. We did the same thing for the EK classes.
  • There are these couple lines in multiple functions that I think are redundant, since the parse object is called from convert/api.py and will ALWAYS execute parse.parse_raw() for all sonar models.
    if not self.unpacked_data:
      self.parse_raw()
    As you make changes re the parent/child classes, could you also remove these?

@dash-uvic

Copy link
Copy Markdown
Contributor Author

@leewujung I've done the suggested changes and included comments from [1505] and moved the Sv_offset to calibration. Just need to write a test for the new Sv interpolation and I'll push.

@leewujung

Copy link
Copy Markdown
Member

@dash-uvic : Awesome. Looking forward to it!

…alibrate and added calibration tests. Now Sv_offset attempts interpolation for non-defined Freq/PulseLen and returns 0.0 with a warning otherwise
@LOCEANlloydizard

Copy link
Copy Markdown
Collaborator

Hi @dash-uvic, the error currently raised in the tests is fixed in #1591, so fetching the latest from upstream should resolve it!
Cheers!

@leewujung

Copy link
Copy Markdown
Member

@dash-uvic: I pulled in the upstream and everything runs through fine, so you should be good to go on adding additional tests for Sv offset.

@dash-uvic

Copy link
Copy Markdown
Contributor Author

Sorry just got back from vacation. I just did a pull and ran the tests and everything looks good (Sv offset tests were in the last push). I did a minor code cleanup push just now to remove a few commented out asserts.

Comment thread echopype/calibrate/cal_params.py Outdated
@leewujung

leewujung commented Jun 6, 2026

Copy link
Copy Markdown
Member

@dash-uvic : I have asked @LOCEANlloydizard to help review this PR, so that I don't continue to be the bottleneck. 😵

@LOCEANlloydizard : there are some discussions in #1505 that @dash-uvic has included in this PR, in addition to refactoring the AZFP ULS data classes. Thanks again for taking this on!

LOCEANlloydizard and others added 2 commits June 11, 2026 16:30
Co-authored-by: Wu-Jung Lee <leewujung@gmail.com>

@LOCEANlloydizard LOCEANlloydizard left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @dash-uvic, thanks for the PR! I merged the small conflict, but I don't have much more to add beyond the inline comments! Overall this looks good to me and address the earlier discussion around moving to a shared AZFP base class with ULS5/ULS6 implementations! The test coverage also looks good, with the new integration tests against files and Matlab outputs (don't know if we want some unit tests?)

I was wondering about the Sv_offset interpolation: from the discussion in #1505 I understood the main goal as handling pulse lengths that were not explicitly listed in the lookup table (and possibly extrapolating ?). The examples discussed there seemed to propose 1D interpolation over pulse length, whereas the implementation use a 2D interpolation across both frequency and pulse length, with no extrapolation outside the interpolation grid? just to make sure!

Other than that my comments are mostly small cleanup questions, and once those are clarified this looks good to me! Cheers!



@pytest.mark.skip(reason="required pulse length not in Sv offset dictionary")
#@pytest.mark.skip(reason="required pulse length not in Sv offset dictionary")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we remove the line and not comment?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah we can remove the line as its just a leftover from using the old Sv offset dictionary.

Comment thread echopype/calibrate/calibrate_azfp.py Outdated
# Check if outside of calibration grid
if np.isnan(zq):
raise ValueError(
f"Pulse lengths less than 150 or greater than 1000 usecs for {frequency}kHz "

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be in Hz and not kHz?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes your right that should be Hz.

except ValueError:
logger.warning(
f"The Sv for {freq}kHz and pulse length {pulse_len}us "
"is uncalibrated (Sv_offset=0.0)"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

units ok too?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be Hz as well.

Comment thread echopype/convert/parse_azfp.py Outdated
self.unpacked_data["data_type"][ping_num] = [self.unpacked_data["data_type"][ping_num]]
self.unpacked_data["range_samples_per_bin"][ping_num] = [
self.unpacked_data["range_samples_per_bin"][ping_num]
]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would it make sense to normalize these fields in _split_header() when num_chan == 1?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I stuck it in there since _split_header is different between parse_uls5/parse_ulse6 and didn't make sense to duplicate code there twice. But if it makes more sense in the _split_header we could put it there, and if would make sense if the ULS6 spec ever changed its behaviour with single frequency AZFPs.

Comment thread echopype/convert/parse_uls5.py Outdated
sonar_model="AZFP",
**kwargs,
):
super().__init__(file, storage_options, storage_options, sonar_model)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

twice?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

replace first occurrence with file_meta

assert np.allclose(
ds_matlab_output['Output']['BatteryTx'][0][0].squeeze(),
echodata["Vendor_specific"].battery_tx,
rtol=1e-08

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we remove test for battery?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nevermind, saw it in test_convert_azfp_battery

@dash-uvic

Copy link
Copy Markdown
Contributor Author

@LOCEANlloydizard I discussed it with our in-house AZFP expert since acoustics are not my area and this seemed to be an reasonable approach. Outside the interpolation grid the AZFP/transducers don't operate properly anyway. Do i need to make these edits or do you plan to? I can get them done today if I do.

@LOCEANlloydizard

Copy link
Copy Markdown
Collaborator

Hi @dash-uvic, ok nice thank you! Yes, if you don't mind making the changes after pulling the updated version (following the merge, etc.), then we can take a look from there! Thanks again!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants