Merge branch 'main' of github.com:nqrduck/nqrduck-autotm

This commit is contained in:
jupfi 2024-02-02 10:53:19 +01:00
commit 236b373b96
6 changed files with 1759 additions and 456 deletions

View file

@ -1,4 +1,5 @@
import logging
import time
import numpy as np
import json
import time
@ -6,9 +7,10 @@ from serial.tools.list_ports import comports
from PyQt6.QtTest import QTest
from PyQt6 import QtSerialPort
from PyQt6.QtCore import QThread, pyqtSignal, pyqtSlot, Qt
from PyQt6.QtCore import QTimer
from PyQt6.QtWidgets import QApplication
from nqrduck.module.module_controller import ModuleController
from .model import S11Data, LookupTable
from .model import S11Data, ElectricalLookupTable, MechanicalLookupTable, SavedPosition, Stepper
logger = logging.getLogger(__name__)
@ -16,6 +18,60 @@ logger = logging.getLogger(__name__)
class AutoTMController(ModuleController):
BAUDRATE = 115200
def on_loading(self):
"""This method is called when the module is loaded.
It sets up the serial connection and connects the signals and slots.
"""
logger.debug("Setting up serial connection")
self.find_devices()
# Connect signals
self.module.model.serial_data_received.connect(self.process_frequency_sweep_data)
self.module.model.serial_data_received.connect(self.process_measurement_data)
self.module.model.serial_data_received.connect(self.process_calibration_data)
self.module.model.serial_data_received.connect(self.process_voltage_sweep_result)
self.module.model.serial_data_received.connect(self.print_info)
self.module.model.serial_data_received.connect(self.read_position_data)
self.module.model.serial_data_received.connect(self.process_reflection_data)
self.module.model.serial_data_received.connect(self.process_position_sweep_result)
self.module.model.serial_data_received.connect(self.process_signalpath_data)
@pyqtSlot(str, object)
def process_signals(self, key: str, value: object) -> None:
logger.debug("Received signal: %s", key)
if key == "set_tune_and_match":
self.tune_and_match(value)
def tune_and_match(self, frequency: float) -> None:
""" This method is called when this module already has a LUT table. It should then tune and match the probe coil to the specified frequency.
"""
if self.module.model.LUT is None:
logger.error("Could not tune and match. No LUT available.")
return
elif self.module.model.LUT.TYPE == "Electrical":
tuning_voltage, matching_voltage = self.module.model.LUT.get_voltages(frequency)
confirmation = self.set_voltages(str(tuning_voltage), str(matching_voltage))
# We need to change the signal pathway to preamp to measure the reflection
self.switch_to_atm()
reflection = self.read_reflection(frequency)
# We need to change the signal pathway back to atm to perform a measurement
self.switch_to_preamp()
self.module.nqrduck_signal.emit("confirm_tune_and_match", reflection)
elif self.module.model.LUT.TYPE == "Mechanical":
tuning_position, matching_position = self.module.model.LUT.get_positions(frequency)
self.go_to_position(tuning_position, matching_position)
self.switch_to_atm()
# Switch to atm to measure the reflection
reflection = self.read_reflection(frequency)
# Switch back to preamp to perform a measurement
self.switch_to_preamp()
# The Lime doesn"t like it if we send the command to switch to atm and then immediately send the command to measure the reflection.
# So we wait a bit before starting the measurement
QTimer.singleShot(100, lambda: self.module.nqrduck_signal.emit("confirm_tune_and_match", reflection))
def find_devices(self) -> None:
"""Scan for available serial devices and add them to the model as available devices."""
logger.debug("Scanning for available serial devices")
@ -62,6 +118,12 @@ class AutoTMController(ModuleController):
self.module.model.serial = serial
logger.debug("Connected to device %s", device)
# On opening of the command we set the switch position to atm
self.switch_to_atm()
self.set_voltages("0", "0")
except Exception as e:
logger.error("Could not connect to device %s: %s", device, e)
@ -79,7 +141,7 @@ class AutoTMController(ModuleController):
MAX_FREQUENCY = 200e6 # Hz
try:
start_frequence = start_frequency.replace(",", ".")
start_frequency = start_frequency.replace(",", ".")
stop_frequency = stop_frequency.replace(",", ".")
start_frequency = float(start_frequency) * 1e6
stop_frequency = float(stop_frequency) * 1e6
@ -130,98 +192,177 @@ class AutoTMController(ModuleController):
self.module.model.clear_data_points()
self.module.view.create_frequency_sweep_spinner_dialog()
@pyqtSlot(str)
def process_frequency_sweep_data(self, text : str) -> None:
"""This method is called when data is received from the serial connection during a frequency sweep.
It processes the data and adds it to the model.
"""
if text.startswith("f") and self.module.view.frequency_sweep_spinner.isVisible():
text = text[1:].split("r")
frequency = float(text[0])
return_loss, phase = map(float, text[1].split("p"))
self.module.model.add_data_point(frequency, return_loss, phase)
@pyqtSlot(str)
def process_measurement_data(self, text : str) -> None:
"""This method is called when data is received from the serial connection during a measurement.
It processes the data and adds it to the model.
"""
if self.module.model.active_calibration is None and text.startswith("r"):
logger.debug("Measurement finished")
self.module.model.measurement = S11Data(
self.module.model.data_points.copy()
)
self.finish_frequency_sweep()
@pyqtSlot(str)
def process_calibration_data(self, text : str) -> None:
"""This method is called when data is received from the serial connection during a calibration.
It processes the data and adds it to the model.
Args:
calibration_type (str): The type of calibration that is being performed.
"""
if text.startswith("r") and self.module.model.active_calibration in ["short", "open", "load"]:
calibration_type = self.module.model.active_calibration
logger.debug(f"{calibration_type.capitalize()} calibration finished")
setattr(self.module.model, f"{calibration_type}_calibration",
S11Data(self.module.model.data_points.copy()))
self.module.model.active_calibration = None
self.module.view.frequency_sweep_spinner.hide()
@pyqtSlot(str)
def process_voltage_sweep_result(self, text : str) -> None:
"""This method is called when data is received from the serial connection during a voltage sweep.
It processes the data and adds it to the model.
Args:
text (str): The data received from the serial connection.
"""
if text.startswith("v"):
text = text[1:].split("t")
tuning_voltage, matching_voltage = map(float, text)
LUT = self.module.model.el_lut
if LUT is not None:
if LUT.is_incomplete():
logger.debug("Received voltage sweep result: Tuning %s Matching %s", tuning_voltage, matching_voltage)
LUT.add_voltages(tuning_voltage, matching_voltage)
self.continue_or_finish_voltage_sweep(LUT)
self.module.model.tuning_voltage = tuning_voltage
self.module.model.matching_voltage = matching_voltage
logger.debug("Updated voltages: Tuning %s Matching %s", self.module.model.tuning_voltage, self.module.model.matching_voltage)
def finish_frequency_sweep(self):
"""This method is called when a frequency sweep is finished.
It hides the frequency sweep spinner dialog and adds the data to the model.
"""
self.module.view.frequency_sweep_spinner.hide()
self.module.model.frequency_sweep_stop = time.time()
duration = self.module.model.frequency_sweep_stop - self.module.model.frequency_sweep_start
self.module.view.add_info_text(f"Frequency sweep finished in {duration:.2f} seconds")
def continue_or_finish_voltage_sweep(self, LUT):
"""This method is called when a voltage sweep is finished.
It checks if the voltage sweep is finished or if the next voltage sweep should be started.
Args:
LUT (LookupTable): The lookup table that is being generated.
"""
if LUT.is_incomplete():
# Start the next voltage sweep
self.start_next_voltage_sweep(LUT)
else:
# Finish voltage sweep
self.finish_voltage_sweep(LUT)
def start_next_voltage_sweep(self, LUT):
"""This method is called when a voltage sweep is finished.
It starts the next voltage sweep.
Args:
LUT (LookupTable): The lookup table that is being generated.
"""
next_frequency = LUT.get_next_frequency()
# We write the first command to the serial connection
if self.module.view._ui_form.prevVoltagecheckBox.isChecked():
# Command format is s<frequency in MHz>o<optional tuning voltage>o<optional matching voltage>
# We use the currently set voltages
command = "s%so%so%s" % (next_frequency, self.module.model.tuning_voltage, self.module.model.matching_voltage)
else:
command = "s%s" % (next_frequency)
LUT.started_frequency = next_frequency
logger.debug("Starting next voltage sweep: %s", command)
self.send_command(command)
def finish_voltage_sweep(self, LUT):
"""This method is called when a voltage sweep is finished.
It hides the voltage sweep spinner dialog and adds the data to the model.
Args:
LUT (LookupTable): The lookup table that is being generated."""
logger.debug("Voltage sweep finished")
self.module.view.el_LUT_spinner.hide()
self.module.model.LUT = LUT
self.module.model.voltage_sweep_stop = time.time()
duration = self.module.model.voltage_sweep_stop - self.module.model.voltage_sweep_start
self.module.view.add_info_text(f"Voltage sweep finished in {duration:.2f} seconds")
self.module.nqrduck_signal.emit("LUT_finished", LUT)
@pyqtSlot(str)
def print_info(self, text : str) -> None:
"""This method is called when data is received from the serial connection.
It prints the data to the info text box.
Args:
text (str): The data received from the serial connection.
"""
if text.startswith("i"):
text = text[1:]
self.module.view.add_info_text(text)
elif text.startswith("e"):
text = text[1:]
self.module.view.add_error_text(text)
@pyqtSlot(str)
def read_position_data(self, text : str) -> None:
"""This method is called when data is received from the serial connection."""
if text.startswith("p"):
# Format is p<tuning_position>m<matching_position>
text = text[1:].split("m")
tuning_position, matching_position = map(int, text)
self.module.model.tuning_stepper.position = tuning_position
self.module.model.matching_stepper.position = matching_position
self.module.model.tuning_stepper.homed = True
self.module.model.matching_stepper.homed = True
logger.debug("Tuning position: %s, Matching position: %s", tuning_position, matching_position)
self.module.view.on_active_stepper_changed()
def on_ready_read(self) -> None:
"""This method is called when data is received from the serial connection."""
serial = self.module.model.serial
while serial.canReadLine():
text = serial.readLine().data().decode()
text = text.rstrip("\r\n")
# logger.debug("Received data: %s", text)
# If the text starts with 'f' and the frequency sweep spinner is visible we know that the data is a data point
# then we have the data for the return loss and the phase at a certain frequency
if (
text.startswith("f")
and self.module.view.frequency_sweep_spinner.isVisible()
):
text = text[1:].split("r")
frequency = float(text[0])
return_loss, phase = map(float, text[1].split("p"))
self.module.model.add_data_point(frequency, return_loss, phase)
# If the text starts with 'r' and no calibration is active we know that the data is a measurement
elif text.startswith("r") and self.module.model.active_calibration == None:
logger.debug("Measurement finished")
self.module.model.measurement = S11Data(
self.module.model.data_points.copy()
)
self.module.view.frequency_sweep_spinner.hide()
self.module.model.frequency_sweep_stop = time.time()
self.module.view.add_info_text(
"Frequency sweep finished in %.2f seconds"
% (
self.module.model.frequency_sweep_stop
- self.module.model.frequency_sweep_start
)
)
# If the text starts with 'r' and a short calibration is active we know that the data is a short calibration
elif (
text.startswith("r") and self.module.model.active_calibration == "short"
):
logger.debug("Short calibration finished")
self.module.model.short_calibration = S11Data(
self.module.model.data_points.copy()
)
self.module.model.active_calibration = None
self.module.view.frequency_sweep_spinner.hide()
# If the text starts with 'r' and an open calibration is active we know that the data is an open calibration
elif (
text.startswith("r") and self.module.model.active_calibration == "open"
):
logger.debug("Open calibration finished")
self.module.model.open_calibration = S11Data(
self.module.model.data_points.copy()
)
self.module.model.active_calibration = None
self.module.view.frequency_sweep_spinner.hide()
# If the text starts with 'r' and a load calibration is active we know that the data is a load calibration
elif (
text.startswith("r") and self.module.model.active_calibration == "load"
):
logger.debug("Load calibration finished")
self.module.model.load_calibration = S11Data(
self.module.model.data_points.copy()
)
self.module.model.active_calibration = None
self.module.view.frequency_sweep_spinner.hide()
# If the text starts with 'i' we know that the data is an info message
elif text.startswith("i"):
text = "ATM Info: " + text[1:]
self.module.view.add_info_text(text)
# If the text starts with 'e' we know that the data is an error message
elif text.startswith("e"):
text = "ATM Error: " + text[1:]
self.module.view.add_info_text(text)
# If the text starts with 'v' we know that the data is a voltage sweep result
elif text.startswith("v"):
text = text[1:]
text = text.split("t")
matching_voltage = float(text[0])
tuning_voltage = float(text[1])
# Now we add the datapoint to the current LUT
LUT = self.module.model.LUT
logger.debug(
"Received voltage sweep result: %s %s",
matching_voltage,
tuning_voltage,
)
LUT.add_voltages(matching_voltage, tuning_voltage)
text = serial.readLine().data().decode().rstrip("\r\n")
logger.debug("Received data: %s", text)
# Start the next voltage sweep if there are more voltages to sweep
if LUT.is_incomplete():
next_frequency = LUT.get_next_frequency()
command = "s%s" % next_frequency
LUT.started_frequency = next_frequency
logger.debug("Starting next voltage sweep: %s", command)
self.send_command(command)
self.module.model.serial_data_received.emit(text)
@pyqtSlot(str)
def process_reflection_data(self, text):
"""This method is called when data is received from the serial connection.
It processes the data and adds it to the model.
Args:
text (str): The data received from the serial connection.
"""
if text.startswith("m"):
text = text[1:]
return_loss, phase = map(float, text.split("p"))
self.module.model.last_reflection = (return_loss, phase)
### Calibration Stuff ###
def on_short_calibration(
self, start_frequency: float, stop_frequency: float
@ -370,6 +511,37 @@ class AutoTMController(ModuleController):
self.module.model.open_calibration = S11Data.from_json(data["open"])
self.module.model.load_calibration = S11Data.from_json(data["load"])
def save_measurement(self, filename: str) -> None:
"""Save measurement to file.
Args:
filename (str): Path to file.
"""
logger.debug("Saving measurement.")
if not self.module.model.measurement:
logger.debug("No measurement to save.")
return
measurement = self.module.model.measurement.to_json()
with open(filename, "w") as f:
json.dump(measurement, f)
def load_measurement(self, filename: str) -> None:
"""Load measurement from file.
Args:
filename (str): Path to file.
"""
logger.debug("Loading measurement.")
with open(filename, "r") as f:
measurement = json.load(f)
self.module.model.measurement = S11Data.from_json(measurement)
### Voltage Control ###
def set_voltages(self, tuning_voltage: str, matching_voltage: str) -> None:
"""This method is called when the set voltages button is pressed.
It writes the specified tuning and matching voltage to the serial connection.
@ -380,6 +552,8 @@ class AutoTMController(ModuleController):
"""
logger.debug("Setting voltages")
MAX_VOLTAGE = 5 # V
timeout_duration = 15 # timeout in seconds
try:
tuning_voltage = tuning_voltage.replace(",", ".")
matching_voltage = matching_voltage.replace(",", ".")
@ -411,15 +585,35 @@ class AutoTMController(ModuleController):
matching_voltage,
)
command = "v%sv%s" % (matching_voltage, tuning_voltage)
self.send_command(command)
if tuning_voltage == self.module.model.tuning_voltage and matching_voltage == self.module.model.matching_voltage:
logger.debug("Voltages already set")
return
command = "v%sv%s" % (tuning_voltage, matching_voltage)
start_time = time.time()
def generate_lut(
confirmation = self.send_command(command)
while matching_voltage != self.module.model.matching_voltage and tuning_voltage != self.module.model.tuning_voltage:
QApplication.processEvents()
# Check for timeout
if time.time() - start_time > timeout_duration:
logger.error("Voltage setting timed out")
break
logger.debug("Voltages set successfully")
return confirmation
else:
logger.error("Could not set voltages")
return confirmation
### Electrical Lookup Table ###
def generate_electrical_lut(
self,
start_frequency: str,
stop_frequency: str,
frequency_step: str,
voltage_resolution: str,
) -> None:
"""This method is called when the generate LUT button is pressed.
It generates a lookup table for the specified frequency range and voltage resolution.
@ -428,20 +622,17 @@ class AutoTMController(ModuleController):
start_frequency (str): The start frequency in Hz.
stop_frequency (str): The stop frequency in Hz.
frequency_step (str): The frequency step in Hz.
voltage_resolution (str): The voltage resolution in V.
"""
logger.debug("Generating LUT")
try:
start_frequency = start_frequency.replace(",", ".")
stop_frequency = stop_frequency.replace(",", ".")
frequency_step = frequency_step.replace(",", ".")
voltage_resolution = voltage_resolution.replace(",", ".")
start_frequency = float(start_frequency)
stop_frequency = float(stop_frequency)
frequency_step = float(frequency_step)
voltage_resolution = float(voltage_resolution)
except ValueError:
error = "Could not generate LUT. Start frequency, stop frequency, frequency step and voltage resolution must be floats"
error = "Could not generate LUT. Start frequency, stop frequency, frequency step must be floats"
logger.error(error)
self.module.view.add_info_text(error)
return
@ -450,9 +641,8 @@ class AutoTMController(ModuleController):
start_frequency < 0
or stop_frequency < 0
or frequency_step < 0
or voltage_resolution < 0
):
error = "Could not generate LUT. Start frequency, stop frequency, frequency step and voltage resolution must be positive"
error = "Could not generate LUT. Start frequency, stop frequency, frequency step must be positive"
logger.error(error)
self.module.view.add_info_text(error)
return
@ -463,48 +653,101 @@ class AutoTMController(ModuleController):
self.module.view.add_info_text(error)
return
if frequency_step > (stop_frequency - start_frequency):
# - 0.1 is to prevent float errors
if frequency_step - 0.1 > (stop_frequency - start_frequency):
error = "Could not generate LUT. Frequency step must be smaller than the frequency range"
logger.error(error)
self.module.view.add_info_text(error)
return
logger.debug(
"Generating LUT from %s MHz to %s MHz with a frequency step of %s MHz and a voltage resolution of %s V",
"Generating LUT from %s MHz to %s MHz with a frequency step of %s MHz",
start_frequency,
stop_frequency,
frequency_step,
voltage_resolution,
)
self.switch_to_atm()
# self.set_voltages("0", "0")
# We create the lookup table
LUT = LookupTable(
start_frequency, stop_frequency, frequency_step, voltage_resolution
LUT = ElectricalLookupTable(
start_frequency, stop_frequency, frequency_step
)
LUT.started_frequency = start_frequency
self.module.model.LUT = LUT
# We write the first command to the serial connection
command = "s%s" % (start_frequency)
if self.module.view._ui_form.prevVoltagecheckBox.isChecked():
# Command format is s<frequency in MHz>o<optional tuning voltage>o<optional matching voltage>
# We use the currently set voltages
logger.debug("Starting preset Voltage sweep with voltage Tuning: %s V and Matching: %s V", self.module.model.tuning_voltage, self.module.model.matching_voltage)
command = "s%so%so%s" % (start_frequency, self.module.model.tuning_voltage, self.module.model.matching_voltage)
else:
command = "s%s" % (start_frequency)
# For timing of the voltage sweep
self.module.model.voltage_sweep_start = time.time()
confirmation = self.send_command(command)
if not confirmation:
return
# If the command was send successfully, we set the LUT
if confirmation:
self.module.model.el_lut = LUT
self.module.view.create_el_LUT_spinner_dialog()
def switch_to_preamp(self) -> None:
"""This method is used to send the command 'cp' to the atm system. This switches the signal pathway of the atm system to 'RX' to 'Preamp'.
This is the mode for either NQR or NMR measurements or if on wants to check the tuning of the probe coil on a network analyzer.
"""
if self.module.model.signal_path == "preamp":
logger.debug("Already in preamp")
return
TIMEOUT = 1 # s
logger.debug("Switching to preamp")
self.send_command("cp")
start_time = time.time()
while self.module.model.signal_path != "preamp":
QApplication.processEvents()
# Check for timeout
if time.time() - start_time > TIMEOUT:
logger.error("Switching to preamp timed out")
break
def switch_to_atm(self) -> None:
"""This method is used to send the command 'ca' to the atm system. This switches the signal pathway of the atm system to 'RX' to 'ATM.
In this state the atm system can be used to measure the reflection coefficient of the probecoils.
"""
if self.module.model.signal_path == "atm":
logger.debug("Already in atm mode")
return
TIMEOUT = 1 # s
logger.debug("Switching to atm")
self.send_command("ca")
start_time = time.time()
while self.module.model.signal_path != "atm":
QApplication.processEvents()
# Check for timeout
if time.time() - start_time > TIMEOUT:
logger.error("Switching to atm timed out")
break
def process_signalpath_data(self, text : str) -> None:
"""This method is called when data is received from the serial connection.
It processes the data and adds it to the model.
Args:
text (str): The data received from the serial connection.
"""
if text.startswith("c"):
text = text[1:]
if text == "p":
self.module.model.signal_path = "preamp"
elif text == "a":
self.module.model.signal_path = "atm"
def send_command(self, command: str) -> bool:
"""This method is used to send a command to the active serial connection.
@ -544,7 +787,7 @@ class AutoTMController(ModuleController):
logger.debug("Confirmation: %s", confirmation)
if confirmation == "c":
logger.debug("Command send successfully")
logger.debug("Command sent successfully")
return True
else:
logger.error("Could not send command. No confirmation received")
@ -557,9 +800,378 @@ class AutoTMController(ModuleController):
logger.error("Could not send command. %s", e)
self.module.view.add_error_text("Could not send command. %s" % e)
### Stepper Motor Control ###
def homing(self) -> None:
"""This method is used to send the command 'h' to the atm system.
This command is used to home the stepper motors of the atm system.
"""
logger.debug("Homing")
self.send_command("h")
self.module.model.tuning_stepper.last_direction = 1
self.module.model.matching_stepper.last_direction = 1
@pyqtSlot(str)
def on_stepper_changed(self, stepper: str) -> None:
"""This method is called when the stepper position is changed.
It sends the command to the atm system to change the stepper position.
Args:
stepper (str): The stepper that is being changed. Either 'tuning' or 'matching'.
"""
logger.debug("Stepper %s changed", stepper)
stepper = stepper.lower()
if stepper == "tuning":
self.module.model.active_stepper = self.module.model.tuning_stepper
elif stepper == "matching":
self.module.model.active_stepper = self.module.model.matching_stepper
def validate_position(self, future_position: int, stepper : Stepper) -> bool:
"""Validate the stepper's future position."""
if future_position < 0:
self.module.view.add_error_text("Could not move stepper. Stepper position cannot be negative")
return False
if future_position > stepper.MAX_STEPS:
self.module.view.add_error_text(f"Could not move stepper. Stepper position cannot be larger than {stepper.MAX_STEPS}")
return False
return True
def calculate_steps_for_absolute_move(self, target_position: int, stepper : Stepper) -> int:
"""Calculate the number of steps for an absolute move."""
current_position = stepper.position
return target_position - current_position
def send_stepper_command(self, steps: int, stepper : Stepper) -> None:
"""Send a command to the stepper motor based on the number of steps."""
# Here we handle backlash of the tuner
# Determine the direction of the current steps
backlash = 0
current_direction = np.sign(steps) # This will be -1,or 1
if stepper.TYPE == "Tuning":
logger.debug("Stepper last direction: %s", stepper.last_direction)
logger.debug("Current direction: %s", current_direction)
if stepper.last_direction != current_direction:
backlash = stepper.BACKLASH_STEPS * current_direction
stepper.last_direction = current_direction
logger.debug("Stepper last direction: %s", stepper.last_direction)
motor_identifier = stepper.TYPE.lower()[0]
command = f"m{motor_identifier}{steps},{backlash}"
confirmation = self.send_command(command)
return confirmation
def on_relative_move(self, steps: str, stepper: Stepper = None) -> None:
"""This method is called when the relative move button is pressed."""
timeout_duration = 15 # timeout in seconds
start_time = time.time()
if stepper is None:
stepper = self.module.model.active_stepper
stepper_position = stepper.position
future_position = stepper.position + int(steps)
if future_position == stepper_position:
logger.debug("Stepper already at position")
return
if self.validate_position(future_position, stepper):
confirmation = self.send_stepper_command(int(steps), stepper) # Convert the steps string to an integer
while stepper_position == stepper.position:
QApplication.processEvents()
# Check for timeout
if time.time() - start_time > timeout_duration:
logger.error("Relative move timed out")
break # or handle timeout differently
return confirmation
def on_absolute_move(self, steps: str, stepper: Stepper = None) -> None:
"""This method is called when the absolute move button is pressed."""
timeout_duration = 15 # timeout in seconds
start_time = time.time()
if stepper is None:
stepper = self.module.model.active_stepper
stepper_position = stepper.position
future_position = int(steps)
if future_position == stepper_position:
logger.debug("Stepper already at position")
return
if self.validate_position(future_position, stepper):
actual_steps = self.calculate_steps_for_absolute_move(future_position, stepper)
confirmation = self.send_stepper_command(actual_steps, stepper)
while stepper_position == stepper.position:
QApplication.processEvents()
# Check for timeout
if time.time() - start_time > timeout_duration:
logger.error("Absolute move timed out")
break # or handle timeout differently
return confirmation
### Position Saving and Loading ###
def load_positions(self, path : str) -> None:
"""Load the saved positions from a json file.
Args:
path (str): The path to the json file.
"""
# First clear the old positions
self.module.model.saved_positions = []
with open(path, "r") as f:
positions = json.load(f)
for position in positions:
logger.debug("Loading position: %s", position)
self.add_position(position["frequency"], position["tuning_position"], position["matching_position"])
def save_positions(self, path: str) -> None:
"""Save the current positions to a json file.
Args:
path (str): The path to the json file.
"""
positions = self.module.model.saved_positions
with open(path, "w") as f:
json_position = [position.to_json() for position in positions]
json.dump(json_position, f)
def add_position(self, frequency: str, tuning_position: str, matching_position: str) -> None:
"""Add a position to the lookup table.
Args:
frequency (str): The frequency of the position.
tuning_position (str): The tuning position.
matching_position (str): The matching position.
"""
logger.debug("Adding new position at %s MHz", frequency)
self.module.model.add_saved_position(frequency, tuning_position, matching_position)
def on_go_to_position(self, position: SavedPosition) -> None:
"""Go to the specified position.
Args:
position (SavedPosition): The position to go to.
"""
logger.debug("Going to position: %s", position)
confirmation = self.on_absolute_move(position.tuning_position, self.module.model.tuning_stepper)
if confirmation:
self.on_absolute_move(position.matching_position, self.module.model.matching_stepper)
def on_delete_position(self, position: SavedPosition) -> None:
"""Delete the specified position.
Args:
position (SavedPosition): The position to delete.
"""
logger.debug("Deleting position: %s", position)
self.module.model.delete_saved_position(position)
#### Mechanical tuning and matching ####
def generate_mechanical_lut(self, start_frequency: str, stop_frequency: str, frequency_step: str) -> None:
"""Generate a lookup table for the specified frequency range and voltage resolution.
Args:
start_frequency (str): The start frequency in Hz.
stop_frequency (str): The stop frequency in Hz.
frequency_step (str): The frequency step in Hz.
"""
try:
start_frequency = start_frequency.replace(",", ".")
stop_frequency = stop_frequency.replace(",", ".")
frequency_step = frequency_step.replace(",", ".")
start_frequency = float(start_frequency)
stop_frequency = float(stop_frequency)
frequency_step = float(frequency_step)
except ValueError:
error = "Could not generate LUT. Start frequency, stop frequency, frequency step must be floats"
logger.error(error)
self.module.view.add_info_text(error)
return
if (
start_frequency < 0
or stop_frequency < 0
or frequency_step < 0
):
error = "Could not generate LUT. Start frequency, stop frequency, frequency step must be positive"
logger.error(error)
self.module.view.add_info_text(error)
return
if start_frequency > stop_frequency:
error = "Could not generate LUT. Start frequency must be smaller than stop frequency"
logger.error(error)
self.module.view.add_info_text(error)
return
# - 0.1 is to prevent float errors
if frequency_step - 0.1 > (stop_frequency - start_frequency):
error = "Could not generate LUT. Frequency step must be smaller than the frequency range"
logger.error(error)
self.module.view.add_info_text(error)
return
logger.debug(
"Generating LUT from %s MHz to %s MHz with a frequency step of %s MHz",
start_frequency,
stop_frequency,
frequency_step,
)
self.switch_to_atm()
# We create the lookup table
LUT = MechanicalLookupTable(
start_frequency, stop_frequency, frequency_step
)
# Lock GUI
self.module.view.create_mech_LUT_spinner_dialog()
self.module.model.mech_lut = LUT
self.start_next_mechTM(LUT)
def start_next_mechTM(self, LUT):
"""Start the next mechanical tuning and matching sweep."""
next_frequency = LUT.get_next_frequency()
LUT.started_frequency = next_frequency
logger.debug("Starting next mechanical tuning and matching:")
# Now we vary the tuning capacitor position and matching capacitor position
# Step size tuner:
TUNER_STEP_SIZE = 10
# Step size matcher:
MATCHER_STEP_SIZE = 50
TUNING_RANGE = 40
MATCHING_RANGE = 500
tuning_backlash = self.module.model.tuning_stepper.BACKLASH_STEPS
# I'm not sure about this value ...
matching_backlash = 0
# Command for the position sweep: p<frequency in MHz>t<range>,<step size>,<backlash>,<last_direction>m<range>,<step size>,<backlash>,<last_direction>"
tuning_last_direction = self.module.model.tuning_stepper.last_direction
matching_last_direction = self.module.model.matching_stepper.last_direction
command = f"p{next_frequency}t{TUNING_RANGE},{TUNER_STEP_SIZE},{tuning_backlash},{tuning_last_direction}m{MATCHING_RANGE},{MATCHER_STEP_SIZE},{matching_backlash},{matching_last_direction}"
confirmation = self.send_command(command)
@pyqtSlot(str)
def process_position_sweep_result(self, text):
if text.startswith("z"):
text = text[1:]
# Format is z<tuning_position>,<tuning_last_direction>m<matching_position>,<matching_last_direction>
text = text.split("m")
tuning_position, tuning_last_direction = map(int, text[0].split(","))
matching_position, matching_last_direction = map(int, text[1].split(","))
# Keep backlash compensation consistent
self.module.model.tuning_stepper.last_direction = tuning_last_direction
self.module.model.matching_stepper.last_direction = matching_last_direction
# Update the positions
self.module.model.tuning_stepper.position = tuning_position
self.module.model.matching_stepper.position = matching_position
self.module.view.on_active_stepper_changed()
logger.debug("Tuning position: %s, Matching position: %s", tuning_position, matching_position)
LUT = self.module.model.mech_lut
logger.debug("Received position sweep result: %s %s", matching_position, tuning_position)
LUT.add_positions(tuning_position, matching_position)
self.continue_or_finish_position_sweep(LUT)
def continue_or_finish_position_sweep(self, LUT):
"""Continue or finish the position sweep."""
if LUT.is_incomplete():
self.start_next_mechTM(LUT)
else:
self.finish_position_sweep(LUT)
def finish_position_sweep(self, LUT):
"""Finish the position sweep."""
logger.debug("Finished position sweep")
self.module.model.mech_lut = LUT
self.module.model.LUT = LUT
self.module.view.mech_LUT_spinner.hide()
self.module.nqrduck_signal.emit("LUT_finished", LUT)
def go_to_position(self, tuning_position : int, matching_position : int) -> None:
"""Go to the specified position.
Args:
position (SavedPosition): The position to go to.
"""
confirmation = self.on_absolute_move(tuning_position, self.module.model.tuning_stepper)
if confirmation:
confirmation = self.on_absolute_move(matching_position, self.module.model.matching_stepper)
if confirmation:
return True
# This method isn't used anymore but it might be useful in the future so I'll keep it here
def read_reflection(self, frequency) -> float:
"""Starts a reflection measurement and reads the reflection at the specified frequency."""
# We send the command to the atm system
command = f"r{frequency}"
try:
confirmation = self.send_command(command)
QApplication.processEvents()
if confirmation:
reflection = self.module.model.last_reflection
# Set the timeout duration (e.g., 5 seconds)
timeout_duration = 5
# Record the start time
start_time = time.time()
# Wait for reflection data until the timeout is reached
while reflection is None:
# Check if the timeout has been reached
if time.time() - start_time > timeout_duration:
logger.error("Reading reflection timed out after %d seconds", timeout_duration)
self.module.view.add_error_text(f"Could not read reflection. Timed out after {timeout_duration} seconds")
return None
# Refresh the reflection data
reflection = self.module.model.last_reflection
QApplication.processEvents()
# Reset the reflection cache
self.module.model.last_reflection = None
magnitude = reflection[0]
CENTER_POINT_MAGNITUDE = 900 # mV
MAGNITUDE_SLOPE = 30 # dB/mV
magnitude = (magnitude - CENTER_POINT_MAGNITUDE) / MAGNITUDE_SLOPE
return -magnitude
else:
logger.error("Could not read reflection. No confirmation received")
self.module.view.add_error_text("Could not read reflection. No confirmation received")
return None
except Exception as e:
logger.error("Could not read reflection. %s", e)
self.module.view.add_error_text(f"Could not read reflection. {e}")
return None

View file

@ -10,6 +10,7 @@ logger = logging.getLogger(__name__)
class S11Data:
FILE_EXTENSION = "s11"
# Conversion factors - the data is generally sent and received in mV
# These values are used to convert the data to dB and degrees
CENTER_POINT_MAGNITUDE = 900 # mV
@ -139,6 +140,9 @@ class S11Data:
* phase_sign[i]
)
# Murks: The last point is always wrong so just set it to the previous value
phase_data_corrected[-1] = phase_data_corrected[-2]
return phase_data_corrected
def to_json(self):
@ -167,26 +171,102 @@ class LookupTable:
start_frequency: float,
stop_frequency: float,
frequency_step: float,
voltage_resolution: float,
) -> None:
self.start_frequency = start_frequency
self.stop_frequency = stop_frequency
self.frequency_step = frequency_step
self.voltage_resolution = voltage_resolution
# This is the frequency at which the tuning and matching process was started
self.started_frequency = None
def get_entry_number(self, frequency: float) -> int:
"""This method returns the entry number of the given frequency.
Args:
frequency (float): The frequency for which the entry number should be returned.
Returns:
int: The entry number of the given frequency.
"""
# Round to closest integer
return int(round((frequency - self.start_frequency) / self.frequency_step))
class Stepper:
def __init__(self) -> None:
self.homed = False
self.position = 0
class SavedPosition():
"""This class is used to store a saved position for tuning and matching of electrical probeheads."""
def __init__(self, frequency: float, tuning_position : int, matching_position : int) -> None:
self.frequency = frequency
self.tuning_position = tuning_position
self.matching_position = matching_position
def to_json(self):
return {
"frequency": self.frequency,
"tuning_position": self.tuning_position,
"matching_position": self.matching_position,
}
class TuningStepper(Stepper):
TYPE = "Tuning"
MAX_STEPS = 1e6
BACKLASH_STEPS = 60
def __init__(self) -> None:
super().__init__()
# Backlash stepper
self.last_direction = None
class MatchingStepper(Stepper):
TYPE = "Matching"
MAX_STEPS = 1e6
BACKLASH_STEPS = 0
def __init__(self) -> None:
super().__init__()
self.last_direction = None
class ElectricalLookupTable(LookupTable):
TYPE = "Electrical"
def __init__(self, start_frequency: float, stop_frequency: float, frequency_step: float) -> None:
super().__init__(start_frequency, stop_frequency, frequency_step)
self.init_voltages()
def init_voltages(self) -> None:
"""Initialize the lookup table with default values."""
for frequency in np.arange(
self.start_frequency, self.stop_frequency, self.frequency_step
self.start_frequency, self.stop_frequency + self.frequency_step, self.frequency_step
):
self.started_frequency = frequency
self.add_voltages(None, None)
def add_voltages(self, tuning_voltage: float, matching_voltage: float) -> None:
"""Add a tuning and matching voltage for the last started frequency to the lookup table.
Args:
tuning_voltage (float): The tuning voltage for the given frequency.
matching_voltage (float): The matching voltage for the given frequency."""
self.data[self.started_frequency] = (tuning_voltage, matching_voltage)
def get_voltages(self, frequency: float) -> tuple:
"""Get the tuning and matching voltage for the given frequency.
Args:
frequency (float): The frequency for which the tuning and matching voltage should be returned.
Returns:
tuple: The tuning and matching voltage for the given frequency.
"""
entry_number = self.get_entry_number(frequency)
key = list(self.data.keys())[entry_number]
return self.data[key]
def is_incomplete(self) -> bool:
"""This method returns True if the lookup table is incomplete,
i.e. if there are frequencies for which no the tuning or matching voltage is none.
@ -214,19 +294,78 @@ class LookupTable:
return None
def add_voltages(self, tuning_voltage: float, matching_voltage: float) -> None:
"""Add a tuning and matching voltage for the last started frequency to the lookup table.
class MechanicalLookupTable(LookupTable):
# Hmm duplicate code
TYPE = "Mechanical"
def __init__(self, start_frequency: float, stop_frequency: float, frequency_step: float) -> None:
super().__init__(start_frequency, stop_frequency, frequency_step)
self.init_positions()
def init_positions(self) -> None:
"""Initialize the lookup table with default values."""
for frequency in np.arange(
self.start_frequency, self.stop_frequency + self.frequency_step, self.frequency_step
):
self.started_frequency = frequency
self.add_positions(None, None)
def add_positions(self, tuning_position: int, matching_position: int) -> None:
"""Add a tuning and matching position for the last started frequency to the lookup table.
Args:
tuning_voltage (float): The tuning voltage for the given frequency.
matching_voltage (float): The matching voltage for the given frequency."""
self.data[self.started_frequency] = (tuning_voltage, matching_voltage)
tuning_position (int): The tuning position for the given frequency.
matching_position (int): The matching position for the given frequency."""
self.data[self.started_frequency] = (tuning_position, matching_position)
def get_positions(self, frequency: float) -> tuple:
"""Get the tuning and matching position for the given frequency.
Args:
frequency (float): The frequency for which the tuning and matching position should be returned.
Returns:
tuple: The tuning and matching position for the given frequency.
"""
entry_number = self.get_entry_number(frequency)
key = list(self.data.keys())[entry_number]
return self.data[key]
def is_incomplete(self) -> bool:
"""This method returns True if the lookup table is incomplete,
i.e. if there are frequencies for which no the tuning or matching position is none.
Returns:
bool: True if the lookup table is incomplete, False otherwise.
"""
return any(
[
tuning_position is None or matching_position is None
for tuning_position, matching_position in self.data.values()
]
)
def get_next_frequency(self) -> float:
"""This method returns the next frequency for which the tuning and matching position is not yet set.
Returns:
float: The next frequency for which the tuning and matching position is not yet set.
"""
for frequency, (tuning_position, matching_position) in self.data.items():
if tuning_position is None or matching_position is None:
return frequency
return None
class AutoTMModel(ModuleModel):
available_devices_changed = pyqtSignal(list)
serial_changed = pyqtSignal(QSerialPort)
data_points_changed = pyqtSignal(list)
active_stepper_changed = pyqtSignal(Stepper)
saved_positions_changed = pyqtSignal(list)
serial_data_received = pyqtSignal(str)
short_calibration_finished = pyqtSignal(S11Data)
open_calibration_finished = pyqtSignal(S11Data)
@ -240,6 +379,24 @@ class AutoTMModel(ModuleModel):
self.calibration = None
self.serial = None
self.tuning_stepper = TuningStepper()
self.matching_stepper = MatchingStepper()
self.active_stepper = self.tuning_stepper
self.saved_positions = []
self.el_lut = None
self.mech_lut = None
self.LUT = None
self.last_reflection = None
self.tuning_voltage = None
self.matching_voltage = None
# AutoTM system or preamp
self.signal_path = None
@property
def available_devices(self):
return self._available_devices
@ -273,6 +430,25 @@ class AutoTMModel(ModuleModel):
self.data_points.clear()
self.data_points_changed.emit(self.data_points)
@property
def saved_positions(self):
return self._saved_positions
@saved_positions.setter
def saved_positions(self, value):
self._saved_positions = value
self.saved_positions_changed.emit(value)
def add_saved_position(self, frequency: float, tuning_position: int, matching_position: int) -> None:
"""Add a saved position to the model."""
self.saved_positions.append(SavedPosition(frequency, tuning_position, matching_position))
self.saved_positions_changed.emit(self.saved_positions)
def delete_saved_position(self, position: SavedPosition) -> None:
"""Delete a saved position from the model."""
self.saved_positions.remove(position)
self.saved_positions_changed.emit(self.saved_positions)
@property
def measurement(self):
"""The measurement property is used to store the current measurement.
@ -285,6 +461,15 @@ class AutoTMModel(ModuleModel):
self._measurement = value
self.measurement_finished.emit(value)
@property
def active_stepper(self):
return self._active_stepper
@active_stepper.setter
def active_stepper(self, value):
self._active_stepper = value
self.active_stepper_changed.emit(value)
# Calibration properties
@property

