5050from mhkit .dolfyn .time import epoch2dt64 , dt642epoch
5151
5252
53+ def _check_numeric (value , name : str ):
54+ if np .issubdtype (type (value ), np .ndarray ):
55+ value = value .item ()
56+ if not (
57+ isinstance (value , (int , float ))
58+ or np .issubdtype (type (value ), np .integer )
59+ or np .issubdtype (type (value ), np .floating )
60+ ):
61+ raise TypeError (f"{ name } must be a numeric type (int or float)." )
62+
63+
5364def _fmax_warning (
5465 fn : Union [int , float , np .ndarray ], fmax : Union [int , float , np .ndarray ]
5566) -> Union [int , float , np .ndarray ]:
@@ -69,11 +80,6 @@ def _fmax_warning(
6980 The adjusted maximum frequency limit, ensuring it does not exceed the Nyquist frequency.
7081 """
7182
72- if not isinstance (fn , (int , float , np .ndarray )):
73- raise TypeError ("'fn' must be a numeric type (int or float)." )
74- if not isinstance (fmax , (int , float , np .ndarray )):
75- raise TypeError ("'fmax' must be a numeric type (int or float)." )
76-
7783 if fmax > fn :
7884 warnings .warn (
7985 f"`fmax` = { fmax } is greater than the Nyquist frequency. Setting"
@@ -120,10 +126,8 @@ def minimum_frequency(
120126 if not np .issubdtype (water_depth .dtype , np .number ):
121127 raise TypeError ("'water_depth' must be a numeric type or array of numerics." )
122128
123- if not isinstance (c , (int , float )):
124- raise TypeError ("'c' must be a numeric type (int or float)." )
125- if not isinstance (c_seabed , (int , float )):
126- raise TypeError ("'c_seabed' must be a numeric type (int or float)." )
129+ _check_numeric (c , "c" )
130+ _check_numeric (c_seabed , "c_seabed" )
127131
128132 if np .any (water_depth <= 0 ):
129133 raise ValueError ("All elements of 'water_depth' must be positive numbers." )
@@ -143,6 +147,7 @@ def sound_pressure_spectral_density(
143147 pressure : xr .DataArray ,
144148 fs : Union [int , float ],
145149 bin_length : Union [int , float ] = 1 ,
150+ fft_length : Optional [Union [int , float ]] = None ,
146151 rms : bool = True ,
147152) -> xr .DataArray :
148153 """
@@ -167,6 +172,8 @@ def sound_pressure_spectral_density(
167172 Data collection sampling rate [Hz]
168173 bin_length: int or float
169174 Length of time in seconds to create FFTs. Default: 1.
175+ fft_length: int or float, optional
176+ Length of FFT to use. If None, uses bin_length * fs. Default: None.
170177 rms: bool
171178 If True, calculates the mean-squared SPSD. Set to False to
172179 calculate standard SPSD. Default: True.
@@ -180,20 +187,23 @@ def sound_pressure_spectral_density(
180187 # Type checks
181188 if not isinstance (pressure , xr .DataArray ):
182189 raise TypeError ("'pressure' must be an xarray.DataArray." )
183- if not isinstance (fs , (int , float )):
184- raise TypeError ("'fs' must be a numeric type (int or float)." )
185- if not isinstance (bin_length , (int , float )):
186- raise TypeError ("'bin_length' must be a numeric type (int or float)." )
190+ _check_numeric (fs , "fs" )
191+ _check_numeric (bin_length , "bin_length" )
187192
188193 # Ensure that 'pressure' has a 'time' coordinate
189194 if "time" not in pressure .dims :
190195 raise ValueError ("'pressure' must be indexed by 'time' dimension." )
191196
192197 # window length of each time series
193198 nbin = bin_length * fs
199+ if fft_length is not None :
200+ _check_numeric (fft_length , "fft_length" )
201+ nfft = fft_length
202+ else :
203+ nfft = nbin
194204
195205 # Use dolfyn PSD functionality
196- binner = VelBinner (n_bin = nbin , fs = fs , n_fft = nbin )
206+ binner = VelBinner (n_bin = nbin , fs = fs , n_fft = nfft )
197207 # Always 50% overlap if numbers reshape perfectly
198208 # Mean square sound pressure
199209 psd = binner .power_spectral_density (pressure , freq_units = "Hz" )
@@ -221,9 +231,9 @@ def sound_pressure_spectral_density(
221231 "units" : pressure .units + "^2/Hz" ,
222232 "long_name" : long_name ,
223233 "fs" : fs ,
224- "nbin " : str ( bin_length ) + " s" ,
234+ "bin_length " : bin_length ,
225235 "overlap" : "50%" ,
226- "nfft " : nbin ,
236+ "n_fft " : nfft ,
227237 },
228238 )
229239
@@ -234,6 +244,7 @@ def apply_calibration(
234244 spsd : xr .DataArray ,
235245 sensitivity_curve : xr .DataArray ,
236246 fill_value : Union [float , int , np .ndarray ],
247+ interp_method : str = "linear" ,
237248) -> xr .DataArray :
238249 """
239250 Applies custom calibration to spectral density values.
@@ -248,6 +259,9 @@ def apply_calibration(
248259 fill_value: float or int
249260 Value with which to fill missing values from the calibration curve,
250261 in units of dB rel 1 V^2/uPa^2.
262+ interp_method: str
263+ Interpolation method to use when interpolating the calibration curve
264+ to the frequencies in 'spsd'. Default is 'linear'.
251265
252266 Returns
253267 -------
@@ -259,8 +273,7 @@ def apply_calibration(
259273 raise TypeError ("'spsd' must be an xarray.DataArray." )
260274 if not isinstance (sensitivity_curve , xr .DataArray ):
261275 raise TypeError ("'sensitivity_curve' must be an xarray.DataArray." )
262- if not isinstance (fill_value , (int , float , np .ndarray )):
263- raise TypeError ("'fill_value' must be a numeric type (int or float)." )
276+ _check_numeric (fill_value , "fill_value" )
264277
265278 # Ensure 'freq' dimension exists in 'spsd'
266279 if "freq" not in spsd .dims :
@@ -295,7 +308,7 @@ def apply_calibration(
295308 freq = sensitivity_curve .dims [0 ]
296309 # Interpolate calibration curve to desired value
297310 calibration = sensitivity_curve .interp (
298- {freq : spsd_calibrated ["freq" ]}, method = "linear"
311+ {freq : spsd_calibrated ["freq" ]}, method = interp_method
299312 )
300313 # Fill missing with provided value
301314 calibration = calibration .fillna (fill_value )
@@ -340,6 +353,7 @@ def sound_pressure_spectral_density_level(spsd: xr.DataArray) -> xr.DataArray:
340353 attrs = {
341354 "units" : "dB re 1 uPa^2/Hz" ,
342355 "long_name" : "Sound Pressure Spectral Density Level" ,
356+ "time_resolution" : spsd .attrs ["bin_length" ],
343357 },
344358 )
345359
@@ -571,8 +585,8 @@ def band_aggregate(
571585 for val in octave :
572586 if not isinstance (val , int ) or (val <= 0 ):
573587 raise TypeError ("'octave' must contain positive integers." )
574- if not isinstance (fmin , int ) or ( fmin <= 0 ):
575- raise TypeError ( "'fmin' must be a positive integer. " )
588+ _check_numeric (fmin , " fmin" )
589+ _check_numeric ( fmax , "fmax " )
576590 if fmax <= fmin : # also checks that fmax is positive
577591 raise ValueError ("'fmax' must be greater than 'fmin'." )
578592
0 commit comments