From 879dbfe333c70dd83e027dde9ed6297d1ba11d20 Mon Sep 17 00:00:00 2001 From: Sergio Aguirre Date: Sat, 31 Oct 2026 19:50:30 +0100 Subject: [PATCH 1/2] [FIX] replace the NumPy aliases removed in NumPy 2 np.complex_ and np.int0 were removed in NumPy 2.0, and np.int in NumPy 1.24. With no version pin in setup.py, a fresh install gets NumPy 2 and SignalObj fails on its first spectrum. Use np.complex128, np.intp and the builtin int, which are what the aliases stood for. Co-Authored-By: Claude Fable 5.1 --- pytta/_h5utils.py | 2 +- pytta/classes/signal.py | 18 +++++++++--------- pytta/functions.py | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/pytta/_h5utils.py b/pytta/_h5utils.py index 6af8b3d..8994fc5 100644 --- a/pytta/_h5utils.py +++ b/pytta/_h5utils.py @@ -32,7 +32,7 @@ def int_parser(attr): np.int32, np.int64, int, - np.int0)): + np.intp)): return int(attr) else: return attr diff --git a/pytta/classes/signal.py b/pytta/classes/signal.py index 79151c3..6b615c2 100644 --- a/pytta/classes/signal.py +++ b/pytta/classes/signal.py @@ -941,14 +941,14 @@ def __truediv__(self, other): raise ValueError("Both signal-like objects must have the \ same number of channels.") result_freqSignal = np.zeros(self.freqSignal.shape, - dtype=np.complex_) + dtype=np.complex128) for channel in range(other.numChannels): result_freqSignal[:, channel] = \ currentFreqSignal[:, channel] \ / otherFreqSignal[:, channel] else: result_freqSignal = np.zeros(self.freqSignal.shape, - dtype=np.complex_) + dtype=np.complex128) for channel in range(self.numChannels): result_freqSignal[:, channel] = \ currentFreqSignal[:, channel] \ @@ -998,14 +998,14 @@ def __mul__(self, other): raise ValueError("Both signal-like objects must have the \ same number of channels.") result_freqSignal = np.zeros(self.freqSignal.shape, - dtype=np.complex_) + dtype=np.complex128) for channel in range(other.numChannels): result_freqSignal[:, channel] = \ currentFreqSignal[:, channel] \ * otherFreqSignal[:, channel] else: result_freqSignal = np.zeros(self.freqSignal.shape, - dtype=np.complex_) + dtype=np.complex128) for channel in range(self.numChannels): result_freqSignal[:, channel] = \ currentFreqSignal[:, channel] \ @@ -1144,7 +1144,7 @@ def _ifft(self): """ # spectrum denormalization if self.signalType == 'power': - adjustedFreqSignal = np.zeros(self._freqSignal.shape, dtype=np.complex_) + adjustedFreqSignal = np.zeros(self._freqSignal.shape, dtype=np.complex128) # * N/2 for AC adjustedFreqSignal[1:, :] = \ self._freqSignal * len(self._freqSignal) @@ -1762,8 +1762,8 @@ def _crossfade_spectruns(self, a, b, freqLims, freqVector): leftWin, np.ones(totalSamples-len(leftWin)-f0idx))) - aFreqSignal = np.zeros(a.shape, dtype=np.complex_) - bFreqSignal = np.zeros(b.shape, dtype=np.complex_) + aFreqSignal = np.zeros(a.shape, dtype=np.complex128) + bFreqSignal = np.zeros(b.shape, dtype=np.complex128) for chIndex in range(a.shape[1]): aFreqSignal[:,chIndex] = a[:,chIndex] * fullRightWin @@ -1791,14 +1791,14 @@ def _calc_csd_tf(self, sig1, sig2, samplingRate, windowName, def _make_rms_spectra(freqSignal): - newFreqSignal = np.zeros(freqSignal.shape, dtype=np.complex_) + newFreqSignal = np.zeros(freqSignal.shape, dtype=np.complex128) newFreqSignal[1:, :] = freqSignal[1:, :] / 2**(1/2) newFreqSignal[0, :] = freqSignal[0, :] return newFreqSignal def _make_pk_spectra(freqSignal): - newFreqSignal = np.zeros(freqSignal.shape, dtype=np.complex_) + newFreqSignal = np.zeros(freqSignal.shape, dtype=np.complex128) newFreqSignal[1:, :] = freqSignal[1:, :] * 2**(1/2) newFreqSignal[0, :] = freqSignal[0, :] return newFreqSignal diff --git a/pytta/functions.py b/pytta/functions.py index 9b39239..b4f47fb 100644 --- a/pytta/functions.py +++ b/pytta/functions.py @@ -251,7 +251,7 @@ def resample(signal, newSamplingRate): Resample the timeSignal of the input SignalObj to the given sample rate using the scipy.signal.resample() function """ - newSignalSize = np.int(signal.timeLength*newSamplingRate) + newSignalSize = int(signal.timeLength*newSamplingRate) resampled = ss.resample(signal.timeSignal[:], newSignalSize) newSignal = SignalObj(resampled, "time", newSamplingRate) return newSignal From 0a4f31a6cfe7ae4cc94749d1fd89c6ecf44393cd Mon Sep 17 00:00:00 2001 From: Sergio Aguirre Date: Sat, 31 Oct 2026 20:46:04 +0100 Subject: [PATCH 2/2] [FIX] accept a NumPy number as channel correction factor With NumPy 2 the ratio refPrms / rms in calib_pressure and calib_volt is a NumPy float32, which is not a Python float, so the CF setter raised TypeError and no channel could be calibrated. It now accepts any NumPy integer or floating number and stores it as a float. Co-Authored-By: Claude Fable 5.1 --- pytta/classes/_base.py | 4 ++-- tests/test_calibration.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 tests/test_calibration.py diff --git a/pytta/classes/_base.py b/pytta/classes/_base.py index 13cb139..8faf2bb 100644 --- a/pytta/classes/_base.py +++ b/pytta/classes/_base.py @@ -490,8 +490,8 @@ def CF(self): @CF.setter def CF(self, newCF): - if isinstance(newCF, float) or isinstance(newCF, int): - self._CF = newCF + if isinstance(newCF, (int, float, np.integer, np.floating)): + self._CF = float(newCF) else: raise TypeError('Channel correction factor must be a number.') return diff --git a/tests/test_calibration.py b/tests/test_calibration.py new file mode 100644 index 0000000..0454fe6 --- /dev/null +++ b/tests/test_calibration.py @@ -0,0 +1,31 @@ +""" +Test for SignalObj.calib_pressure: the correction factor is the reference +rms over the rms of the calibrator recording. On NumPy 2 that ratio is a +NumPy float32, which the channel must accept. +""" +import os +import unittest + +os.environ.setdefault('MPLBACKEND', 'Agg') + +import numpy as np + +import pytta + +FS = 48000 + + +class TestCalibration(unittest.TestCase): + + def test_correction_factor_from_a_calibrator(self): + tt = np.arange(FS) / FS + calibrator = pytta.SignalObj( + 0.1 * np.sqrt(2) * np.sin(2 * np.pi * 1000 * tt), 'time', FS) + sig = pytta.SignalObj(np.ones(FS), 'time', FS) + sig.calib_pressure(0, calibrator, refPrms=1.0) + self.assertAlmostEqual(sig.channels[1].CF, 10, places=5) + self.assertTrue(np.allclose(sig.timeSignal, 10, rtol=1e-5)) + + +if __name__ == '__main__': + unittest.main()