diff --git a/hitl/loopback.py b/hitl/loopback.py index 679d2b7..475cd63 100644 --- a/hitl/loopback.py +++ b/hitl/loopback.py @@ -12,7 +12,7 @@ import sys from gmqtt import Client as MqttClient from miniconf import Miniconf -# The minimum allowably loopback voltage error (difference between output set point and input +# The minimum allowable loopback voltage error (difference between output set point and input # measured value). MINIMUM_VOLTAGE_ERROR = 0.010 @@ -52,34 +52,30 @@ class TelemetryReader: """ Helper utility to read Stabilizer telemetry. """ @classmethod - async def create(cls, prefix, broker): + async def create(cls, prefix, broker, queue): """Create a connection to the broker and an MQTT device using it.""" client = MqttClient(client_id='') await client.connect(broker) - return cls(client, prefix) + return cls(client, prefix, queue) - def __init__(self, client, prefix): + def __init__(self, client, prefix, queue): """ Constructor. """ self.client = client self._telemetry = [] self.client.on_message = self.handle_telemetry self._telemetry_topic = f'{prefix}/telemetry' self.client.subscribe(self._telemetry_topic) + self.queue = queue def handle_telemetry(self, _client, topic, payload, _qos, _properties): """ Handle incoming telemetry messages over MQTT. """ assert topic == self._telemetry_topic - self._telemetry.append(json.loads(payload)) + self.queue.put_nowait(json.loads(payload)) - def get_latest(self): - """ Get the latest telemetry message received. """ - return self._telemetry[-1] - - -async def test_loopback(miniconf, telemetry, set_point): +async def test_loopback(miniconf, telemetry_queue, set_point, gain=1, channel=0): """ Test loopback operation of Stabilizer. Note: @@ -91,24 +87,27 @@ async def test_loopback(miniconf, telemetry, set_point): miniconf: The miniconf configuration interface. telemetry: a helper utility to read inbound telemetry. set_point: The desired output voltage to test. + channel: The loopback channel to test on. Either 0 or 1. + gain: The desired AFE gain. """ - print(f'Testing loopback for Vout = {set_point:.2f}') + print(f'Testing loopback for Vout = {set_point:.2f}, Gain = x{gain}') print('---------------------------------') - # Configure the IIRs to output at the set point - await miniconf.command('iir_ch/0/0', static_iir_output(set_point), retain=False) - await miniconf.command('iir_ch/1/0', static_iir_output(set_point), retain=False) - await miniconf.command('telemetry_period', 1, retain=False) + # Configure the AFE and IIRs to output at the set point + await miniconf.command(f'afe/{channel}', f'G{gain}', retain=False) + await miniconf.command(f'iir_ch/{channel}/0', static_iir_output(set_point), retain=False) - # Wait for telemetry values to update. + # Configure signal generators to not affect the test. + await miniconf.command('signal_generator/0/amplitude', 0, retain=False) + + # Wait for telemetry to update. await asyncio.sleep(5.0) # Verify the ADCs are receiving the setpoint voltage. tolerance = max(0.05 * set_point, MINIMUM_VOLTAGE_ERROR) - latest_values = telemetry.get_latest() + latest_values = await telemetry_queue.get() print(f'Latest telemtry: {latest_values}') - assert abs(latest_values['adcs'][0] - set_point) < tolerance - assert abs(latest_values['adcs'][1] - set_point) < tolerance + assert abs(latest_values['adcs'][channel] - set_point) < tolerance print('PASS') print('') @@ -123,22 +122,38 @@ def main(): args = parser.parse_args() + telemetry_queue = asyncio.LifoQueue() + + async def telemetry(): + await TelemetryReader.create(args.prefix, args.broker, telemetry_queue) + try: + while True: + await asyncio.sleep(1) + except asyncio.CancelledError: + pass + + + telemetry_task = asyncio.Task(telemetry()) + async def test(): """ The actual testing being completed. """ interface = await Miniconf.create(args.prefix, args.broker) - telemetry = await TelemetryReader.create(args.prefix, args.broker) + + # Disable IIR holds and configure the telemetry rate. + await interface.command('allow_hold', False, retain=False) + await interface.command('force_hold', False, retain=False) + await interface.command('telemetry_period', 1, retain=False) # Test loopback with a static 1V output of the DACs. - await test_loopback(interface, telemetry, 1.0) + await test_loopback(interface, telemetry_queue, 1.0) # Repeat test with AFE = 2x - print('Configuring AFEs to 2x input') - await interface.command('afe/0', "G2", retain=False) - await interface.command('afe/1', "G2", retain=False) - await test_loopback(interface, telemetry, 1.0) + await test_loopback(interface, telemetry_queue, 1.0, gain=2) # Test with 0V output - await test_loopback(interface, telemetry, 0.0) + await test_loopback(interface, telemetry_queue, 0.0) + + telemetry_task.cancel() loop = asyncio.get_event_loop() sys.exit(loop.run_until_complete(test())) diff --git a/hitl/run.sh b/hitl/run.sh index e3a86dc..7c7aa27 100755 --- a/hitl/run.sh +++ b/hitl/run.sh @@ -15,7 +15,7 @@ python3 -m venv --system-site-packages py . py/bin/activate # Install Miniconf utilities for configuring stabilizer. -python3 -m pip install git+https://github.com/quartiq/miniconf#subdirectory=miniconf-py +python3 -m pip install git+https://github.com/quartiq/miniconf#subdirectory=py/miniconf-mqtt python3 -m pip install gmqtt cargo flash --chip STM32H743ZITx --elf target/thumbv7em-none-eabihf/release/dual-iir