View file

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>1280</width>
<height>862</height>
<height>1089</height>
</rect>
</property>
<property name="sizePolicy">
@ -26,6 +26,7 @@
<widget class="QLabel" name="titleconnectionLabel">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
@ -76,10 +77,70 @@
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="tmsettingsLabel">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>T&amp;M Settings:</string>
</property>
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout_8">
<item row="1" column="1">
<widget class="QDoubleSpinBox" name="stopfrequencyBox">
<property name="value">
<double>80.099999999999994</double>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_13">
<property name="text">
<string>Stop Frequency (MHz)</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QDoubleSpinBox" name="startfrequencyBox">
<property name="value">
<double>80.000000000000000</double>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>Start Frequency (MHz)</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_14">
<property name="text">
<string>Frequency Step (MHz)</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="frequencystepBox">
<property name="value">
<double>0.100000000000000</double>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="titletypeLabel">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
@ -91,86 +152,17 @@
<item>
<widget class="QTabWidget" name="typeTab">
<property name="currentIndex">
<number>0</number>
<number>1</number>
</property>
<widget class="QWidget" name="mechTab">
<attribute name="title">
<string>Mechanical</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,1,0">
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,1,0,0,0">
<item>
<layout class="QGridLayout" name="gridLayout_4" rowstretch="0,0,0,0,0,0">
<layout class="QGridLayout" name="gridLayout_4" rowstretch="0,0,0,0,0,0,0">
<item row="5" column="1">
<widget class="QPushButton" name="homematcherButton">
<property name="text">
<string>Home</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_17">
<property name="text">
<string>Step Size:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="stepsizeBox">
<property name="minimum">
<number>-1000</number>
</property>
<property name="maximum">
<number>1000</number>
</property>
<property name="value">
<number>500</number>
</property>
</widget>
</item>
<item row="2" column="0" colspan="3">
<widget class="QLabel" name="label_18">
<property name="text">
<string>Tuning Stepper:</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QPushButton" name="decreasetunerButton">
<property name="text">
<string>-</string>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QPushButton" name="increasetunerButton">
<property name="text">
<string>+</string>
</property>
</widget>
</item>
<item row="4" column="0" colspan="3">
<widget class="QLabel" name="label_19">
<property name="text">
<string>Matching Stepper:</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QPushButton" name="decreasematcherButton">
<property name="text">
<string>-</string>
</property>
</widget>
</item>
<item row="5" column="2">
<widget class="QPushButton" name="increasematcherButton">
<property name="text">
<string>+</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QPushButton" name="hometunerButton">
<widget class="QPushButton" name="homeButton">
<property name="text">
<string>Home</string>
</property>
@ -178,17 +170,127 @@
</item>
<item row="0" column="0" colspan="3">
<widget class="QLabel" name="label_16">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Stepper Control:</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QSpinBox" name="absoluteposBox">
<property name="maximum">
<number>1000000</number>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="stepperselectBox">
<item>
<property name="text">
<string>Tuning</string>
</property>
</item>
<item>
<property name="text">
<string>Matching</string>
</property>
</item>
</widget>
</item>
<item row="5" column="0">
<widget class="QPushButton" name="decreaseButton">
<property name="text">
<string>-</string>
</property>
</widget>
</item>
<item row="5" column="2">
<widget class="QPushButton" name="increaseButton">
<property name="text">
<string>+</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_18">
<property name="text">
<string>Stepper:</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_20">
<property name="text">
<string>Absolute:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="stepsizeBox">
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>1000000</number>
</property>
<property name="value">
<number>500</number>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_17">
<property name="text">
<string>Step Size:</string>
</property>
</widget>
</item>
<item row="6" column="2">
<widget class="QPushButton" name="absoluteGoButton">
<property name="text">
<string>Go</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Position:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="stepperposLabel">
<property name="text">
<string>0</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="starpositionButton">
<widget class="QPushButton" name="positionButton">
<property name="text">
<string>Start Position</string>
<string>Saved Positions</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="mechLUTButton">
<property name="text">
<string>Generate LUT</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="viewmechLUTButton">
<property name="text">
<string>View LUT</string>
</property>
</widget>
</item>
@ -211,66 +313,12 @@
<attribute name="title">
<string>Electrical</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_3" rowstretch="0,0,0,0,0,0,0,0,0,0,0,0,0">
<item row="5" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Voltage Resolution</string>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QDoubleSpinBox" name="stopfrequencyBox"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Voltage Tuning</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QDoubleSpinBox" name="tuningBox"/>
</item>
<item row="8" column="1">
<widget class="QDoubleSpinBox" name="frequencystepBox"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_11">
<property name="font">
<font>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Generate LUT:</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QDoubleSpinBox" name="startfrequencyBox"/>
</item>
<item row="9" column="0" colspan="2">
<widget class="QPushButton" name="generateLUTButton">
<property name="text">
<string>Start Voltage Sweep</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label_13">
<property name="text">
<string>Stop Frequency (MHz)</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QDoubleSpinBox" name="resolutionBox"/>
</item>
<layout class="QGridLayout" name="gridLayout_3" rowstretch="0,0,0,0,0,0,0,0,0,0,0">
<item row="0" column="0">
<widget class="QLabel" name="label_9">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
@ -279,15 +327,41 @@
</property>
</widget>
</item>
<item row="11" column="0">
<widget class="QLabel" name="label_15">
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>RF Switch:</string>
<string>Voltage Tuning</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="matchingBox"/>
</item>
<item row="8" column="0" colspan="2">
<widget class="QPushButton" name="generateLUTButton">
<property name="text">
<string>Generate LUT</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QDoubleSpinBox" name="tuningBox"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_11">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Generate LUT:</string>
</property>
</widget>
</item>
<item row="10" column="0" colspan="2">
<widget class="QPushButton" name="viewLUTButton">
<widget class="QPushButton" name="viewelLUTButton">
<property name="text">
<string>View LUT</string>
</property>
@ -307,34 +381,10 @@
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_12">
<item row="9" column="0">
<widget class="QCheckBox" name="prevVoltagecheckBox">
<property name="text">
<string>Start Frequency (MHz)</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="matchingBox"/>
</item>
<item row="8" column="0">
<widget class="QLabel" name="label_14">
<property name="text">
<string>Frequency Step (MHz)</string>
</property>
</widget>
</item>
<item row="12" column="0">
<widget class="QPushButton" name="switchpreampButton">
<property name="text">
<string>Preamplifier</string>
</property>
</widget>
</item>
<item row="12" column="1">
<widget class="QPushButton" name="switchATMButton">
<property name="text">
<string>ATM</string>
<string>Start from previous Voltage</string>
</property>
</widget>
</item>
@ -342,10 +392,42 @@
</widget>
</widget>
</item>
<item>
<widget class="QLabel" name="rfswitchLabel">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>RF Switch:</string>
</property>
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout_7">
<item row="0" column="0">
<widget class="QPushButton" name="switchATMButton">
<property name="text">
<string>ATM</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QPushButton" name="switchpreampButton">
<property name="text">
<string>Preamplifier</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="titlefrequencyLabel">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
@ -363,17 +445,10 @@
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="label_8">
<item row="1" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>MHz</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="stopEdit">
<property name="text">
<string>100</string>
<string>Stop Frequency:</string>
</property>
</widget>
</item>
@ -384,13 +459,6 @@
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Stop Frequency:</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
@ -398,6 +466,20 @@
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="stopEdit">
<property name="text">
<string>100</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="label_8">
<property name="text">
<string>MHz</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
@ -425,6 +507,7 @@
<widget class="QLabel" name="titleinfoLabel">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
@ -443,8 +526,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>297</width>
<height>68</height>
<width>291</width>
<height>83</height>
</rect>
</property>
</widget>
@ -464,6 +547,41 @@
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,0">
<item>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QPushButton" name="importButton">
<property name="text">
<string>Import Measurement</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="exportButton">
<property name="text">
<string>Export Measurement</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
</layout>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

