diff --git a/src/openqlab/analysis/servo_design.py b/src/openqlab/analysis/servo_design.py index 1d84e7fd6e8e5affbc7c7923b4b3a8592ddfd9a6..b714335bded9df33890ff5a7c14e46fcfd524082 100644 --- a/src/openqlab/analysis/servo_design.py +++ b/src/openqlab/analysis/servo_design.py @@ -189,14 +189,11 @@ class Integrator(Filter): Frequency were the ~1/f slope starts, defaults to 0.001 * `corner_frequency`. """ - def __init__(self, corner_frequency, second_parameter=None, enabled=True): - super().__init__(corner_frequency, second_parameter, enabled) - def calculate(self): z = -self.corner_frequency - if self.sF is None: - self._second_parameter = self.corner_frequency * 0.001 - p = -self.sF + if self.second_parameter is None: + self.second_parameter = self.corner_frequency * 0.001 + p = -self.second_parameter k = 1.0 # Gain = 1 return z, p, k @@ -204,14 +201,6 @@ class Integrator(Filter): def description(self): return "Int {0}".format(human_readable(self.corner_frequency, "Hz")) - @property - def sF(self): - return self.second_parameter - - @sF.setter - def sF(self, value): - self.second_parameter = value - class Differentiator(Filter): """ @@ -227,35 +216,24 @@ class Differentiator(Filter): """ - def __init__(self, corner_frequency, second_parameter=None, enabled=True): - super().__init__(corner_frequency, second_parameter, enabled) - def calculate(self): z = -self.corner_frequency - if self.sF is None: - self._second_parameter = self.corner_frequency * 10 - p = -self.sF - k = self.sF / self.corner_frequency + if self.second_parameter is None: + self.second_parameter = self.corner_frequency * 10 + p = -self.second_parameter + k = self.second_parameter / self.corner_frequency return z, p, k @property def description(self): return "Diff {0}".format(human_readable(self.corner_frequency, "Hz")) - @property - def sF(self): - return self.second_parameter - - @sF.setter - def sF(self, value): - self.second_parameter = value - class Lowpass(Filter): """ - Create a 2nd-order lowpass filter with variable quality factor `Q`. + Create a 2nd-order lowpass filter with variable quality factor `second_parameter`. - The default `Q` of ``1/sqrt(2)`` results in a Butterworth filter with flat passband. + The default `second_parameter` of ``1/sqrt(2)`` results in a Butterworth filter with flat passband. Parameters ---------- @@ -264,18 +242,20 @@ class Lowpass(Filter): """ - def __init__(self, corner_frequency, Q=0.707, enabled=True): - super().__init__(corner_frequency, Q, enabled) + def __init__(self, corner_frequency, second_parameter=0.707, enabled=True): + super().__init__(corner_frequency, second_parameter, enabled) def calculate(self): z = [] corner_frequency = self.corner_frequency - Q = self.Q + second_parameter = self.second_parameter p = [ - -corner_frequency / (2 * Q) - + ((corner_frequency / (2 * Q)) ** 2 - corner_frequency ** 2) ** 0.5, - -corner_frequency / (2 * Q) - - ((corner_frequency / (2 * Q)) ** 2 - corner_frequency ** 2) ** 0.5, + -corner_frequency / (2 * second_parameter) + + ((corner_frequency / (2 * second_parameter)) ** 2 - corner_frequency ** 2) + ** 0.5, + -corner_frequency / (2 * second_parameter) + - ((corner_frequency / (2 * second_parameter)) ** 2 - corner_frequency ** 2) + ** 0.5, ] k = corner_frequency ** 2 @@ -284,44 +264,38 @@ class Lowpass(Filter): @property def description(self): return "LP2 {0}, Q={1:.4g}".format( - human_readable(self.corner_frequency, "Hz"), self.Q + human_readable(self.corner_frequency, "Hz"), self.second_parameter ) - @property - def Q(self): - return self.second_parameter - - @Q.setter - def Q(self, value): - self.second_parameter = value - class Notch(Filter): """ Create a notch filter at frequency `corner_frequency` with a quality - factor `Q`, where the -3dB filter bandwidth ``bw`` is - given by ``Q = corner_frequency/bw``. + factor `second_parameter`, where the -3dB filter bandwidth ``bw`` is + given by ``second_parameter = corner_frequency/bw``. Parameters ---------- corner_frequency: :obj:`float` Frequency to remove from the spectrum - Q: :obj:`float` + second_parameter: :obj:`float` Quality factor of the notch filter. Defaults to 1. """ - def __init__(self, corner_frequency, Q=1, enabled=True): - super().__init__(corner_frequency, Q, enabled) + def __init__(self, corner_frequency, second_parameter=1, enabled=True): + super().__init__(corner_frequency, second_parameter, enabled) def calculate(self): corner_frequency = self.corner_frequency - Q = self.Q + second_parameter = self.second_parameter z = [corner_frequency * 1j, -corner_frequency * 1j] p = [ - -corner_frequency / (2 * Q) - + ((corner_frequency / (2 * Q)) ** 2 - corner_frequency ** 2) ** 0.5, - -corner_frequency / (2 * Q) - - ((corner_frequency / (2 * Q)) ** 2 - corner_frequency ** 2) ** 0.5, + -corner_frequency / (2 * second_parameter) + + ((corner_frequency / (2 * second_parameter)) ** 2 - corner_frequency ** 2) + ** 0.5, + -corner_frequency / (2 * second_parameter) + - ((corner_frequency / (2 * second_parameter)) ** 2 - corner_frequency ** 2) + ** 0.5, ] k = 1 return z, p, k @@ -329,17 +303,9 @@ class Notch(Filter): @property def description(self): return "Notch {0}, Q={1:.4g}".format( - human_readable(self.corner_frequency, "Hz"), self.Q + human_readable(self.corner_frequency, "Hz"), self.second_parameter ) - @property - def Q(self): - return self.second_parameter - - @Q.setter - def Q(self, value): - self.second_parameter = value - class ServoDesign: """ @@ -534,30 +500,30 @@ class ServoDesign: """ self.add(Differentiator(corner_frequency, fstop, enabled)) - def lowpass(self, corner_frequency, Q=0.707, enabled=True): + def lowpass(self, corner_frequency, second_parameter=0.707, enabled=True): """ - Add a 2nd-order lowpass filter with variable quality factor `Q`. + Add a 2nd-order lowpass filter with variable quality factor `second_parameter`. - The default `Q` of ``1/sqrt(2)`` results in a Butterworth filter with flat passband. + The default `second_parameter` of ``1/sqrt(2)`` results in a Butterworth filter with flat passband. Parameters ---------- parameter: :obj:`type` parameter description """ - self.add(Lowpass(corner_frequency, Q, enabled)) + self.add(Lowpass(corner_frequency, second_parameter, enabled)) - def notch(self, corner_frequency, Q=1, enabled=True): + def notch(self, corner_frequency, second_parameter=1, enabled=True): """ Add a notch filter at frequency `corner_frequency` with a - quality factor `Q`, where the -3dB filter bandwidth ``bw`` - is given by ``Q = corner_frequency/bw``. + quality factor `second_parameter`, where the -3dB filter bandwidth ``bw`` + is given by ``second_parameter = corner_frequency/bw``. Parameters ---------- corner_frequency: :obj:`float` Frequency to remove from the spectrum - Q: :obj:`float` + second_parameter: :obj:`float` Quality factor of the notch filter Returns @@ -565,7 +531,7 @@ class ServoDesign: :obj:`Servo` the servo object with added notch filter """ - self.add(Notch(corner_frequency, Q, enabled)) + self.add(Notch(corner_frequency, second_parameter, enabled)) #################################### # CLASS UTILITY diff --git a/src/tests/test_analysis/test_servo_design/test_servo_design.py b/src/tests/test_analysis/test_servo_design/test_servo_design.py index 9d1a7509ddde81050555e24bca3e009a6370411e..189d82a41ba1a8cd4761d14ede1bbbc6c38f9777 100644 --- a/src/tests/test_analysis/test_servo_design/test_servo_design.py +++ b/src/tests/test_analysis/test_servo_design/test_servo_design.py @@ -23,33 +23,33 @@ filedir = Path(__file__).parent class TestFilter(unittest.TestCase): def test_integrator(self): corner_frequency = 840 - sF = 5300 - i = Integrator(corner_frequency, sF) + second_parameter = 5300 + i = Integrator(corner_frequency, second_parameter) np.testing.assert_allclose(i.zeros, -corner_frequency) - np.testing.assert_allclose(i.poles, -sF) + np.testing.assert_allclose(i.poles, -second_parameter) np.testing.assert_allclose(i.gain, 1) - i.sF = corner_frequency * 0.001 + i.second_parameter = corner_frequency * 0.001 np.testing.assert_allclose(i.zeros, -corner_frequency) np.testing.assert_allclose(i.poles, -corner_frequency / 1000) def test_differentiator(self): corner_frequency = 840 - sF = 5300 - i = Differentiator(corner_frequency, sF) + second_parameter = 5300 + i = Differentiator(corner_frequency, second_parameter) np.testing.assert_allclose(i.zeros, -corner_frequency) - np.testing.assert_allclose(i.poles, -sF) + np.testing.assert_allclose(i.poles, -second_parameter) np.testing.assert_allclose(i.gain, 6.309524) - i.sF = None + i.second_parameter = None np.testing.assert_allclose(i.zeros, -corner_frequency) np.testing.assert_allclose(i.poles, -corner_frequency * 10) np.testing.assert_allclose(i.gain, 10) def test_lowpass(self): corner_frequency = 840 - Q = 3 - i1 = Lowpass(corner_frequency, Q) + second_parameter = 3 + i1 = Lowpass(corner_frequency, second_parameter) np.testing.assert_allclose(i1.zeros, -corner_frequency) np.testing.assert_allclose( i1.poles, [-140.0 + 828.25116963j, -140.0 - 828.25116963j] @@ -65,16 +65,16 @@ class TestFilter(unittest.TestCase): def test_notch(self): corner_frequency = 840 - Q = 1 - i1 = Notch(corner_frequency, Q) + second_parameter = 1 + i1 = Notch(corner_frequency, second_parameter) np.testing.assert_allclose(i1.zeros, [0.0 + 840.0j, -0.0 - 840.0j]) np.testing.assert_allclose( i1.poles, [-420.0 + 727.46133918j, -420.0 - 727.46133918j] ) np.testing.assert_allclose(i1.gain, 1) - Q = 32 - i2 = Notch(corner_frequency, Q) + second_parameter = 32 + i2 = Notch(corner_frequency, second_parameter) np.testing.assert_allclose(i2.zeros, [0.0 + 840.0j, -0.0 - 840.0j]) np.testing.assert_allclose( i2.poles, [-13.125 + 839.89745468j, -13.125 - 839.89745468j] @@ -217,7 +217,7 @@ class TestServoDesign(unittest.TestCase): self.sd.integrator(500) self.sd.differentiator(1000, 1000 * 1000) - self.sd.notch(900, Q=200) + self.sd.notch(900, second_parameter=200) zpk_expected = ( np.array([-500.0, -1000.0, 0.0 + 900.0j, -0.0 - 900.0j]), np.array([-0.5, -1e6, -2.25e00 + 899.9971875j, -2.25e00 - 899.9971875j]), @@ -279,7 +279,7 @@ class TestServoDesign(unittest.TestCase): self.assertGreaterEqual(ax.get_xlim()[1], 5e3) def test_get_dataframe(self): - self.sd.notch(1e3, Q=3) + self.sd.notch(1e3, second_parameter=3) df = self.sd.plot(plot=False) self.assertIsInstance(df, DataFrame) columns = ["Servo A", "Servo P"] @@ -305,18 +305,18 @@ class TestServoDesign(unittest.TestCase): self.sd.lowpass(438) self.assertEqual(self.sd.filters[2].description, "LP2 438 Hz, Q=0.707") self.sd.filters[2].corner_frequency = 1200 - self.sd.filters[2].Q = 12 + self.sd.filters[2].second_parameter = 12 self.assertEqual(self.sd.filters[2].description, "LP2 1.2 kHz, Q=12") - self.sd.notch(438, Q=11) + self.sd.notch(438, second_parameter=11) self.assertEqual(self.sd.filters[3].description, "Notch 438 Hz, Q=11") self.sd.filters[3].corner_frequency = 1200 - self.sd.filters[3].Q = 1.3 + self.sd.filters[3].second_parameter = 1.3 self.assertEqual(self.sd.filters[3].description, "Notch 1.2 kHz, Q=1.3") def test_discrete_form(self): self.sd.integrator(500) - self.sd.notch(900, Q=200) + self.sd.notch(900, second_parameter=200) discrete_orig = { "sampling_frequency": 100000.0, "gain": 1.0, @@ -370,8 +370,8 @@ class TestServoDesign(unittest.TestCase): self.sd.integrator(500) self.sd.differentiator(1000, 1000 * 1000) - self.sd.notch(900, Q=200) - self.sd.notch(1400, Q=200, enabled=False) + self.sd.notch(900, second_parameter=200) + self.sd.notch(1400, second_parameter=200, enabled=False) self.sd.differentiator(3000, enabled=False) zpk_expected = ( np.array([-500.0, -1000.0, 0.0 + 900.0j, -0.0 - 900.0j]), @@ -384,7 +384,7 @@ class TestServoDesign(unittest.TestCase): def test_discrete_form_with_disabled_filter(self): self.sd.integrator(500) - self.sd.notch(900, Q=200) + self.sd.notch(900, second_parameter=200) self.sd.integrator(500, enabled=False) self.sd.lowpass(5000, enabled=False) discrete_orig = { @@ -445,7 +445,7 @@ class TestServoDesign(unittest.TestCase): np.testing.assert_allclose(f1["sos"], f2["sos"]) def test_jsonpickle(self): - self.sd.notch(900, Q=200) + self.sd.notch(900, second_parameter=200) self.sd.integrator(500) self.sd.lowpass(5000)