Skip to content

Commit 59289da

Browse files
committed
Fixed test and added a test data download utility
1 parent 3c914c3 commit 59289da

File tree

3 files changed

+89
-31
lines changed

3 files changed

+89
-31
lines changed

echopype/consolidate/split_beam_angle.py

Lines changed: 35 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
Contains functions necessary to compute the split-beam (alongship/athwartship)
33
angles and add them to a Dataset.
44
"""
5-
65
from typing import List, Tuple
76

7+
import dask.array as da
88
import numpy as np
99
import xarray as xr
1010

@@ -111,8 +111,12 @@ def _compute_angle_from_complex(
111111
else:
112112
raise ValueError("beam_type not recognized!")
113113

114-
theta = theta / sens[0] - offset[0]
115-
phi = phi / sens[1] - offset[1]
114+
if isinstance(sens[0].data, da.Array):
115+
theta = theta / float(sens[0].values) - float(offset[0].values)
116+
phi = phi / float(sens[1].values) - float(offset[1].values)
117+
else:
118+
theta = theta / sens[0] - offset[0]
119+
phi = phi / sens[1] - offset[1]
116120

117121
return theta, phi
118122

@@ -234,39 +238,46 @@ def get_angle_complex_samples(
234238
else:
235239
# beam_type different for some channels, process each channel separately
236240
theta_list, phi_list, valid_channels = [], [], []
237-
beam_types = ds_beam["beam_type"].values
238-
239-
for idx, ch_id in enumerate(bs["channel"].data):
240-
beam_type_ch = beam_types[idx]
241-
242-
if beam_type_ch not in SUPPORTED_BEAM_TYPES:
243-
logger.warning(f"Skipping channel {ch_id}: unsupported beam_type {beam_type_ch}")
241+
for ch_id in bs["channel"].data:
242+
beam_type = ds_beam["beam_type"].sel(channel=ch_id)
243+
beam_type = int(beam_type)
244+
if beam_type not in SUPPORTED_BEAM_TYPES:
245+
logger.warning(f"Skipping channel {ch_id}: unsupported beam_type {beam_type}")
244246
continue
245247

246248
theta_ch, phi_ch = _compute_angle_from_complex(
247-
bs=bs.isel(channel=idx),
248-
beam_type=beam_type_ch,
249+
bs=bs.sel(channel=ch_id),
250+
# beam_type is not time-varying
251+
beam_type=beam_type,
249252
sens=[
250-
float(angle_params["angle_sensitivity_alongship"].isel(channel=idx).values),
251-
float(angle_params["angle_sensitivity_athwartship"].isel(channel=idx).values),
253+
angle_params["angle_sensitivity_alongship"].sel(channel=ch_id),
254+
angle_params["angle_sensitivity_athwartship"].sel(channel=ch_id),
252255
],
253256
offset=[
254-
float(angle_params["angle_offset_alongship"].isel(channel=idx).values),
255-
float(angle_params["angle_offset_athwartship"].isel(channel=idx).values),
257+
angle_params["angle_offset_alongship"].sel(channel=ch_id),
258+
angle_params["angle_offset_athwartship"].sel(channel=ch_id),
256259
],
257260
)
258261
theta_list.append(theta_ch)
259262
phi_list.append(phi_ch)
260263
valid_channels.append(ch_id)
261264

262-
if not theta_list:
263-
raise ValueError("No valid channels found for angle computation.")
264-
265265
# Combine angles from all channels
266-
theta = xr.concat(theta_list, dim="channel")
267-
theta = theta.assign_coords(channel=("channel", valid_channels))
268-
269-
phi = xr.concat(phi_list, dim="channel")
270-
phi = phi.assign_coords(channel=("channel", valid_channels))
266+
theta = xr.DataArray(
267+
data=theta_list,
268+
coords={
269+
"channel": valid_channels,
270+
"ping_time": bs["ping_time"],
271+
"range_sample": bs["range_sample"],
272+
},
273+
)
274+
phi = xr.DataArray(
275+
data=phi_list,
276+
coords={
277+
"channel": valid_channels,
278+
"ping_time": bs["ping_time"],
279+
"range_sample": bs["range_sample"],
280+
},
281+
)
271282

272283
return theta, phi

echopype/tests/consolidate/test_consolidate_integration.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -341,16 +341,12 @@ def test_add_splitbeam_angle_partial_valid_channels(test_path):
341341

342342
# Manually override beam_type for one channel to simulate single-beam
343343
beam_types = ed["Sonar/Beam_group1"]["beam_type"].values
344-
total_channels = beam_types.shape[0]
345-
valid_channels = 0
346344
channel_to_invalidate = None
347345

348346
# Force the first channel to an unsupported beam_type
349347
for idx, bt in enumerate(beam_types):
350-
if bt != 0:
351-
valid_channels += 1
352-
if channel_to_invalidate is None:
353-
channel_to_invalidate = idx
348+
if bt != 0 and channel_to_invalidate is None:
349+
channel_to_invalidate = idx
354350

355351
# Simulate disabling one valid channel
356352
beam_types[channel_to_invalidate] = 0
@@ -363,7 +359,7 @@ def test_add_splitbeam_angle_partial_valid_channels(test_path):
363359
ds_Sv = ep.consolidate.add_splitbeam_angle(source_Sv=ds_Sv, echodata=ed, waveform_mode="CW",
364360
encode_mode="complex", pulse_compression=False, to_disk=False)
365361

366-
expected_valid_channels = valid_channels - 1
362+
expected_valid_channels = len(ds_Sv.channel.values) - 1
367363

368364
valid_angle_channels = [
369365
ch for ch in ds_Sv.channel.values
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import subprocess
2+
import sys
3+
from pathlib import Path
4+
5+
CONTAINER_NAME = "echopype-test-http-server"
6+
DOCKER_IMAGE = "cormorack/http"
7+
8+
9+
def check_docker():
10+
try:
11+
subprocess.run(["docker", "--version"], check=True, stdout=subprocess.DEVNULL)
12+
subprocess.run(["docker", "info"], check=True, stdout=subprocess.DEVNULL)
13+
except subprocess.CalledProcessError:
14+
print("Error: Docker is not installed or the daemon is not running.")
15+
sys.exit(1)
16+
17+
18+
def run_container():
19+
subprocess.run(["docker", "run", "-d", "--name", CONTAINER_NAME, DOCKER_IMAGE], check=True)
20+
21+
22+
def copy_test_data(destination: Path):
23+
destination.mkdir(parents=True, exist_ok=True)
24+
subprocess.run([
25+
"docker", "cp", "-L",
26+
f"{CONTAINER_NAME}:/usr/local/apache2/htdocs/data/.",
27+
str(destination)
28+
], check=True)
29+
30+
31+
def cleanup_container():
32+
subprocess.run(["docker", "rm", "-f", CONTAINER_NAME], check=True)
33+
34+
35+
def main():
36+
script_dir = Path(__file__).resolve().parent
37+
destination_dir = script_dir.parent / "test_data"
38+
check_docker()
39+
run_container()
40+
41+
print(f"Copying test data to {destination_dir} ...")
42+
copy_test_data(destination_dir)
43+
44+
print("Cleaning up container...")
45+
cleanup_container()
46+
47+
print(f"Test data downloaded successfully to {destination_dir}")
48+
49+
50+
if __name__ == "__main__":
51+
main()

0 commit comments

Comments
 (0)