View file

@ -21,6 +21,7 @@ from nqrduck.contrib.mplwidget import MplWidget
from nqrduck.assets.icons import Logos
from nqrduck.assets.animations import DuckAnimations
from .widget import Ui_Form
from .model import S11Data
logger = logging.getLogger(__name__)
@ -34,11 +35,14 @@ class AutoTMView(ModuleView):
self._ui_form.setupUi(self)
self.widget = widget
self.frequency_sweep_spinner = self.FrequencySweepSpinner(self)
self.frequency_sweep_spinner = self.LoadingSpinner(self)
self.frequency_sweep_spinner.hide()
# Disable the connectButton while no devices are selected
self._ui_form.connectButton.setDisabled(True)
self._ui_form.decreaseButton.setEnabled(False)
self._ui_form.increaseButton.setEnabled(False)
self._ui_form.absoluteGoButton.setEnabled(False)
# On clicking of the refresh button scan for available usb devices
self._ui_form.refreshButton.clicked.connect(self.module.controller.find_devices)
@ -61,18 +65,28 @@ class AutoTMView(ModuleView):
)
)
# On clicking of the generateLUTButton call the generate_lut method
# On clicking of the generateLUTButton call the generate_mechanical_lut method
self._ui_form.generateLUTButton.clicked.connect(
lambda: self.module.controller.generate_lut(
lambda: self.module.controller.generate_electrical_lut(
self._ui_form.startfrequencyBox.text(),
self._ui_form.stopfrequencyBox.text(),
self._ui_form.frequencystepBox.text(),
)
)
# On clicking of the generateLUTButton call the generate_electrical_lut method
self._ui_form.mechLUTButton.clicked.connect(
lambda: self.module.controller.generate_mechanical_lut(
self._ui_form.startfrequencyBox.text(),
self._ui_form.stopfrequencyBox.text(),
self._ui_form.frequencystepBox.text(),
self._ui_form.resolutionBox.text(),
)
)
# On clicking of the viewLUTButton call the view_lut method
self._ui_form.viewLUTButton.clicked.connect(self.view_lut)
self._ui_form.viewelLUTButton.clicked.connect(self.view_el_lut)
self._ui_form.viewmechLUTButton.clicked.connect(self.view_mech_lut)
# On clicking of the setvoltagesButton call the set_voltages method
self._ui_form.setvoltagesButton.clicked.connect(
@ -97,7 +111,7 @@ class AutoTMView(ModuleView):
)
# On clicking of the homingButton call the homing method
self._ui_form.starpositionButton.clicked.connect(self.module.controller.homing)
self._ui_form.homeButton.clicked.connect(self.module.controller.homing)
# Connect the measurement finished signal to the plot_measurement slot
self.module.model.measurement_finished.connect(self.plot_measurement)
@ -112,11 +126,35 @@ class AutoTMView(ModuleView):
self._ui_form.startButton.setIcon(Logos.Play_16x16())
self._ui_form.startButton.setIconSize(self._ui_form.startButton.size())
# Stepper selection
self._ui_form.stepperselectBox.currentIndexChanged.connect(lambda: self.module.controller.on_stepper_changed(self._ui_form.stepperselectBox.currentText()))
self._ui_form.increaseButton.clicked.connect(lambda: self.module.controller.on_relative_move(self._ui_form.stepsizeBox.text()))
self._ui_form.decreaseButton.clicked.connect(lambda: self.module.controller.on_relative_move("-" + self._ui_form.stepsizeBox.text()))
self._ui_form.absoluteGoButton.clicked.connect(lambda: self.module.controller.on_absolute_move(self._ui_form.absoluteposBox.text()))
# Active stepper changed
self.module.model.active_stepper_changed.connect(self.on_active_stepper_changed)
# Position Button
self._ui_form.positionButton.clicked.connect(self.on_position_button_clicked)
# Import and export buttons
self._ui_form.exportButton.setIcon(Logos.Save16x16())
self._ui_form.exportButton.setIconSize(self._ui_form.exportButton.size())
self._ui_form.exportButton.clicked.connect(self.on_export_button_clicked)
self._ui_form.importButton.setIcon(Logos.Load16x16())
self._ui_form.importButton.setIconSize(self._ui_form.importButton.size())
self._ui_form.importButton.clicked.connect(self.on_import_button_clicked)
self.init_plot()
self.init_labels()
def init_labels(self) -> None:
"""Makes some of the labels bold for better readability."""
self._ui_form.tmsettingsLabel.setStyleSheet("font-weight: bold;")
self._ui_form.titleconnectionLabel.setStyleSheet("font-weight: bold;")
self._ui_form.titlefrequencyLabel.setStyleSheet("font-weight: bold;")
self._ui_form.titletypeLabel.setStyleSheet("font-weight: bold;")
@ -126,7 +164,7 @@ class AutoTMView(ModuleView):
"""Initialize the S11 plot."""
ax = self._ui_form.S11Plot.canvas.ax
ax.set_xlabel("Frequency (MHz)")
ax.set_ylabel("S11 (dB)")
ax.set_ylabel("S11 (dB)", loc="center")
ax.set_title("S11")
ax.grid(True)
ax.set_xlim(0, 100)
@ -156,6 +194,10 @@ class AutoTMView(ModuleView):
self._ui_form.connectButton.setEnabled(False)
logger.debug("Updated available devices list")
def on_stepper_changed():
"""Update the stepper position label according to the current stepper position."""
logger.debug("Updating stepper position label")
@pyqtSlot()
def on_connect_button_clicked(self) -> None:
"""This method is called when the connect button is clicked.
@ -184,6 +226,38 @@ class AutoTMView(ModuleView):
logger.debug("Updated serial connection label")
@pyqtSlot()
def on_active_stepper_changed(self) -> None:
"""Update the stepper position label according to the current stepper position."""
logger.debug("Updating stepper position label")
self._ui_form.stepperposLabel.setText(str(self.module.model.active_stepper.position))
logger.debug("Updated stepper position label")
# Only allow position change when stepper is homed
if self.module.model.active_stepper.homed:
self._ui_form.decreaseButton.setEnabled(True)
self._ui_form.increaseButton.setEnabled(True)
self._ui_form.absoluteGoButton.setEnabled(True)
self._ui_form.positionButton.setEnabled(True)
self._ui_form.mechLUTButton.setEnabled(True)
self._ui_form.viewmechLUTButton.setEnabled(True)
else:
self._ui_form.decreaseButton.setEnabled(False)
self._ui_form.increaseButton.setEnabled(False)
self._ui_form.absoluteGoButton.setEnabled(False)
self._ui_form.positionButton.setEnabled(False)
self._ui_form.mechLUTButton.setEnabled(False)
self._ui_form.viewmechLUTButton.setEnabled(False)
@pyqtSlot()
def on_position_button_clicked(self) -> None:
"""This method is called when the position button is clicked.
It opens the position window.
"""
logger.debug("Position button clicked")
self.position_window = self.StepperSavedPositionsWindow(self.module, self)
self.position_window.show()
def plot_measurement(self, data: "S11Data") -> None:
"""Update the S11 plot with the current data points.
@ -226,7 +300,9 @@ class AutoTMView(ModuleView):
else:
magnitude_ax.plot(frequency, return_loss_db, color="blue")
self.phase_ax.set_ylabel("|Phase (deg)|")
self.phase_ax.yaxis.tick_right()
self.phase_ax.yaxis.set_label_position("right")
self.phase_ax.set_ylabel("Phase (deg)")
self.phase_ax.plot(frequency, phase, color="orange", linestyle="--")
# self.phase_ax.invert_yaxis()
@ -288,21 +364,237 @@ class AutoTMView(ModuleView):
def create_frequency_sweep_spinner_dialog(self) -> None:
"""Creates a frequency sweep spinner dialog."""
self.frequency_sweep_spinner = self.FrequencySweepSpinner(self)
self.frequency_sweep_spinner = self.LoadingSpinner("Performing frequency sweep ...", self)
self.frequency_sweep_spinner.show()
def view_lut(self) -> None:
"""Creates a new Dialog that shows the currently active LUT."""
def create_el_LUT_spinner_dialog(self) -> None:
"""Creates a electrical LUT spinner dialog."""
self.el_LUT_spinner = self.LoadingSpinner("Generating electrical LUT ...", self)
self.el_LUT_spinner.show()
def create_mech_LUT_spinner_dialog(self) -> None:
"""Creates a mechanical LUT spinner dialog."""
self.mech_LUT_spinner = self.LoadingSpinner("Generating mechanical LUT ...", self)
self.mech_LUT_spinner.show()
def view_el_lut(self) -> None:
"""Creates a new Dialog that shows the currently active electrical LUT."""
logger.debug("View LUT")
if self.module.model.el_lut is None:
logger.debug("No LUT available")
self.add_error_text("No LUT available")
return
self.lut_window = self.LutWindow(self.module)
self.lut_window.show()
class FrequencySweepSpinner(QDialog):
def view_mech_lut(self) -> None:
"""Creates a new Dialog that shows the currently active mechanical LUT."""
logger.debug("View mechanical LUT")
if self.module.model.mech_lut is None:
logger.debug("No LUT available")
self.add_error_text("No LUT available")
return
self.lut_window = self.LutWindow(self.module)
self.lut_window.show()
@pyqtSlot()
def on_export_button_clicked(self) -> None:
"""Slot for when the export button is clicked."""
logger.debug("Export button clicked")
file_manager = self.QFileManager(S11Data.FILE_EXTENSION, parent=self.widget)
file_name = file_manager.saveFileDialog()
if file_name:
self.module.controller.save_measurement(file_name)
@pyqtSlot()
def on_import_button_clicked(self) -> None:
"""Slot for when the import button is clicked."""
logger.debug("Import button clicked")
file_manager = self.QFileManager(S11Data.FILE_EXTENSION, parent=self.widget)
file_name = file_manager.loadFileDialog()
if file_name:
self.module.controller.load_measurement(file_name)
class StepperSavedPositionsWindow(QDialog):
def __init__(self, module, parent=None):
super().__init__(parent)
self.setParent(parent)
self.module = module
self.setWindowTitle("Saved positions")
# make window larger
self.resize(800, 800)
# Add vertical main layout
main_layout = QVBoxLayout()
# Create table widget
self.table_widget = QTableWidget()
self.table_widget.setColumnCount(5)
self.table_widget.setHorizontalHeaderLabels(
["Frequency (MHz)", "Tuning Position", "Matching Position", "Button", "Delete"]
)
self.table_widget.setColumnWidth(0, 150)
self.table_widget.setColumnWidth(1, 200)
self.table_widget.setColumnWidth(2, 200)
self.table_widget.setColumnWidth(3, 100)
self.table_widget.setColumnWidth(4, 100)
self.on_saved_positions_changed()
# Add a 'Load Position' button (File selector)
load_position_button = QPushButton("Load Positions File")
load_position_button.clicked.connect(self.on_load_position_button_clicked)
main_layout.addWidget(load_position_button)
# Add a 'Save Position' button (File selector)
save_position_button = QPushButton("Save Positions File")
save_position_button.clicked.connect(self.on_save_position_button_clicked)
main_layout.addWidget(save_position_button)
# Add a 'New Position' button
new_position_button = QPushButton("New Position")
new_position_button.clicked.connect(self.on_new_position_button_clicked)
main_layout.addWidget(new_position_button)
# Add table widget to main layout
main_layout.addWidget(self.table_widget)
# On saved positions changed
self.module.model.saved_positions_changed.connect(self.on_saved_positions_changed)
self.setLayout(main_layout)
def file_selector(self, mode) -> str:
"""Opens a file selector and returns the selected file."""
filedialog = QFileDialog()
if mode == "load":
filedialog.setAcceptMode(QFileDialog.AcceptMode.AcceptOpen)
elif mode == "save":
filedialog.setAcceptMode(QFileDialog.AcceptMode.AcceptSave)
filedialog.setNameFilter("position files (*.pos)")
filedialog.setDefaultSuffix("pos")
filedialog.exec()
filename = filedialog.selectedFiles()[0]
return filename
def on_load_position_button_clicked(self) -> None:
"""File picker for loading a position from a file."""
filename = self.file_selector("load")
logger.debug("Loading position from %s" % filename)
self.module.controller.load_positions(filename)
def on_save_position_button_clicked(self) -> None:
"""File picker for saving a position to a file."""
filename = self.file_selector("save")
logger.debug("Saving position to %s" % filename)
self.module.controller.save_positions(filename)
def on_new_position_button_clicked(self) -> None:
"""Opens a new position dialog."""
logger.debug("New position button clicked")
self.new_position_window = self.NewPositionWindow(self.module, self)
self.new_position_window.show()
def on_saved_positions_changed(self) -> None:
"""This method is called when the saved positions changed.
It updates the table widget.
"""
logger.debug("Updating saved positions table")
self.table_widget.clearContents()
self.table_widget.setRowCount(0)
for row, position in enumerate(self.module.model.saved_positions):
self.table_widget.insertRow(row)
self.table_widget.setItem(row, 0, QTableWidgetItem(str(position.frequency)))
self.table_widget.setItem(
row, 1, QTableWidgetItem(position.tuning_position)
)
self.table_widget.setItem(
row, 2, QTableWidgetItem(position.matching_position)
)
go_button = QPushButton("Go")
go_button.clicked.connect(
lambda _, position=position: self.module.controller.on_go_to_position(
position
)
)
self.table_widget.setCellWidget(row, 3, go_button)
delete_button = QPushButton("Delete")
delete_button.clicked.connect(
lambda _, position=position: self.module.controller.on_delete_position(
position
)
)
self.table_widget.setCellWidget(row, 4, delete_button)
logger.debug("Updated saved positions table")
class NewPositionWindow(QDialog):
def __init__(self, module, parent=None):
super().__init__(parent)
self.setParent(parent)
self.module = module
self.setWindowTitle("New Position")
# Add vertical main layout
main_layout = QVBoxLayout()
# Add horizontal layout for the frequency range
frequency_layout = QHBoxLayout()
main_layout.addLayout(frequency_layout)
frequency_label = QLabel("Frequency")
frequency_layout.addWidget(frequency_label)
frequency_edit = QLineEdit()
frequency_layout.addWidget(frequency_edit)
unit_label = QLabel("MHz")
frequency_layout.addWidget(unit_label)
frequency_layout.addStretch()
# Add horizontal layout for the calibration type
type_layout = QHBoxLayout()
main_layout.addLayout(type_layout)
# Add vertical layout for short calibration
tuning_layout = QVBoxLayout()
tuning_label = QLabel("Tuning Position")
tuning_layout.addWidget(tuning_label)
tuning_edit = QLineEdit()
tuning_layout.addWidget(tuning_edit)
type_layout.addLayout(tuning_layout)
# Add vertical layout for open calibration
matching_layout = QVBoxLayout()
matching_label = QLabel("Matching Position")
matching_layout.addWidget(matching_label)
matching_edit = QLineEdit()
matching_layout.addWidget(matching_edit)
type_layout.addLayout(matching_layout)
# Add vertical layout for save calibration
data_layout = QVBoxLayout()
# Apply button
apply_button = QPushButton("Apply")
apply_button.clicked.connect(lambda: self.on_apply_button_clicked(frequency_edit.text(), tuning_edit.text(), matching_edit.text()))
data_layout.addWidget(apply_button)
main_layout.addLayout(data_layout)
self.setLayout(main_layout)
def on_apply_button_clicked(self, frequency: str, tuning_position: str, matching_position: str) -> None:
"""This method is called when the apply button is clicked."""
self.module.controller.add_position(frequency, tuning_position, matching_position)
# Close the calibration window
self.close()
class LoadingSpinner(QDialog):
"""This class implements a spinner dialog that is shown during a frequency sweep."""
def __init__(self, parent=None):
def __init__(self, text : str, parent=None):
super().__init__(parent)
self.setWindowTitle("Frequency sweep")
self.setWindowTitle("Loading")
self.setModal(True)
self.setWindowFlag(Qt.WindowType.FramelessWindowHint)
self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
@ -312,7 +604,7 @@ class AutoTMView(ModuleView):
self.spinner_label.setMovie(self.spinner_movie)
self.layout = QVBoxLayout(self)
self.layout.addWidget(QLabel("Performing frequency sweep..."))
self.layout.addWidget(QLabel(text))
self.layout.addWidget(self.spinner_label)
self.spinner_movie.start()
@ -324,16 +616,31 @@ class AutoTMView(ModuleView):
self.setParent(parent)
self.setWindowTitle("LUT")
# Set size
self.resize(800, 800)
# Add vertical main layout
main_layout = QVBoxLayout()
LUT = self.module.model.LUT
# Create table widget
self.table_widget = QTableWidget()
self.table_widget.setColumnCount(3)
self.table_widget.setHorizontalHeaderLabels(
["Frequency (MHz)", "Matching Voltage", "Tuning Voltage"]
)
LUT = self.module.model.LUT
self.table_widget.setColumnCount(4)
self.table_widget.setColumnWidth(0, 150)
self.table_widget.setColumnWidth(1, 200)
self.table_widget.setColumnWidth(2, 200)
self.table_widget.setColumnWidth(3, 100)
if LUT.TYPE == "Mechanical":
self.table_widget.setHorizontalHeaderLabels(
["Frequency (MHz)", "Tuning Position", "Matching Position"]
)
elif LUT.TYPE == "Electrical":
self.table_widget.setHorizontalHeaderLabels(
["Frequency (MHz)", "Tuning Voltage", "Matching Voltage"]
)
for row, frequency in enumerate(LUT.data.keys()):
self.table_widget.insertRow(row)
self.table_widget.setItem(row, 0, QTableWidgetItem(str(frequency)))
@ -344,20 +651,38 @@ class AutoTMView(ModuleView):
row, 2, QTableWidgetItem(str(LUT.data[frequency][1]))
)
# Button to test the specific entry in the LUT
test_button = QPushButton("Test")
# For electrical probe coils the matching voltage is the first entry in the LUT
if LUT.TYPE == "Electrical":
tuning_voltage = str(LUT.data[frequency][0])
matching_voltage = str(LUT.data[frequency][1])
test_button.clicked.connect(
lambda _, tuning_voltage=tuning_voltage, matching_voltage=matching_voltage: self.module.controller.set_voltages(
tuning_voltage, matching_voltage
)
)
# For mechanical probe coils the tuning voltage is the first entry in the LUT
elif LUT.TYPE == "Mechanical":
tuning_position = str(LUT.data[frequency][0])
matching_position = str(LUT.data[frequency][1])
test_button.clicked.connect(
lambda _, tuning_position=tuning_position, matching_position=matching_position: self.module.controller.go_to_position(
tuning_position, matching_position
)
)
self.table_widget.setCellWidget(row, 3, test_button)
# Add table widget to main layout
main_layout.addWidget(self.table_widget)
# Add Test LUT button
test_lut_button = QPushButton("Test LUT")
test_lut_button.clicked.connect(self.test_lut)
main_layout.addWidget(test_lut_button)
self.setLayout(main_layout)
def test_lut(self):
"""This method is called when the Test LUT button is clicked. It sets all of the voltages from the lut with a small delay.
One can then view the matching on a seperate VNA.
"""
# This should be in the controller
for frequency in self.module.model.LUT.data.keys():
tuning_voltage = str(self.module.model.LUT.data[frequency][1])
matching_voltage = str(self.module.model.LUT.data[frequency][0])

