Skip to content
Snippets Groups Projects
Commit cb942282 authored by Luis Dekant's avatar Luis Dekant
Browse files

Merge branch '78-filter-attribute-mixup' into 'develop'

Resolve "filter attribute mixup"

Closes #78

See merge request las-nq/openqlab!41
parents c4551c46 f4962e9b
Branches
Tags
No related merge requests found
......@@ -191,9 +191,9 @@ class Integrator(Filter):
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
......@@ -226,10 +226,10 @@ class Differentiator(Filter):
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
......@@ -247,9 +247,9 @@ class Differentiator(Filter):
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` (might be referenced as `Q` in the description).
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
----------
......@@ -258,18 +258,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
......@@ -278,7 +280,7 @@ 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
......@@ -293,29 +295,31 @@ class Lowpass(Filter):
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`
Quality factor of the notch filter. Defaults to 1.
second_parameter: :obj:`float`
Quality factor of the notch filter. Defaults to 1. Referenced as `Q` in description.
"""
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
......@@ -323,7 +327,7 @@ 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
......@@ -528,30 +532,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
......@@ -559,7 +563,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
......
......
......@@ -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)
......
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment