def convert(values, unit1, unit2): """Converts values from unit1 to unit2 Parameters ---------- values: ndarray Values of the field to convert unit1: str start unit unit2: str final unit Returns ------- ndarray of the converted field """ unit1_save = unit1 unit2_save = unit2 # Format the strings unit1 = unit1.replace("*", "").replace(" ", "").replace("^", "") unit2 = unit2.replace("*", "").replace(" ", "").replace("^", "") # Unit1 parsing if "/" in unit1: dim1_denom, prefix1_denom = get_dim_prefix(unit1.split("/")[1]) unit1 = unit1.split("/")[0] else: dim1_denom = [0, 0, 0, 0, 0, 0] prefix1_denom = 1.0 dim1_num, prefix1_num = get_dim_prefix(unit1) # Unit2 parsing if "/" in unit2: dim2_denom, prefix2_denom = get_dim_prefix(unit2.split("/")[1]) unit2 = unit2.split("/")[0] else: dim2_denom = [0, 0, 0, 0, 0, 0] prefix2_denom = 1.0 dim2_num, prefix2_num = get_dim_prefix(unit2) # Check compatibility dim1 = [i - j for i, j in zip(dim1_num, dim1_denom)] dim2 = [i - j for i, j in zip(dim2_num, dim2_denom)] if dim1 != dim2: raise UnitError("ERROR: Units " + unit1_save + " and " + unit2_save + " do not match") else: return values * (prefix1_num / prefix1_denom) / (prefix2_num / prefix2_denom)
def dB_to_dBA(values, freqs): """Converts values from dB into dBA (requires frequency vector) Parameters ---------- values: array Values of the field to convert (must be 1D) freqs: array Frequency vector Returns ------- ndarray of the converted field """ freq2 = square(freqs) RA = (12200.0**2 * freq2**2 / ((freq2 + 20.6**2) * sqrt( (freq2 + 107.7**2) * (freq2 + 737.0**2)) * (freq2 + 12200.0**2))) Aweight = 2.0 + 20.0 * log10(RA) try: values += Aweight return values except: raise UnitError("ERROR: dBA conversion only available for 1D fft")
def get_magnitude_along( self, *args, unit="SI", is_norm=False, axis_data=[], is_squeeze=True ): """Returns the ndarray of the magnitude of the FT, using conversions and symmetries if needed. Parameters ---------- self: Data a Data object *args: list of strings List of axes requested by the user, their units and values (optional) unit: str Unit requested by the user ("SI" by default) is_norm: bool Boolean indicating if the field must be normalized (False by default) axis_data: list list of ndarray corresponding to user-input data Returns ------- list of 1Darray of axes values, ndarray of magnitude values """ if len(args) == 1 and type(args[0]) == tuple: args = args[0] # if called from another script with *args return_dict = self.get_along( args, axis_data=axis_data, is_squeeze=is_squeeze, is_magnitude=True ) values = return_dict[self.symbol] # 1/nth octave band for axis in return_dict["axes_list"]: if axis.name == "freqs" or axis.corr_name == "freqs": index = axis.index if axis.noct is not None: (values, foct) = apply_along_axis( to_noct, index, values, return_dict["freqs"], noct=axis.noct ) return_dict[axis.name] = foct # Convert into right unit (apart because of dBA conversion) if unit == self.unit or unit == "SI": if is_norm: try: values = values / self.normalizations.get("ref") except: raise NormError( "ERROR: Reference value not specified for normalization" ) elif unit == "dB": ref_value = 1.0 if "ref" in self.normalizations.keys(): ref_value *= self.normalizations.get("ref") values = to_dB(values, self.unit, ref_value) elif unit == "dBA": ref_value = 1.0 if "ref" in self.normalizations.keys(): ref_value *= self.normalizations.get("ref") if "freqs" in return_dict.keys(): for axis in return_dict["axes_list"]: if axis.name == "freqs" or axis.corr_name == "freqs": index = axis.index values = apply_along_axis( to_dBA, index, values, return_dict["freqs"], self.unit, ref_value ) else: raise UnitError( "ERROR: dBA conversion only available for fft with frequencies" ) elif unit in self.normalizations: values = values / self.normalizations.get(unit) else: values = convert(values, self.unit, unit) return_dict[self.symbol] = values return return_dict
def dB_to_dBA(values, freqs, noct=None): """Converts values from dB into dBA (requires frequency vector) Parameters ---------- values: array Values of the field to convert (must be 1D) freqs: array Frequency vector Returns ------- ndarray of the converted field """ if noct is not None: freqs_ref = [ 12.5, 16, 20, 25, 31.5, 40, 50, 63, 80, 100, 125, 160, 200, 250, 315, 400, 500, 630, 800, 1000, 1250, 1600, 2000, 2500, 3150, 4000, 5000, 6300, 8000, 10000, 12500, 16000, 20000, ] Aweight = [ -63.4, -56.7, -50.5, -44.7, -39.4, -34.6, -30.2, -26.2, -22.5, -19.1, -16.1, -13.4, -10.9, -8.6, -6.6, -4.8, -3.2, -1.9, -0.8, 0, 0.6, 1, 1.2, 1.3, 1.2, 1, 0.5, -0.1, -1.1, -2.5, -4.3, -6.6, -9.3, ] mask = argwhere(freqs in freqs_ref) Aweight = take(Aweight, mask) else: freq2 = square(freqs) freq2[freq2 == 0] = nan RA = ( 12200.0 ** 2 * freq2 ** 2 / ( (freq2 + 20.6 ** 2) * sqrt((freq2 + 107.7 ** 2) * (freq2 + 737.0 ** 2)) * (freq2 + 12200.0 ** 2) ) ) Aweight = 2.0 + 20.0 * log10(RA) Aweight[isnan(Aweight)] = -100 # replacing NaN by -100 dB Aweight[ values <= 0 ] = 0 # avoiding to increase dB in dBA at frequencies where noise is already null try: values += Aweight return values except: raise UnitError("ERROR: dBA conversion only available for 1D fft")
def convert(self, values, unit, is_norm, is_squeeze, is_magnitude, axes_list): """Returns the values of the field transformed or converted. Parameters ---------- self: Data a Data object values: ndarray array of the field unit: str Unit requested by the user ("SI" by default) is_norm: bool Boolean indicating if the field must be normalized (False by default) Returns ------- values: ndarray values of the field """ # Take magnitude before summing if is_magnitude: values = np_abs(values) # Apply sums, means, etc for axis_requested in axes_list: # sum over sum axes if axis_requested.extension == "sum": values = np_sum(values, axis=axis_requested.index, keepdims=True) # root sum square over rss axes elif axis_requested.extension == "rss": values = sqrt( np_sum(values**2, axis=axis_requested.index, keepdims=True)) # mean value over mean axes elif axis_requested.extension == "mean": values = np_mean(values, axis=axis_requested.index, keepdims=True) # RMS over rms axes elif axis_requested.extension == "rms": values = sqrt( np_mean(values**2, axis=axis_requested.index, keepdims=True)) # integration over integration axes elif axis_requested.extension == "integrate": values = trapz( values, x=axis_requested.values, axis=axis_requested.index) / (np_max(axis_requested.values) - np_min(axis_requested.values)) if is_squeeze: values = squeeze(values) if unit == self.unit or unit == "SI": if is_norm: try: values = values / self.normalizations.get("ref") except: raise NormError( "ERROR: Reference value not specified for normalization") elif unit == "dB": ref_value = 1.0 if "ref" in self.normalizations.keys(): ref_value *= self.normalizations.get("ref") values = to_dB(np_abs(values), self.unit, ref_value) elif unit == "dBA": ref_value = 1.0 if "ref" in self.normalizations.keys(): ref_value *= self.normalizations.get("ref") for axis in axes_list: is_match = False if axis.name == "freqs" or axis.corr_name == "freqs": index = axis.index values = apply_along_axis(to_dBA, index, values, axis.values, self.unit, ref_value) is_match = True elif axis.name == "frequency": index = axis.index values = apply_along_axis(to_dBA, index, values, axis.values, self.unit, ref_value) is_match = True if not is_match: raise UnitError( "ERROR: dBA conversion only available for fft with frequencies" ) elif unit in self.normalizations: values = values / self.normalizations.get(unit) else: values = convert_unit(values, self.unit, unit) return values
def get_harmonics(self, N_harm, *args, unit="SI", is_norm=False, is_flat=False): """Returns the complex Fourier Transform of the field, using conversions and symmetries if needed. Parameters ---------- self: Data a Data object N_harm: int Number of largest harmonics to be extracted args: list Axes names, ranges and units unit: str Unit demanded by the user ("SI" by default) is_norm: bool Boolean indicating if the field must be normalized (False by default) is_flat: bool Boolean if the output data remains flattened (for 2D cases) Returns ------- list of 1Darray of axes values, ndarray of magnitude of FT """ # Read the axes input in args if len(args) == 1 and type(args[0]) == tuple: args = args[0] # if called from another script with *args axes_list = read_input_strings(args, []) # Extract the requested axes (symmetries + unit) for axis_requested in axes_list: if axis_requested[3] == "values": # Get original values of the axis axis_requested.append( self.get_FT_axis(axis_requested[0] + axis_requested[1])) # Interpolate axis with input data if str(axis_requested[4]) == "whole": axis_requested[4] = axis_requested[5] axis_requested[5] = "whole" else: axis_requested[4] = get_common_base(axis_requested[5], axis_requested[4]) # Change fft name for the slices of the field if axis_requested[0] == "freqs": axis_requested[0] = "time" elif axis_requested[0] == "wavenumber": axis_requested[0] = "angle" # Check if the requested axis is defined in the Data object for axis_requested in axes_list: axis_name = axis_requested[0] is_match = False for index, axis in enumerate(self.axes): if axis.name == axis_name: is_match = True if not is_match: axes_list.remove(axis_requested) # Rebuild symmetries of field if axis is extracted values = self.values for index, axis in enumerate(self.axes): for axis_requested in axes_list: if axis.name in self.symmetries.keys( ) and axis.name == axis_requested[0]: values = rebuild_symmetries(values, index, self.symmetries.get(axis.name)) # Extract the slices of the field (single values) for index, axis in enumerate(self.axes): is_match = False for axis_requested in axes_list: if axis.name == axis_requested[0]: is_match = True if axis_requested[3] == "indices" and axis_requested[ 2] == "single": values = take(values, axis_requested[4], axis=index) if not is_match: # Axis was not specified -> take slice at the first value values = take(values, [0], axis=index) # Interpolate over axis values (single values) for index, axis in enumerate(self.axes): for axis_requested in axes_list: if (axis.name == axis_requested[0] and axis_requested[3] == "values" and axis_requested[2] == "single"): values = apply_along_axis( get_interpolation, index, values, axis_requested[5], axis_requested[4], ) # Perform Fourier Transform values = np_abs(comp_fft(values)) # Extract slices again (intervals) for index, axis in enumerate(self.axes): for axis_requested in axes_list: if axis.name == axis_requested[0]: if axis_requested[2] == "indices" and axis_requested[ 2] == "interval": values = take(values, axis_requested[4], axis=index) # Interpolate over axis values again (intervals) for index, axis in enumerate(self.axes): for axis_requested in axes_list: if axis.name == axis_requested[0]: if axis_requested[3] == "values" and axis_requested[ 2] == "interval": values = apply_along_axis( get_interpolation, index, values, axis_requested[5], axis_requested[4], ) # Eliminate dimensions=1 values = squeeze(values) # Test if data is 1D or 2D if len(values.shape) > 2: raise AxisError("ERROR: only 1D or 2D implemented") else: # Convert into right unit if unit == self.unit or unit == "SI": if is_norm: try: values = values / self.normalizations.get("ref") except: raise NormError( "ERROR: Reference value not specified for normalization" ) elif unit == "dB": ref_value = 1.0 if "ref" in self.normalizations.keys(): ref_value *= self.normalizations.get("ref") values = to_dB(values, self.unit, ref_value) elif unit == "dBA": ref_value = 1.0 is_match = False if "ref" in self.normalizations.keys(): ref_value *= self.normalizations.get("ref") for axis_requested in axes_list: if axis_requested[0] == "time": is_match = True values = to_dBA(values, axis_requested[4], self.unit, ref_value) if not is_match: raise UnitError( "ERROR: dBA conversion only available for fft with frequencies" ) elif unit in self.normalizations: values = values / self.normalizations.get(unit) else: values = convert(values, self.unit, unit) # 1D case if len(values.shape) == 1: for axis_requested in axes_list: if axis_requested[2] == "interval": axis_values = axis_requested[4] indices = argsort(negative(values)) indices = indices[:N_harm] axis_values = axis_values[indices] values = values[indices] return [axis_values, values] # 2D case else: for axis_requested in axes_list: if axis_requested[0] == "angle": r = axis_requested[4] elif axis_requested[0] == "time": f = axis_requested[4] # Flatten the data values_flat = values.flatten() R, F = meshgrid(r, f) f = F.flatten() r = R.flatten() # Get the N_harm largest peaks indices = argsort(negative(values_flat)) indices = indices[:N_harm] values = values_flat[indices] f = f[indices] r = r[indices] if len(values.shape) == 2 and not is_flat: f.reshape((N_harm, N_harm)) r.reshape((N_harm, N_harm)) values.reshape((N_harm, N_harm)) return [f, r, values]