View file

@ -1,6 +1,6 @@
# Form implementation generated from reading ui file '../Modules/nqrduck-autotm/src/nqrduck_autotm/resources/autotm_widget.ui'
# Form implementation generated from reading ui file 'Modules/nqrduck-autotm/src/nqrduck_autotm/resources/autotm_widget.ui'
#
# Created by: PyQt6 UI code generator 6.4.2
# Created by: PyQt6 UI code generator 6.5.1
#
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
# run again. Do not edit this file unless you know what you are doing.
@ -12,7 +12,7 @@ from PyQt6 import QtCore, QtGui, QtWidgets
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(1280, 862)
Form.resize(1280, 1089)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
@ -25,6 +25,7 @@ class Ui_Form(object):
self.titleconnectionLabel = QtWidgets.QLabel(parent=Form)
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
self.titleconnectionLabel.setFont(font)
self.titleconnectionLabel.setObjectName("titleconnectionLabel")
self.verticalLayout_2.addWidget(self.titleconnectionLabel)
@ -50,9 +51,41 @@ class Ui_Form(object):
self.connectButton = QtWidgets.QPushButton(parent=Form)
self.connectButton.setObjectName("connectButton")
self.verticalLayout_2.addWidget(self.connectButton)
self.tmsettingsLabel = QtWidgets.QLabel(parent=Form)
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
self.tmsettingsLabel.setFont(font)
self.tmsettingsLabel.setObjectName("tmsettingsLabel")
self.verticalLayout_2.addWidget(self.tmsettingsLabel)
self.gridLayout_8 = QtWidgets.QGridLayout()
self.gridLayout_8.setObjectName("gridLayout_8")
self.stopfrequencyBox = QtWidgets.QDoubleSpinBox(parent=Form)
self.stopfrequencyBox.setProperty("value", 80.1)
self.stopfrequencyBox.setObjectName("stopfrequencyBox")
self.gridLayout_8.addWidget(self.stopfrequencyBox, 1, 1, 1, 1)
self.label_13 = QtWidgets.QLabel(parent=Form)
self.label_13.setObjectName("label_13")
self.gridLayout_8.addWidget(self.label_13, 1, 0, 1, 1)
self.startfrequencyBox = QtWidgets.QDoubleSpinBox(parent=Form)
self.startfrequencyBox.setProperty("value", 80.0)
self.startfrequencyBox.setObjectName("startfrequencyBox")
self.gridLayout_8.addWidget(self.startfrequencyBox, 0, 1, 1, 1)
self.label_12 = QtWidgets.QLabel(parent=Form)
self.label_12.setObjectName("label_12")
self.gridLayout_8.addWidget(self.label_12, 0, 0, 1, 1)
self.label_14 = QtWidgets.QLabel(parent=Form)
self.label_14.setObjectName("label_14")
self.gridLayout_8.addWidget(self.label_14, 2, 0, 1, 1)
self.frequencystepBox = QtWidgets.QDoubleSpinBox(parent=Form)
self.frequencystepBox.setProperty("value", 0.1)
self.frequencystepBox.setObjectName("frequencystepBox")
self.gridLayout_8.addWidget(self.frequencystepBox, 2, 1, 1, 1)
self.verticalLayout_2.addLayout(self.gridLayout_8)
self.titletypeLabel = QtWidgets.QLabel(parent=Form)
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
self.titletypeLabel.setFont(font)
self.titletypeLabel.setObjectName("titletypeLabel")
self.verticalLayout_2.addWidget(self.titletypeLabel)
@ -64,46 +97,65 @@ class Ui_Form(object):
self.verticalLayout.setObjectName("verticalLayout")
self.gridLayout_4 = QtWidgets.QGridLayout()
self.gridLayout_4.setObjectName("gridLayout_4")
self.homematcherButton = QtWidgets.QPushButton(parent=self.mechTab)
self.homematcherButton.setObjectName("homematcherButton")
self.gridLayout_4.addWidget(self.homematcherButton, 5, 1, 1, 1)
self.label_17 = QtWidgets.QLabel(parent=self.mechTab)
self.label_17.setObjectName("label_17")
self.gridLayout_4.addWidget(self.label_17, 1, 0, 1, 1)
self.stepsizeBox = QtWidgets.QSpinBox(parent=self.mechTab)
self.stepsizeBox.setMinimum(-1000)
self.stepsizeBox.setMaximum(1000)
self.stepsizeBox.setProperty("value", 500)
self.stepsizeBox.setObjectName("stepsizeBox")
self.gridLayout_4.addWidget(self.stepsizeBox, 1, 1, 1, 1)
self.label_18 = QtWidgets.QLabel(parent=self.mechTab)
self.label_18.setObjectName("label_18")
self.gridLayout_4.addWidget(self.label_18, 2, 0, 1, 3)
self.decreasetunerButton = QtWidgets.QPushButton(parent=self.mechTab)
self.decreasetunerButton.setObjectName("decreasetunerButton")
self.gridLayout_4.addWidget(self.decreasetunerButton, 3, 0, 1, 1)
self.increasetunerButton = QtWidgets.QPushButton(parent=self.mechTab)
self.increasetunerButton.setObjectName("increasetunerButton")
self.gridLayout_4.addWidget(self.increasetunerButton, 3, 2, 1, 1)
self.label_19 = QtWidgets.QLabel(parent=self.mechTab)
self.label_19.setObjectName("label_19")
self.gridLayout_4.addWidget(self.label_19, 4, 0, 1, 3)
self.decreasematcherButton = QtWidgets.QPushButton(parent=self.mechTab)
self.decreasematcherButton.setObjectName("decreasematcherButton")
self.gridLayout_4.addWidget(self.decreasematcherButton, 5, 0, 1, 1)
self.increasematcherButton = QtWidgets.QPushButton(parent=self.mechTab)
self.increasematcherButton.setObjectName("increasematcherButton")
self.gridLayout_4.addWidget(self.increasematcherButton, 5, 2, 1, 1)
self.hometunerButton = QtWidgets.QPushButton(parent=self.mechTab)
self.hometunerButton.setObjectName("hometunerButton")
self.gridLayout_4.addWidget(self.hometunerButton, 3, 1, 1, 1)
self.homeButton = QtWidgets.QPushButton(parent=self.mechTab)
self.homeButton.setObjectName("homeButton")
self.gridLayout_4.addWidget(self.homeButton, 5, 1, 1, 1)
self.label_16 = QtWidgets.QLabel(parent=self.mechTab)
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
self.label_16.setFont(font)
self.label_16.setObjectName("label_16")
self.gridLayout_4.addWidget(self.label_16, 0, 0, 1, 3)
self.absoluteposBox = QtWidgets.QSpinBox(parent=self.mechTab)
self.absoluteposBox.setMaximum(1000000)
self.absoluteposBox.setObjectName("absoluteposBox")
self.gridLayout_4.addWidget(self.absoluteposBox, 6, 1, 1, 1)
self.stepperselectBox = QtWidgets.QComboBox(parent=self.mechTab)
self.stepperselectBox.setObjectName("stepperselectBox")
self.stepperselectBox.addItem("")
self.stepperselectBox.addItem("")
self.gridLayout_4.addWidget(self.stepperselectBox, 1, 1, 1, 1)
self.decreaseButton = QtWidgets.QPushButton(parent=self.mechTab)
self.decreaseButton.setObjectName("decreaseButton")
self.gridLayout_4.addWidget(self.decreaseButton, 5, 0, 1, 1)
self.increaseButton = QtWidgets.QPushButton(parent=self.mechTab)
self.increaseButton.setObjectName("increaseButton")
self.gridLayout_4.addWidget(self.increaseButton, 5, 2, 1, 1)
self.label_18 = QtWidgets.QLabel(parent=self.mechTab)
self.label_18.setObjectName("label_18")
self.gridLayout_4.addWidget(self.label_18, 1, 0, 1, 1)
self.label_20 = QtWidgets.QLabel(parent=self.mechTab)
self.label_20.setObjectName("label_20")
self.gridLayout_4.addWidget(self.label_20, 6, 0, 1, 1)
self.stepsizeBox = QtWidgets.QSpinBox(parent=self.mechTab)
self.stepsizeBox.setMinimum(0)
self.stepsizeBox.setMaximum(1000000)
self.stepsizeBox.setProperty("value", 500)
self.stepsizeBox.setObjectName("stepsizeBox")
self.gridLayout_4.addWidget(self.stepsizeBox, 3, 1, 1, 1)
self.label_17 = QtWidgets.QLabel(parent=self.mechTab)
self.label_17.setObjectName("label_17")
self.gridLayout_4.addWidget(self.label_17, 3, 0, 1, 1)
self.absoluteGoButton = QtWidgets.QPushButton(parent=self.mechTab)
self.absoluteGoButton.setObjectName("absoluteGoButton")
self.gridLayout_4.addWidget(self.absoluteGoButton, 6, 2, 1, 1)
self.label_4 = QtWidgets.QLabel(parent=self.mechTab)
self.label_4.setObjectName("label_4")
self.gridLayout_4.addWidget(self.label_4, 2, 0, 1, 1)
self.stepperposLabel = QtWidgets.QLabel(parent=self.mechTab)
self.stepperposLabel.setObjectName("stepperposLabel")
self.gridLayout_4.addWidget(self.stepperposLabel, 2, 1, 1, 1)
self.verticalLayout.addLayout(self.gridLayout_4)
self.starpositionButton = QtWidgets.QPushButton(parent=self.mechTab)
self.starpositionButton.setObjectName("starpositionButton")
self.verticalLayout.addWidget(self.starpositionButton)
self.positionButton = QtWidgets.QPushButton(parent=self.mechTab)
self.positionButton.setObjectName("positionButton")
self.verticalLayout.addWidget(self.positionButton)
self.mechLUTButton = QtWidgets.QPushButton(parent=self.mechTab)
self.mechLUTButton.setObjectName("mechLUTButton")
self.verticalLayout.addWidget(self.mechLUTButton)
self.viewmechLUTButton = QtWidgets.QPushButton(parent=self.mechTab)
self.viewmechLUTButton.setObjectName("viewmechLUTButton")
self.verticalLayout.addWidget(self.viewmechLUTButton)
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding)
self.verticalLayout.addItem(spacerItem)
self.verticalLayout.setStretch(1, 1)
@ -112,77 +164,66 @@ class Ui_Form(object):
self.elecTab.setObjectName("elecTab")
self.gridLayout_3 = QtWidgets.QGridLayout(self.elecTab)
self.gridLayout_3.setObjectName("gridLayout_3")
self.label_4 = QtWidgets.QLabel(parent=self.elecTab)
self.label_4.setObjectName("label_4")
self.gridLayout_3.addWidget(self.label_4, 5, 0, 1, 1)
self.stopfrequencyBox = QtWidgets.QDoubleSpinBox(parent=self.elecTab)
self.stopfrequencyBox.setObjectName("stopfrequencyBox")
self.gridLayout_3.addWidget(self.stopfrequencyBox, 7, 1, 1, 1)
self.label_2 = QtWidgets.QLabel(parent=self.elecTab)
self.label_2.setObjectName("label_2")
self.gridLayout_3.addWidget(self.label_2, 1, 0, 1, 1)
self.tuningBox = QtWidgets.QDoubleSpinBox(parent=self.elecTab)
self.tuningBox.setObjectName("tuningBox")
self.gridLayout_3.addWidget(self.tuningBox, 1, 1, 1, 1)
self.frequencystepBox = QtWidgets.QDoubleSpinBox(parent=self.elecTab)
self.frequencystepBox.setObjectName("frequencystepBox")
self.gridLayout_3.addWidget(self.frequencystepBox, 8, 1, 1, 1)
self.label_11 = QtWidgets.QLabel(parent=self.elecTab)
font = QtGui.QFont()
font.setBold(True)
self.label_11.setFont(font)
self.label_11.setObjectName("label_11")
self.gridLayout_3.addWidget(self.label_11, 4, 0, 1, 1)
self.startfrequencyBox = QtWidgets.QDoubleSpinBox(parent=self.elecTab)
self.startfrequencyBox.setObjectName("startfrequencyBox")
self.gridLayout_3.addWidget(self.startfrequencyBox, 6, 1, 1, 1)
self.generateLUTButton = QtWidgets.QPushButton(parent=self.elecTab)
self.generateLUTButton.setObjectName("generateLUTButton")
self.gridLayout_3.addWidget(self.generateLUTButton, 9, 0, 1, 2)
self.label_13 = QtWidgets.QLabel(parent=self.elecTab)
self.label_13.setObjectName("label_13")
self.gridLayout_3.addWidget(self.label_13, 7, 0, 1, 1)
self.resolutionBox = QtWidgets.QDoubleSpinBox(parent=self.elecTab)
self.resolutionBox.setObjectName("resolutionBox")
self.gridLayout_3.addWidget(self.resolutionBox, 5, 1, 1, 1)
self.label_9 = QtWidgets.QLabel(parent=self.elecTab)
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
self.label_9.setFont(font)
self.label_9.setObjectName("label_9")
self.gridLayout_3.addWidget(self.label_9, 0, 0, 1, 1)
self.label_15 = QtWidgets.QLabel(parent=self.elecTab)
self.label_15.setObjectName("label_15")
self.gridLayout_3.addWidget(self.label_15, 11, 0, 1, 1)
self.viewLUTButton = QtWidgets.QPushButton(parent=self.elecTab)
self.viewLUTButton.setObjectName("viewLUTButton")
self.gridLayout_3.addWidget(self.viewLUTButton, 10, 0, 1, 2)
self.label_2 = QtWidgets.QLabel(parent=self.elecTab)
self.label_2.setObjectName("label_2")
self.gridLayout_3.addWidget(self.label_2, 1, 0, 1, 1)
self.matchingBox = QtWidgets.QDoubleSpinBox(parent=self.elecTab)
self.matchingBox.setObjectName("matchingBox")
self.gridLayout_3.addWidget(self.matchingBox, 2, 1, 1, 1)
self.generateLUTButton = QtWidgets.QPushButton(parent=self.elecTab)
self.generateLUTButton.setObjectName("generateLUTButton")
self.gridLayout_3.addWidget(self.generateLUTButton, 8, 0, 1, 2)
self.tuningBox = QtWidgets.QDoubleSpinBox(parent=self.elecTab)
self.tuningBox.setObjectName("tuningBox")
self.gridLayout_3.addWidget(self.tuningBox, 1, 1, 1, 1)
self.label_11 = QtWidgets.QLabel(parent=self.elecTab)
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
self.label_11.setFont(font)
self.label_11.setObjectName("label_11")
self.gridLayout_3.addWidget(self.label_11, 4, 0, 1, 1)
self.viewelLUTButton = QtWidgets.QPushButton(parent=self.elecTab)
self.viewelLUTButton.setObjectName("viewelLUTButton")
self.gridLayout_3.addWidget(self.viewelLUTButton, 10, 0, 1, 2)
self.setvoltagesButton = QtWidgets.QPushButton(parent=self.elecTab)
self.setvoltagesButton.setObjectName("setvoltagesButton")
self.gridLayout_3.addWidget(self.setvoltagesButton, 3, 0, 1, 2)
self.label_3 = QtWidgets.QLabel(parent=self.elecTab)
self.label_3.setObjectName("label_3")
self.gridLayout_3.addWidget(self.label_3, 2, 0, 1, 1)
self.label_12 = QtWidgets.QLabel(parent=self.elecTab)
self.label_12.setObjectName("label_12")
self.gridLayout_3.addWidget(self.label_12, 6, 0, 1, 1)
self.matchingBox = QtWidgets.QDoubleSpinBox(parent=self.elecTab)
self.matchingBox.setObjectName("matchingBox")
self.gridLayout_3.addWidget(self.matchingBox, 2, 1, 1, 1)
self.label_14 = QtWidgets.QLabel(parent=self.elecTab)
self.label_14.setObjectName("label_14")
self.gridLayout_3.addWidget(self.label_14, 8, 0, 1, 1)
self.switchpreampButton = QtWidgets.QPushButton(parent=self.elecTab)
self.switchpreampButton.setObjectName("switchpreampButton")
self.gridLayout_3.addWidget(self.switchpreampButton, 12, 0, 1, 1)
self.switchATMButton = QtWidgets.QPushButton(parent=self.elecTab)
self.switchATMButton.setObjectName("switchATMButton")
self.gridLayout_3.addWidget(self.switchATMButton, 12, 1, 1, 1)
self.prevVoltagecheckBox = QtWidgets.QCheckBox(parent=self.elecTab)
self.prevVoltagecheckBox.setObjectName("prevVoltagecheckBox")
self.gridLayout_3.addWidget(self.prevVoltagecheckBox, 9, 0, 1, 1)
self.typeTab.addTab(self.elecTab, "")
self.verticalLayout_2.addWidget(self.typeTab)
self.rfswitchLabel = QtWidgets.QLabel(parent=Form)
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
self.rfswitchLabel.setFont(font)
self.rfswitchLabel.setObjectName("rfswitchLabel")
self.verticalLayout_2.addWidget(self.rfswitchLabel)
self.gridLayout_7 = QtWidgets.QGridLayout()
self.gridLayout_7.setObjectName("gridLayout_7")
self.switchATMButton = QtWidgets.QPushButton(parent=Form)
self.switchATMButton.setObjectName("switchATMButton")
self.gridLayout_7.addWidget(self.switchATMButton, 0, 0, 1, 1)
self.switchpreampButton = QtWidgets.QPushButton(parent=Form)
self.switchpreampButton.setObjectName("switchpreampButton")
self.gridLayout_7.addWidget(self.switchpreampButton, 0, 1, 1, 1)
self.verticalLayout_2.addLayout(self.gridLayout_7)
self.titlefrequencyLabel = QtWidgets.QLabel(parent=Form)
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
self.titlefrequencyLabel.setFont(font)
self.titlefrequencyLabel.setObjectName("titlefrequencyLabel")
self.verticalLayout_2.addWidget(self.titlefrequencyLabel)
@ -191,21 +232,21 @@ class Ui_Form(object):
self.startEdit = QtWidgets.QLineEdit(parent=Form)
self.startEdit.setObjectName("startEdit")
self.gridLayout.addWidget(self.startEdit, 0, 1, 1, 1)
self.label_8 = QtWidgets.QLabel(parent=Form)
self.label_8.setObjectName("label_8")
self.gridLayout.addWidget(self.label_8, 1, 2, 1, 1)
self.stopEdit = QtWidgets.QLineEdit(parent=Form)
self.stopEdit.setObjectName("stopEdit")
self.gridLayout.addWidget(self.stopEdit, 1, 1, 1, 1)
self.label_6 = QtWidgets.QLabel(parent=Form)
self.label_6.setObjectName("label_6")
self.gridLayout.addWidget(self.label_6, 0, 2, 1, 1)
self.label_7 = QtWidgets.QLabel(parent=Form)
self.label_7.setObjectName("label_7")
self.gridLayout.addWidget(self.label_7, 1, 0, 1, 1)
self.label_6 = QtWidgets.QLabel(parent=Form)
self.label_6.setObjectName("label_6")
self.gridLayout.addWidget(self.label_6, 0, 2, 1, 1)
self.label_5 = QtWidgets.QLabel(parent=Form)
self.label_5.setObjectName("label_5")
self.gridLayout.addWidget(self.label_5, 0, 0, 1, 1)
self.stopEdit = QtWidgets.QLineEdit(parent=Form)
self.stopEdit.setObjectName("stopEdit")
self.gridLayout.addWidget(self.stopEdit, 1, 1, 1, 1)
self.label_8 = QtWidgets.QLabel(parent=Form)
self.label_8.setObjectName("label_8")
self.gridLayout.addWidget(self.label_8, 1, 2, 1, 1)
self.verticalLayout_2.addLayout(self.gridLayout)
self.startButton = QtWidgets.QPushButton(parent=Form)
self.startButton.setObjectName("startButton")
@ -219,6 +260,7 @@ class Ui_Form(object):
self.titleinfoLabel = QtWidgets.QLabel(parent=Form)
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
self.titleinfoLabel.setFont(font)
self.titleinfoLabel.setObjectName("titleinfoLabel")
self.verticalLayout_2.addWidget(self.titleinfoLabel)
@ -226,7 +268,7 @@ class Ui_Form(object):
self.scrollArea.setWidgetResizable(True)
self.scrollArea.setObjectName("scrollArea")
self.scrollAreaWidgetContents = QtWidgets.QWidget()
self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 297, 68))
self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 291, 83))
self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")
self.scrollArea.setWidget(self.scrollAreaWidgetContents)
self.verticalLayout_2.addWidget(self.scrollArea)
@ -241,11 +283,25 @@ class Ui_Form(object):
self.S11Plot.setSizePolicy(sizePolicy)
self.S11Plot.setObjectName("S11Plot")
self.verticalLayout_5.addWidget(self.S11Plot)
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
self.verticalLayout_4 = QtWidgets.QVBoxLayout()
self.verticalLayout_4.setObjectName("verticalLayout_4")
self.importButton = QtWidgets.QPushButton(parent=Form)
self.importButton.setObjectName("importButton")
self.verticalLayout_4.addWidget(self.importButton)
self.exportButton = QtWidgets.QPushButton(parent=Form)
self.exportButton.setObjectName("exportButton")
self.verticalLayout_4.addWidget(self.exportButton)
self.horizontalLayout.addLayout(self.verticalLayout_4)
spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
self.horizontalLayout.addItem(spacerItem1)
self.verticalLayout_5.addLayout(self.horizontalLayout)
self.horizontalLayout_2.addLayout(self.verticalLayout_5)
self.horizontalLayout_2.setStretch(1, 1)
self.retranslateUi(Form)
self.typeTab.setCurrentIndex(0)
self.typeTab.setCurrentIndex(1)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
@ -256,43 +312,50 @@ class Ui_Form(object):
self.label.setText(_translate("Form", "Port:"))
self.label_10.setText(_translate("Form", "Connected to:"))
self.connectButton.setText(_translate("Form", "Connect"))
self.titletypeLabel.setText(_translate("Form", "T&M Type:"))
self.homematcherButton.setText(_translate("Form", "Home"))
self.label_17.setText(_translate("Form", "Step Size:"))
self.label_18.setText(_translate("Form", "Tuning Stepper:"))
self.decreasetunerButton.setText(_translate("Form", "-"))
self.increasetunerButton.setText(_translate("Form", "+"))
self.label_19.setText(_translate("Form", "Matching Stepper:"))
self.decreasematcherButton.setText(_translate("Form", "-"))
self.increasematcherButton.setText(_translate("Form", "+"))
self.hometunerButton.setText(_translate("Form", "Home"))
self.label_16.setText(_translate("Form", "Stepper Control:"))
self.starpositionButton.setText(_translate("Form", "Start Position"))
self.typeTab.setTabText(self.typeTab.indexOf(self.mechTab), _translate("Form", "Mechanical"))
self.label_4.setText(_translate("Form", "Voltage Resolution"))
self.label_2.setText(_translate("Form", "Voltage Tuning"))
self.label_11.setText(_translate("Form", "Generate LUT:"))
self.generateLUTButton.setText(_translate("Form", "Start Voltage Sweep"))
self.tmsettingsLabel.setText(_translate("Form", "T&M Settings:"))
self.label_13.setText(_translate("Form", "Stop Frequency (MHz)"))
self.label_9.setText(_translate("Form", "Set Voltages:"))
self.label_15.setText(_translate("Form", "RF Switch:"))
self.viewLUTButton.setText(_translate("Form", "View LUT"))
self.setvoltagesButton.setText(_translate("Form", "Set Voltages"))
self.label_3.setText(_translate("Form", "Voltage Matching"))
self.label_12.setText(_translate("Form", "Start Frequency (MHz)"))
self.label_14.setText(_translate("Form", "Frequency Step (MHz)"))
self.switchpreampButton.setText(_translate("Form", "Preamplifier"))
self.switchATMButton.setText(_translate("Form", "ATM"))
self.titletypeLabel.setText(_translate("Form", "T&M Type:"))
self.homeButton.setText(_translate("Form", "Home"))
self.label_16.setText(_translate("Form", "Stepper Control:"))
self.stepperselectBox.setItemText(0, _translate("Form", "Tuning"))
self.stepperselectBox.setItemText(1, _translate("Form", "Matching"))
self.decreaseButton.setText(_translate("Form", "-"))
self.increaseButton.setText(_translate("Form", "+"))
self.label_18.setText(_translate("Form", "Stepper:"))
self.label_20.setText(_translate("Form", "Absolute:"))
self.label_17.setText(_translate("Form", "Step Size:"))
self.absoluteGoButton.setText(_translate("Form", "Go"))
self.label_4.setText(_translate("Form", "Position:"))
self.stepperposLabel.setText(_translate("Form", "0"))
self.positionButton.setText(_translate("Form", "Saved Positions"))
self.mechLUTButton.setText(_translate("Form", "Generate LUT"))
self.viewmechLUTButton.setText(_translate("Form", "View LUT"))
self.typeTab.setTabText(self.typeTab.indexOf(self.mechTab), _translate("Form", "Mechanical"))
self.label_9.setText(_translate("Form", "Set Voltages:"))
self.label_2.setText(_translate("Form", "Voltage Tuning"))
self.generateLUTButton.setText(_translate("Form", "Generate LUT"))
self.label_11.setText(_translate("Form", "Generate LUT:"))
self.viewelLUTButton.setText(_translate("Form", "View LUT"))
self.setvoltagesButton.setText(_translate("Form", "Set Voltages"))
self.label_3.setText(_translate("Form", "Voltage Matching"))
self.prevVoltagecheckBox.setText(_translate("Form", "Start from previous Voltage"))
self.typeTab.setTabText(self.typeTab.indexOf(self.elecTab), _translate("Form", "Electrical"))
self.rfswitchLabel.setText(_translate("Form", "RF Switch:"))
self.switchATMButton.setText(_translate("Form", "ATM"))
self.switchpreampButton.setText(_translate("Form", "Preamplifier"))
self.titlefrequencyLabel.setText(_translate("Form", "Frequency Sweep:"))
self.startEdit.setText(_translate("Form", "80"))
self.label_8.setText(_translate("Form", "MHz"))
self.stopEdit.setText(_translate("Form", "100"))
self.label_6.setText(_translate("Form", "MHz"))
self.label_7.setText(_translate("Form", "Stop Frequency:"))
self.label_6.setText(_translate("Form", "MHz"))
self.label_5.setText(_translate("Form", "Start Frequency:"))
self.stopEdit.setText(_translate("Form", "100"))
self.label_8.setText(_translate("Form", "MHz"))
self.startButton.setText(_translate("Form", "Start Sweep"))
self.calibrationButton.setText(_translate("Form", "Calibrate"))
self.pushButton_3.setText(_translate("Form", "T&M Settings"))
self.titleinfoLabel.setText(_translate("Form", "Info Box:"))
self.importButton.setText(_translate("Form", "Import Measurement"))
self.exportButton.setText(_translate("Form", "Export Measurement"))
from nqrduck.contrib.mplwidget import MplWidget