diff options
-rw-r--r-- | doc/architecture.drawio | 2 | ||||
-rw-r--r-- | lab_control/frequency_response.py | 14 | ||||
-rw-r--r-- | lab_control/measurement.py | 9 | ||||
-rw-r--r-- | lab_control/test/frequency_response_test.py (renamed from lab_control/test/frequency_response_measurement_test.py) | 37 | ||||
-rw-r--r-- | lab_control/test/measurement_test.py | 53 |
5 files changed, 92 insertions, 23 deletions
diff --git a/doc/architecture.drawio b/doc/architecture.drawio index f32a569..d1e47f2 100644 --- a/doc/architecture.drawio +++ b/doc/architecture.drawio @@ -1 +1 @@ -<mxfile host="Electron" modified="2022-06-01T15:45:48.554Z" agent="5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/18.0.6 Chrome/100.0.4896.143 Electron/18.2.3 Safari/537.36" etag="QFb5ZM_4cWopSkUcfKm_" version="18.0.6" type="device"><diagram id="Aw8Itl_1nmNhQKuLZfUh" name="Page-1">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</diagram></mxfile>
\ No newline at end of file +<mxfile host="Electron" modified="2022-06-02T18:24:10.320Z" agent="5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/16.0.2 Chrome/96.0.4664.55 Electron/16.0.5 Safari/537.36" etag="jSyoEUgZMjaeJw8LBDId" version="16.0.2" type="device"><diagram id="Aw8Itl_1nmNhQKuLZfUh" name="Page-1">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</diagram></mxfile>
\ No newline at end of file diff --git a/lab_control/frequency_response.py b/lab_control/frequency_response.py index af399b1..8d46689 100644 --- a/lab_control/frequency_response.py +++ b/lab_control/frequency_response.py @@ -1,6 +1,6 @@ from lab_control.function_generator import FunctionGenerator from lab_control.oscilloscope import Oscilloscope -from lab_control.measurement import Measurement +from lab_control.measurement import Measurement, getLinearRange class FrequencyResponseMeasurement(Measurement): def __init__(self): @@ -13,13 +13,15 @@ class FrequencyResponseMeasurement(Measurement): self.data = None def measure(self, osc: Oscilloscope, fg: FunctionGenerator) -> None: - frequencyRange = self.maxFrequency - self.minFrequency + frequencies = getLinearRange(self.minFrequency, self.maxFrequency, self.steps) self.data = [] - for i in range(0, self.steps): - frequency = self.minFrequency + i * frequencyRange / (self.steps - 1) - fg.setFrequency(self.functionGeneratorChannel, frequency) + for f in frequencies: + fg.setFrequency(self.functionGeneratorChannel, f) response = osc.measureAmplitude(self.oscilloscopeChannel) - self.data.append((frequency, response)) + self.data.append((f, response)) self.measurementDone = True + + def saveToCSV(self, path: str) -> None: + pass diff --git a/lab_control/measurement.py b/lab_control/measurement.py index 78f393e..eca228d 100644 --- a/lab_control/measurement.py +++ b/lab_control/measurement.py @@ -1,6 +1,15 @@ from lab_control.function_generator import FunctionGenerator from lab_control.oscilloscope import Oscilloscope +def getLinearRange(first: float, last: float, steps: int) -> list[float]: + assert steps > 1, "Linear range requires at least two steps" + diff = last - first + stepSize = diff / (steps - 1) + return [first + i * stepSize for i in range(0, steps)] + class Measurement: def measure(self, osc: Oscilloscope, fg: FunctionGenerator) -> None: pass + + def saveToCSV(self, path: str) -> None: + pass diff --git a/lab_control/test/frequency_response_measurement_test.py b/lab_control/test/frequency_response_test.py index 5a73b3e..014b4f6 100644 --- a/lab_control/test/frequency_response_measurement_test.py +++ b/lab_control/test/frequency_response_test.py @@ -2,6 +2,7 @@ import pytest from lab_control.test.mock_lab import MockLab from lab_control.frequency_response import FrequencyResponseMeasurement +from lab_control.measurement import getLinearRange @pytest.fixture def mockLab(): @@ -11,22 +12,9 @@ def mockLab(): def uut(mockLab): return FrequencyResponseMeasurement() -def test_frequencyResponseDefaults(uut): - assert uut.minFrequency == 20e0 - assert uut.maxFrequency == 16e3 - assert uut.steps == 50 - assert uut.functionGeneratorChannel == 1 - assert uut.oscilloscopeChannel == 1 - -def test_frequencyResponseRamp(mockLab, uut): - uut.minFrequency = 100.0 - uut.maxFrequency = 200.0 - uut.steps = 11 - uut.functionGeneratorChannel = 1 - uut.oscilloscopeChannel = 1 - +def prepareRampResponse(uut, mockLab): # Expect a ramp response from 0.5 to 1.5 * input amplitude - inputAmplitude = 1.0 + inputAmplitude = 2.0 minScale = 0.5 maxScale = 1.5 @@ -40,7 +28,23 @@ def test_frequencyResponseRamp(mockLab, uut): mockLab.setAmplitude(uut.functionGeneratorChannel, inputAmplitude) mockLab.setOn(uut.functionGeneratorChannel) - expectedData = [(f, testFunction(f) * inputAmplitude) for f in [100.0, 110.0, 120.0, 130.0, 140.0, 150.0, 160.0, 170.0, 180.0, 190.0, 200.0]] + return [(f, testFunction(f) * inputAmplitude) for f in getLinearRange(uut.minFrequency, uut.maxFrequency, uut.steps)] + +def test_frequencyResponseDefaults(uut): + assert uut.minFrequency == 20e0 + assert uut.maxFrequency == 16e3 + assert uut.steps == 50 + assert uut.functionGeneratorChannel == 1 + assert uut.oscilloscopeChannel == 1 + +def test_frequencyResponseRamp(mockLab, uut): + uut.minFrequency = 100.0 + uut.maxFrequency = 200.0 + uut.steps = 11 + uut.functionGeneratorChannel = 1 + uut.oscilloscopeChannel = 1 + + expectedData = prepareRampResponse(uut, mockLab) assert not uut.measurementDone assert uut.data == None @@ -49,3 +53,4 @@ def test_frequencyResponseRamp(mockLab, uut): assert uut.measurementDone assert uut.data == expectedData + diff --git a/lab_control/test/measurement_test.py b/lab_control/test/measurement_test.py new file mode 100644 index 0000000..07716a3 --- /dev/null +++ b/lab_control/test/measurement_test.py @@ -0,0 +1,53 @@ +import pytest + +from lab_control.measurement import * + +@pytest.fixture +def uut(): + return Measurement() + +def test_linearRangeAscending(): + first = 100.0 + last = 200.0 + steps = 11 + + expectedRange = [ + 100.0, 110.0, 120.0, 130.0, + 140.0, 150.0, 160.0, 170.0, + 180.0, 190.0, 200.0 + ] + + actualRange = getLinearRange(first, last, steps) + assert actualRange == expectedRange + +def test_linearRangeDescending(): + first = 200.0 + last = 180.0 + steps = 3 + + expectedRange = [ + 200.0, 190.0, 180.0 + ] + + actualRange = getLinearRange(first, last, steps) + assert actualRange == expectedRange + +def test_linearRangeTwoSteps(): + first = 20.0 + last = -20.0 + steps = 2 + + expectedRange = [ + 20.0, -20.0 + ] + + actualRange = getLinearRange(first, last, steps) + assert actualRange == expectedRange + +def test_linearRangeOneStep(): + first = 20.0 + last = -20.0 + steps = 1 + + with pytest.raises(AssertionError): + actualRange = getLinearRange(first, last, steps) |