From f1aac102d24103279bd8ae1ca312b09f0c61ec14 Mon Sep 17 00:00:00 2001 From: Martin Killenberg <martin.killenberg@desy.de> Date: Wed, 16 Aug 2023 15:05:01 +0200 Subject: [PATCH] feat: separate VNA into generic DUT class All notions of VNA, magnitude and phase in prototype.py and the GUI have been removed and generalised. This is just refactoring, no functional change. However, the syntax of the config file has been adapted. --- Python_script/climate-lab-gui.py | 3 +- Python_script/dut_measurement.py | 33 ++++ Python_script/prototype.py | 247 ++++++++---------------- Python_script/test_stand_parameter.json | 21 +- Python_script/vna_measurement.py | 106 ++++++++++ 5 files changed, 237 insertions(+), 173 deletions(-) create mode 100644 Python_script/dut_measurement.py create mode 100644 Python_script/vna_measurement.py diff --git a/Python_script/climate-lab-gui.py b/Python_script/climate-lab-gui.py index c30bd98..b77c3da 100755 --- a/Python_script/climate-lab-gui.py +++ b/Python_script/climate-lab-gui.py @@ -74,8 +74,7 @@ class TestStandMainWindow(QMainWindow): self.setEnabled(True) return - meas = prototype.Measurements(config_data['chamber_ip'], config_data['vna_ip'], output_basename, - False, config_data, ext_sensor_channels, config_data['logger_ip']) + meas = prototype.Measurements(config_data, output_basename,False, ext_sensor_channels) try: if self.tempSweepButton.isChecked(): temperatures = meas.perform_sweep(self.startParameter.value(), self.stopParameter.value(), diff --git a/Python_script/dut_measurement.py b/Python_script/dut_measurement.py new file mode 100644 index 0000000..bffb95d --- /dev/null +++ b/Python_script/dut_measurement.py @@ -0,0 +1,33 @@ +from abc import ABC, abstractmethod + + +class DutMeasurement(ABC): + """ + Interface for DUT measurements + """ + @abstractmethod + def get_dut_measurements(self): + """ + Returns a dictionary with column names a keys and scalar values + """ + pass + + @abstractmethod + def get_dut_signal_names(self): + """ + Returns a list of names used as keys used in the dut measurements dictionary + """ + pass + + @abstractmethod + def get_dut_reference_signal_names(self): + """ + Returns a list of signal names used for stability checks + """ + pass + + @abstractmethod + def get_dut_max_delta_signals(self): + """ + Return the maximum deltas of the reference signals + """ diff --git a/Python_script/prototype.py b/Python_script/prototype.py index 2095a3a..2b708cd 100755 --- a/Python_script/prototype.py +++ b/Python_script/prototype.py @@ -1,14 +1,12 @@ #!/usr/bin/python3 import csv -import math -import cmath import time import numpy from argparse import ArgumentParser import pandas as pd import matplotlib.pyplot as plt import climate_chamber -import VNA +import vna_measurement import virtual_time import json import MeasurementPlot @@ -17,34 +15,28 @@ import analysis import external_sensors import PostPlot import os -import pyvisa - +# Only use these when calculating the equilibrium indicator. Don't use in the algorithm. TEMPERATURE_STABLE = 0x1 HUMIDITY_STABLE = 0x2 -MAGNITUDE_STABLE = 0x4 -PHASE_STABLE = 0x8 +DUT_SIGNAL0_STABLE = 0x4 +DUT_SIGNAL1_STABLE = 0x8 MEASUREMENT_STABLE = 0x10 -# Class has only attributes which we are using in the read_data_function to read data from VNA and Chamber +# Class has only attributes which we are using in the read_data_function to read data from DUT and Chamber class MeasurementData: - def __init__(self, timestamp, temp, hum, power, frequency, s11, s21, s12, s22, perc_temp_heater, - perc_hum_heater, temp_dut, temp_room, temp_meas_instr, hum_dut, hum_room, + def __init__(self, timestamp, temp, hum, dut_data, percent_temp_heater, + percent_hum_heater, temp_dut, temp_room, temp_meas_instr, hum_dut, hum_room, hum_meas_instr, air_press_room, temp_chamber_meas_instr, hum_chamber_meas_instr): self.timestamp = timestamp self.temp = temp self.hum = hum - self.power = power - self.frequency = frequency - self.s11 = s11 - self.s21 = s21 - self.s12 = s12 - self.s22 = s22 - - self.perc_temp_heater = perc_temp_heater - self.perc_hum_heater = perc_hum_heater + self.dut_data = dut_data + + self.percent_temp_heater = percent_temp_heater + self.percent_hum_heater = percent_hum_heater self.temp_dut = temp_dut self.temp_room = temp_room self.temp_meas_instr = temp_meas_instr @@ -57,40 +49,35 @@ class MeasurementData: class Measurements: - def __init__(self, chamber_address, vna_address, output_basename, standby, config_data, - ext_sensor_channels, logger_address): + def __init__(self, config_data, output_basename, standby, ext_sensor_channels): self.max_delta_temp = config_data['delta_temp'] self.max_delta_hum = config_data['delta_hum'] - self.max_delta_mag = config_data['delta_mag'] - self.max_delta_phase = config_data['delta_phase'] + self.ext_sensor_channels = ext_sensor_channels - self.logger_model = config_data['logger_model'] self.sleep_time = config_data["sleep_time"] - self.frequency = config_data["frequency"] - self.vna_config_file = config_data["vna_config_file"] target_accuracy = [self.max_delta_temp, self.max_delta_hum] - self.chamber = climate_chamber.create_chamber(chamber_address, target_accuracy) + self.chamber = climate_chamber.create_chamber(config_data['chamber_ip'], target_accuracy) self.instr_chamber = climate_chamber.create_chamber(config_data['instr_chamber_ip'], target_accuracy) - self.vna = VNA.create_vna(vna_address, target_accuracy) + # FIXME: Do we want a factory function for the DUTs? + if config_data['dut']['type'] == 'VNA': + self.dut = vna_measurement.VnaMeasurement(config_data['dut'], target_accuracy) + else: + raise Exception('Unknown DUT type: '+config_data['dut']['type']) + + self.max_delta_dut_signals = self.dut.get_dut_max_delta_signals() # data logger for external sensors - self.ext_sensors = external_sensors.create_sensors(config_data['logger_model'], logger_address, + self.ext_sensors = external_sensors.create_sensors(config_data['logger_model'], config_data['logger_ip'], ext_sensor_channels) self.standby = standby self.output_basename = output_basename - self.clock = virtual_time.get_clock(chamber_address, target_accuracy) + self.clock = virtual_time.get_clock(config_data['chamber_ip'], target_accuracy) self.temperature_stable = False self.humidity_stable = False - self.magnitude_stable = False - self.phase_stable = False - - self.vna.load_config(self.vna_config_file, self.frequency) - self.vna.create_new_trace("Trace1", "S11") - self.vna.create_new_trace("Trace2", "S12") - self.vna.create_new_trace("Trace3", "S21") - self.vna.create_new_trace("Trace4", "S22") + self.dut_signals_stable = [False, False] + self.postplot_obj = None self.measurement_plot = MeasurementPlot.MeasurementPlot(trace_subplot5=config_data['trace_subplot5']) @@ -170,13 +157,11 @@ class Measurements: def perform_single_measurement(self, output, target_temp, target_hum, soaking_time, n_stable_reads): with open(output, mode='w', newline='') as csv_file: fieldnames = ['TIMESTAMP', 'TARGET_TEMPERATURE', 'READBACK_TEMPERATURE', 'TARGET_HUMIDITY', - 'READBACK_HUMIDITY', 'RF_POWER', 'RF_FREQUENCY', 'DUT_IDENTIFIER', 'RUN_ID', - 'EQUILIBRIUM_INDICATOR', 'TEMP_HEATER', 'HUM_HEATER', - 'TEMP_DUT', 'TEMP_ROOM', 'TEMP_MEAS_INSTR', - 'HUM_DUT','HUM_ROOM', 'HUM_MEAS_INSTR', 'AIR_PRESS_ROOM', - 'READBACK_TEMP_MEAS_INSTR', 'READBACK_HUM_MEAS_INSTR', - 'S11_MAGNITUDE', 'S11_PHASE', 'S12_MAGNITUDE', - 'S12_PHASE', 'S21_MAGNITUDE', 'S21_PHASE', 'S22_MAGNITUDE', 'S22_PHASE'] + 'READBACK_HUMIDITY', 'DUT_IDENTIFIER', 'RUN_ID', 'EQUILIBRIUM_INDICATOR', 'TEMP_HEATER', + 'HUM_HEATER', 'TEMP_DUT', 'TEMP_ROOM', 'TEMP_MEAS_INSTR', 'HUM_DUT', 'HUM_ROOM', + 'HUM_MEAS_INSTR', 'AIR_PRESS_ROOM', 'READBACK_TEMP_MEAS_INSTR', 'READBACK_HUM_MEAS_INSTR'] + fieldnames.extend(self.dut.get_dut_signal_names()) + # csv.dict writer add adda row wise writer = csv.DictWriter(csv_file, fieldnames=fieldnames) writer.writeheader() @@ -196,8 +181,7 @@ class Measurements: next_read_time = self.clock.time() + self.sleep_time while do_another_measurement: # wait until set point is reached (+soaking time) - magnitudes_queue = [] - phase_queue = [] + dut_signal_queues = [[], []] while True: data = self.read_data() @@ -208,45 +192,35 @@ class Measurements: self.temperature_stable = self.calculate_temperature_stability(target_temp, float(data.temp)) self.humidity_stable = self.calculate_humidity_stability(target_hum, float(data.hum)) - # The queue must not be longer than the max number of soaking reads. - # If the queue is already full, we have to pop the first element before we can add the - # current measurement. - if len(magnitudes_queue) >= number_of_soaking_reads: - magnitudes_queue.pop(0) - if self.temperature_stable and self.humidity_stable: - magnitudes_queue.append(self.calculate_mean_magnitude_db(data.s21)) - else: - magnitudes_queue.clear() - # check cable stability parameters - self.magnitude_stable = False - if len(magnitudes_queue) >= number_of_soaking_reads: - spread = max(magnitudes_queue) - min(magnitudes_queue) - if spread < 2*self.max_delta_mag: - self.magnitude_stable = True - - if len(phase_queue) >= number_of_soaking_reads: - phase_queue.pop(0) - if self.temperature_stable and self.humidity_stable: - phase_queue.append(self.calculate_mean_phase(data.s21)) - else: - phase_queue.clear() - - self.phase_stable = False - if len(phase_queue) >= number_of_soaking_reads: - spread = max(phase_queue) - min(phase_queue) - if spread < 2*self.max_delta_phase: - self.phase_stable = True + # Use indexed loop because we need to modify, and zip seems to copy the content :-( + for i, signal_queue in enumerate(dut_signal_queues): + # The queue must not be longer than the max number of soaking reads. + # If the queue is already full, we have to pop the first element before we can add the + # current measurement. + if len(signal_queue) >= number_of_soaking_reads: + signal_queue.pop(0) + if self.temperature_stable and self.humidity_stable: + signal_queue.append(data.dut_data[self.dut.get_dut_reference_signal_names()[i]]) + else: + signal_queue.clear() + + self.dut_signals_stable[i] = False + if len(signal_queue) >= number_of_soaking_reads: + spread = max(signal_queue) - min(signal_queue) + if spread < 2*self.max_delta_dut_signals[i]: + self.dut_signals_stable[i] = True print('Setpoint: ' + str(target_temp) + ' ' + str(target_hum) + ' | Temp: ' + data.temp + ' °C' + ' | Humid: ' + data.hum + '%' - + ' | soaking read nr' + str(len(magnitudes_queue))) + + ' | soaking read nr' + str(len(dut_signal_queues[0]))) self.store_and_plot_data(target_temp, target_hum, data, self.cook_up_equi_indicator()) writer.writerow(self.data_collection[-1]) - if self.temperature_stable and self.humidity_stable and self.magnitude_stable and\ - self.phase_stable: - reference_magnitude = magnitudes_queue[-1] - reference_phase = phase_queue[-1] + if self.temperature_stable and self.humidity_stable and self.dut_signals_stable[0] and\ + self.dut_signals_stable[1]: + reference_values = [] + for signal_queue in dut_signal_queues: + reference_values.append(signal_queue[-1]) print('SOAKING FINISHED!') break else: @@ -261,17 +235,18 @@ class Measurements: data = self.read_data() self.temperature_stable = self.calculate_temperature_stability(target_temp, float(data.temp)) self.humidity_stable = self.calculate_humidity_stability(target_hum, float(data.hum)) - mag = self.calculate_mean_magnitude_db(data.s21) - phase = self.calculate_mean_phase(data.s21) - self.magnitude_stable = (reference_magnitude-self.max_delta_mag <= mag) and\ - (mag <= reference_magnitude+self.max_delta_mag) - self.phase_stable = (reference_phase-self.max_delta_phase <= phase) and\ - (phase <= reference_phase+self.max_delta_phase) + this_measurement_stable = (self.temperature_stable and self.humidity_stable) + for j, max_delta in enumerate(self.max_delta_dut_signals): + value = data.dut_data[self.dut.get_dut_reference_signal_names()[j]] + self.dut_signals_stable[j] = (reference_values[j] - max_delta <= value) and\ + (value <= reference_values[j] + max_delta) + if not self.dut_signals_stable[j]: + this_measurement_stable = False + self.store_and_plot_data(target_temp, target_hum, data, self.cook_up_equi_indicator()) supposedly_stable_measurements.append(self.data_collection[-1]) - if (self.temperature_stable and self.humidity_stable and self.magnitude_stable and - self.phase_stable): + if this_measurement_stable: print('Stable measurement ' + str(i+1) + '/' + str(n_stable_reads)) self.sleep_until(next_read_time) next_read_time += self.sleep_time @@ -282,8 +257,8 @@ class Measurements: for measurement in supposedly_stable_measurements: if all_measurements_stable: - measurement['EQUILIBRIUM_INDICATOR'] = TEMPERATURE_STABLE | HUMIDITY_STABLE |\ - MAGNITUDE_STABLE | PHASE_STABLE |\ + measurement['EQUILIBRIUM_INDICATOR'] = TEMPERATURE_STABLE | HUMIDITY_STABLE | \ + DUT_SIGNAL0_STABLE | DUT_SIGNAL1_STABLE | \ MEASUREMENT_STABLE do_another_measurement = False @@ -298,35 +273,15 @@ class Measurements: [temp, hum, mode, alarms] = self.chamber.read_monitor().split(',') perc_temp_heater, perc_hum_heater = self.chamber.get_heater_percentage() - # loop with 10 iterations in case of VNA readout error - iteration = 0 - while iteration < 10: - try: - power = self.vna.get_current_power() - frequency = self.vna.get_current_cw_frequency() - self.vna.do_single_sweep() - s11 = self.get_trace_data("Trace1") - s12 = self.get_trace_data("Trace2") - s21 = self.get_trace_data("Trace3") - s22 = self.get_trace_data("Trace4") - # if readout is successful, leave loop - break - # exception for pyvisa error or timeout - except pyvisa.errors.VisaIOError: - # call reset function for VNA status register and error queue - self.vna.reset_status() - print('An error occurred during VNA read out') - # wait one second for next try - time.sleep(1) - iteration += 1 + dut_data = self.dut.get_dut_measurements() temp_dut, temp_room, temp_meas_instr, hum_dut, hum_room, hum_meas_instr, air_press_room = \ self.ext_sensors.get_sensor_values() [temp_chamber_meas_instr, hum_chamber_meas_instr, mode_meas_instr, alarms_meas_instr] = \ self.instr_chamber.read_monitor().split(',') - - return MeasurementData(int(self.clock.time()), temp, hum, power, frequency, s11, s21, s12, s22, + + return MeasurementData(int(self.clock.time()), temp, hum, dut_data, perc_temp_heater, perc_hum_heater, temp_dut, temp_room, temp_meas_instr, hum_dut, hum_room, hum_meas_instr, air_press_room, temp_chamber_meas_instr, hum_chamber_meas_instr) @@ -338,11 +293,9 @@ class Measurements: 'READBACK_TEMPERATURE': float(data.temp), 'TARGET_HUMIDITY': target_hum, 'READBACK_HUMIDITY': float(data.hum), - 'RF_POWER': data.power, - 'RF_FREQUENCY': data.frequency, 'EQUILIBRIUM_INDICATOR': equi_indicator, - 'TEMP_HEATER': data.perc_temp_heater, - 'HUM_HEATER': data.perc_hum_heater, + 'TEMP_HEATER': data.percent_temp_heater, + 'HUM_HEATER': data.percent_hum_heater, 'TEMP_DUT': data.temp_dut, 'TEMP_ROOM': data.temp_room, 'TEMP_MEAS_INSTR': data.temp_meas_instr, @@ -352,15 +305,8 @@ class Measurements: 'AIR_PRESS_ROOM': data.air_press_room, 'READBACK_TEMP_MEAS_INSTR': float(data.temp_chamber_meas_instr), 'READBACK_HUM_MEAS_INSTR': float(data.hum_chamber_meas_instr), - 'S11_PHASE': self.calculate_mean_phase(data.s11), - 'S11_MAGNITUDE': self.calculate_mean_magnitude_db(data.s11), - 'S21_PHASE': self.calculate_mean_phase(data.s21), - 'S21_MAGNITUDE': self.calculate_mean_magnitude_db(data.s21), - 'S12_PHASE': self.calculate_mean_phase(data.s12), - 'S12_MAGNITUDE': self.calculate_mean_magnitude_db(data.s12), - 'S22_PHASE': self.calculate_mean_phase(data.s22), - 'S22_MAGNITUDE': self.calculate_mean_magnitude_db(data.s22) } + measurement.update(data.dut_data) self.data_collection.append(measurement) data_frame = pd.DataFrame(self.data_collection) self.measurement_plot.draw(data_frame) @@ -368,38 +314,6 @@ class Measurements: def current_milli_time(self): return int(round(self.clock.time() * 1000)) - def get_trace_data(self, trace): - return self.vna.get_list_of_measurement_values(trace, "SDAT") - - def calculate_complex_numbers(self, values_list): - real_num = values_list[::2] - imaginary_num = values_list[1::2] - complex_numbers = [] - for i, q in zip(real_num, imaginary_num): - complex_numbers.append(complex(i, q)) - return complex_numbers - - def calculate_magnitudes(self, values_list): - complex_numbers = self.calculate_complex_numbers(values_list) - magnitudes = [abs(val) for val in complex_numbers] - return magnitudes - - def calculate_mean_magnitude(self, values_list): - magnitudes = self.calculate_magnitudes(values_list) - return numpy.mean(magnitudes) - - def calculate_mean_magnitude_db(self, values_list): - return 20*math.log10(self.calculate_mean_magnitude(values_list)) - - def calculate_phases(self, values_list): - complex_numbers = self.calculate_complex_numbers(values_list) - phases = [math.degrees(cmath.phase(val)) for val in complex_numbers] - return phases - - def calculate_mean_phase(self, values_list): - phases = self.calculate_phases(values_list) - return numpy.mean(phases) - def cook_up_equi_indicator(self): equilibrium_indicator = 0 @@ -409,11 +323,11 @@ class Measurements: if self.humidity_stable: equilibrium_indicator = equilibrium_indicator | HUMIDITY_STABLE - if self.magnitude_stable: - equilibrium_indicator = equilibrium_indicator | MAGNITUDE_STABLE + if self.dut_signals_stable[0]: + equilibrium_indicator = equilibrium_indicator | DUT_SIGNAL0_STABLE - if self.phase_stable: - equilibrium_indicator = equilibrium_indicator | PHASE_STABLE + if self.dut_signals_stable[1]: + equilibrium_indicator = equilibrium_indicator | DUT_SIGNAL1_STABLE return equilibrium_indicator @@ -474,12 +388,6 @@ def run_temperature_sweep_from_file(temperature_sweep_file, meas): if __name__ == '__main__': parser = ArgumentParser() - parser.add_argument("-c", "--chamber", - help="IP address of climate chamber", metavar="ADDR", - required=True) - parser.add_argument("-v", "--vna", - help="IP address of VNA", metavar="ADDR", - required=True) parser.add_argument('-f', '--file', help='File containing custom list of measurements', default='') @@ -509,7 +417,7 @@ if __name__ == '__main__': else: output_basename = args.output - print(args.chamber, args.vna, args.file, output_basename, args.standby) + print(args.file, output_basename, args.standby) # reading json file for target accuracy with open('test_stand_parameter.json', 'r') as f: @@ -518,8 +426,7 @@ if __name__ == '__main__': with open('ext_sensor_channels.json', 'r') as f2: ext_sensor_channels = json.load(f2) - mes = Measurements(args.chamber, args.vna, output_basename, args.standby, config_data, ext_sensor_channels, - config_data['logger_ip']) + mes = Measurements(config_data, output_basename, args.standby, ext_sensor_channels) try: if args.file: n_measurements = mes.perform_measurements(args.file) diff --git a/Python_script/test_stand_parameter.json b/Python_script/test_stand_parameter.json index c32eb2a..11fabf9 100644 --- a/Python_script/test_stand_parameter.json +++ b/Python_script/test_stand_parameter.json @@ -1 +1,20 @@ -{"delta_temp": 0.1, "delta_hum": 1, "delta_mag": 0.13 , "delta_phase": 1.5, "sleep_time": 10.0, "frequency": 1300000000, "vna_config_file": "CalSetup2.znxml","chamber_ip":"192.168.115.186", "instr_chamber_ip": "192.168.115.187", "vna_ip":"192.168.115.39", "data_folder":"measurements", "logger_ip": "192.168.115.94", "time_unit": "min", "trace_subplot5": "logger_sens", "logger_model": "710"} +{ + "delta_temp": 0.1, + "delta_hum": 1, + "dut": { + "type": "VNA", + "delta_mag": 0.13, + "delta_phase": 1.5, + "frequency": 1300000000, + "vna_ip": "192.168.115.39", + "vna_config_file": "CalSetup2.znxml" + }, + "sleep_time": 10, + "chamber_ip": "192.168.115.186", + "instr_chamber_ip": "192.168.115.187", + "data_folder": "measurements", + "logger_ip": "192.168.115.94", + "time_unit": "min", + "trace_subplot5": "logger_sens", + "logger_model": "710" +} diff --git a/Python_script/vna_measurement.py b/Python_script/vna_measurement.py new file mode 100644 index 0000000..8185049 --- /dev/null +++ b/Python_script/vna_measurement.py @@ -0,0 +1,106 @@ +import VNA +import pyvisa +import time +import numpy +import math +import cmath + +import dut_measurement + +class VnaData: + def __init__(self, power, frequency, s11, s21, s12, s22): + self.power = power + self.frequency = frequency + self.s11 = s11 + self.s21 = s21 + self.s12 = s12 + self.s22 = s22 + + +class VnaMeasurement(dut_measurement.DutMeasurement): + def __init__(self, config_data, target_accuracy): + self.delta_mag = config_data['delta_mag'] + self.delta_phase = config_data['delta_phase'] + + self.vna = VNA.create_vna(config_data['vna_ip'], target_accuracy) + + self.vna.load_config(config_data['vna_config_file'], config_data['frequency']) + self.vna.create_new_trace("Trace1", "S11") + self.vna.create_new_trace("Trace2", "S12") + self.vna.create_new_trace("Trace3", "S21") + self.vna.create_new_trace("Trace4", "S22") + + def _get_trace_data(self, trace): + return self.vna.get_list_of_measurement_values(trace, "SDAT") + + def get_dut_measurements(self): + # FIXME: The try/catch should be way down in the VNA class + for iteration in range(10): + try: + power = self.vna.get_current_power() + frequency = self.vna.get_current_cw_frequency() + self.vna.do_single_sweep() + s11 = self._get_trace_data("Trace1") + s12 = self._get_trace_data("Trace2") + s21 = self._get_trace_data("Trace3") + s22 = self._get_trace_data("Trace4") + + return {'RF_POWER': power, 'RF_FREQUENCY': frequency, + 'S11_MAGNITUDE': self.calculate_mean_magnitude_db(s11), + 'S11_PHASE': self.calculate_mean_phase(s11), + 'S12_MAGNITUDE': self.calculate_mean_magnitude_db(s12), + 'S12_PHASE': self.calculate_mean_phase(s12), + 'S21_MAGNITUDE': self.calculate_mean_magnitude_db(s21), + 'S21_PHASE': self.calculate_mean_phase(s21), + 'S22_MAGNITUDE': self.calculate_mean_magnitude_db(s22), + 'S22_PHASE': self.calculate_mean_phase(s21)} + + # exception for pyvisa error or timeout + except pyvisa.errors.VisaIOError: + # call reset function for VNA status register and error queue + self.vna.reset_status() + print('An error occurred during VNA read out') + # wait one second for next try + time.sleep(1) + # FIXME: In case we did not succeed we need something like a stop_measurement exception + + raise Exception('FIXME: Throw stop_measurement here, and handle it. Don\'t crash!') + + def get_dut_signal_names(self): + return ['RF_POWER', 'RF_FREQUENCY', 'S11_MAGNITUDE', 'S11_PHASE', 'S12_MAGNITUDE', + 'S12_PHASE', 'S21_MAGNITUDE', 'S21_PHASE', 'S22_MAGNITUDE', 'S22_PHASE'] + + def get_dut_reference_signal_names(self): + return ['S21_MAGNITUDE', 'S21_PHASE'] + + def get_dut_max_delta_signals(self): + return [self.delta_mag, self.delta_phase] + + def calculate_complex_numbers(self, values_list): + real_num = values_list[::2] + imaginary_num = values_list[1::2] + complex_numbers = [] + for i, q in zip(real_num, imaginary_num): + complex_numbers.append(complex(i, q)) + return complex_numbers + + def calculate_magnitudes(self, values_list): + complex_numbers = self.calculate_complex_numbers(values_list) + magnitudes = [abs(val) for val in complex_numbers] + return magnitudes + + def calculate_mean_magnitude(self, values_list): + magnitudes = self.calculate_magnitudes(values_list) + return numpy.mean(magnitudes) + + def calculate_mean_magnitude_db(self, values_list): + return 20*math.log10(self.calculate_mean_magnitude(values_list)) + + def calculate_phases(self, values_list): + complex_numbers = self.calculate_complex_numbers(values_list) + phases = [math.degrees(cmath.phase(val)) for val in complex_numbers] + return phases + + def calculate_mean_phase(self, values_list): + phases = self.calculate_phases(values_list) + return numpy.mean(phases) -- GitLab