88import numpy as np
99import xarray as xr
1010
11+ from ..utils .log import _init_logger
1112from ..calibrate .ek80_complex import compress_pulse , get_norm_fac , get_transmit_signal
1213
1314
15+ logger = _init_logger (__name__ )
16+
17+ # Beam type identifiers
18+ BEAM_TYPE_SPLIT_4_SECTOR = 1 # 4-sector split-beam (common Simrad type)
19+ BEAM_TYPE_SPLIT_3_SECTOR = 17 # 3-sector
20+ BEAM_TYPE_SPLIT_3_PLUS_CENTER = 49 # 3-sector + center element
21+ BEAM_TYPE_SPLIT_VARIANT_65 = 65 # Another 3+1 variant (vendor-specific)
22+ BEAM_TYPE_SPLIT_VARIANT_81 = 81 # Another 3+1 variant (vendor-specific)
23+
24+ SUPPORTED_BEAM_TYPES = [
25+ BEAM_TYPE_SPLIT_4_SECTOR ,
26+ BEAM_TYPE_SPLIT_3_SECTOR ,
27+ BEAM_TYPE_SPLIT_3_PLUS_CENTER ,
28+ BEAM_TYPE_SPLIT_VARIANT_65 ,
29+ BEAM_TYPE_SPLIT_VARIANT_81 ,
30+ ]
31+
32+
1433def _compute_angle_from_complex (
1534 bs : xr .DataArray , beam_type : int , sens : List [xr .DataArray ], offset : List [xr .DataArray ]
1635) -> Tuple [xr .DataArray , xr .DataArray ]:
@@ -48,7 +67,7 @@ def _compute_angle_from_complex(
4867 """
4968
5069 # 4-sector transducer
51- if beam_type == 1 :
70+ if beam_type == BEAM_TYPE_SPLIT_4_SECTOR :
5271 bs_fore = (bs .isel (beam = 2 ) + bs .isel (beam = 3 )) / 2 # forward
5372 bs_aft = (bs .isel (beam = 0 ) + bs .isel (beam = 1 )) / 2 # aft
5473 bs_star = (bs .isel (beam = 0 ) + bs .isel (beam = 3 )) / 2 # starboard
@@ -60,9 +79,14 @@ def _compute_angle_from_complex(
6079 phi = np .arctan2 (np .imag (bs_phi ), np .real (bs_phi )) / np .pi * 180
6180
6281 # 3-sector transducer with or without center element
63- elif beam_type in [17 , 49 , 65 , 81 ]:
82+ elif beam_type in [
83+ BEAM_TYPE_SPLIT_3_SECTOR ,
84+ BEAM_TYPE_SPLIT_3_PLUS_CENTER ,
85+ BEAM_TYPE_SPLIT_VARIANT_65 ,
86+ BEAM_TYPE_SPLIT_VARIANT_81 ,
87+ ]:
6488 # 3-sector
65- if beam_type == 17 :
89+ if beam_type == BEAM_TYPE_SPLIT_3_SECTOR :
6690 bs_star = bs .isel (beam = 0 )
6791 bs_port = bs .isel (beam = 1 )
6892 bs_fore = bs .isel (beam = 2 )
@@ -209,8 +233,14 @@ def get_angle_complex_samples(
209233 )
210234 else :
211235 # beam_type different for some channels, process each channel separately
212- theta , phi = [], []
236+ theta_list , phi_list , valid_channels = [], [], []
213237 for ch_id in bs ["channel" ].data :
238+ beam_type_ch = ds_beam ["beam_type" ].sel (channel = ch_id ).item ()
239+
240+ if beam_type_ch not in SUPPORTED_BEAM_TYPES :
241+ logger .warning (f"Skipping channel { ch_id } : unsupported beam_type { beam_type_ch } " )
242+ continue
243+
214244 theta_ch , phi_ch = _compute_angle_from_complex (
215245 bs = bs .sel (channel = ch_id ),
216246 # beam_type is not time-varying
@@ -224,25 +254,18 @@ def get_angle_complex_samples(
224254 angle_params ["angle_offset_athwartship" ].sel (channel = ch_id ),
225255 ],
226256 )
227- theta .append (theta_ch )
228- phi .append (phi_ch )
257+ theta_list .append (theta_ch )
258+ phi_list .append (phi_ch )
259+ valid_channels .append (ch_id )
260+
261+ if not theta_list :
262+ raise ValueError ("No valid channels found for angle computation." )
229263
230264 # Combine angles from all channels
231- theta = xr .DataArray (
232- data = theta ,
233- coords = {
234- "channel" : bs ["channel" ],
235- "ping_time" : bs ["ping_time" ],
236- "range_sample" : bs ["range_sample" ],
237- },
238- )
239- phi = xr .DataArray (
240- data = phi ,
241- coords = {
242- "channel" : bs ["channel" ],
243- "ping_time" : bs ["ping_time" ],
244- "range_sample" : bs ["range_sample" ],
245- },
246- )
265+ theta = xr .concat (theta_list , dim = "channel" )
266+ theta = theta .assign_coords (channel = ("channel" , valid_channels ))
267+
268+ phi = xr .concat (phi_list , dim = "channel" )
269+ phi = phi .assign_coords (channel = ("channel" , valid_channels ))
247270
248271 return theta , phi
0 commit comments