Compare commits

...

5 Commits

Author SHA1 Message Date
linuswck 3f9a4bf140 ld: Block pwr on request if alarm is asserted
- Align the alarm and pwr up behavior with thermostat temp_mon
2024-07-26 14:29:46 +08:00
linuswck 201148fb21 main: allow loading flash without device settings
- Fix bugs: ld & thermostat settings cannot be loaded if device settings
    are not present. But, device settings can be left not configured.
2024-07-26 11:10:32 +08:00
linuswck a90031dd6c ld_pwr_exc_protector: clear irq bit at pwr up 2024-07-26 11:10:32 +08:00
linuswck 7b52072617 ld: cfg hardware when pd_mon params changes 2024-07-26 11:10:32 +08:00
linuswck c241d34434 gui: fix default units and mv to params' titles
- siPrefix(eg. m, u, k) is no longer editable and fixed
- the displayed unit is relocated to ctrl panel params' titles
2024-07-26 11:10:28 +08:00
4 changed files with 100 additions and 42 deletions

View File

@ -5,6 +5,7 @@ from pyqtgraph.parametertree import Parameter, ParameterTree, ParameterItem, reg
import pyqtgraph as pg
pg.setConfigOptions(antialias=True)
from pyqtgraph import mkPen
from pyqtgraph.functions import siEval, siParse, SI_PREFIX_EXPONENTS, FLOAT_REGEX
from pglive.sources.live_axis_range import LiveAxisRange
from pglive.sources.data_connector import DataConnector
from pglive.kwargs import Axis, LeadingLine
@ -45,6 +46,26 @@ def get_argparser():
return parser
def siConvert(val, suffix, typ=float):
"""
Convert a value written in SI notation according to given Si Scale.
Example::
siConvert(0.1, "mA") # returns 100
"""
val, siprefix, suffix = siParse(str(val)+suffix, FLOAT_REGEX)
v = typ(val)
n = -SI_PREFIX_EXPONENTS[siprefix] if siprefix != '' else 0
if n > 0:
return v * 10**n
elif n < 0:
# this case makes it possible to use Decimal objects here
return v / 10**-n
else:
return v
class KirdyDataWatcher(QObject):
"""
This class provides various signals for Mainwindow to update various kinds of GUI objects
@ -341,18 +362,18 @@ class MainWindow(QtWidgets.QMainWindow):
{'name': 'LF Mod Termination (50 Ohm)', 'type': 'list', 'limits': ['On', 'Off'], 'readonly': True}
]},
{'name': 'Output Config', 'expanded': True, 'type': 'group', 'children': [
{'name': 'LD Current Set', 'type': 'float', 'value': 0, 'step': 1, 'decimals': 6, 'limits': (0, 1),
'suffix': 'A', 'siPrefix': True, 'lock': False, 'target': 'laser', 'action': 'set_i'},
{'name': 'LD Current Set', 'type': 'float', 'value': 0, 'step': 0.001, 'decimals': 6, 'limits': (0, 300),
'unit': 'mA', 'lock': False, 'target': 'laser', 'action': 'set_i'},
{'name': 'LD Terminals Short', 'type': 'bool', 'value': False, 'lock': False, 'target': 'laser', 'action': 'set_ld_terms_short'},
{'name': 'Default Power On', 'type': 'bool', 'value': False, 'lock': False, 'target': 'laser', 'action': 'set_default_pwr_on'},
]},
{'name': 'Photodiode Monitor Config', 'expanded': False, 'type': 'group', 'children': [
{'name': 'LD Power Limit', 'type': 'float', 'value': 0, 'step': 1, 'decimals': 6, 'limits': (0, 0.3),
'suffix': 'W', 'siPrefix': True, 'lock': False, 'target': 'laser', 'action': 'set_ld_pwr_limit'},
{'name': 'Responsitivity', 'type': 'float', 'value': 0, 'step': 1, 'decimals': 6, 'limits': (0, 3000),
'suffix': 'A/W', 'siPrefix': True, 'lock': False, 'target': 'laser', 'action': 'set_pd_mon_responsitivity'},
{'name': 'Dark Current', 'type': 'float', 'value': 0, 'step': 1, 'decimals': 6, 'limits': (0, 3000),
'suffix': 'A', 'siPrefix': True, 'lock': False, 'target': 'laser', 'action': 'set_pd_mon_dark_current'},
{'name': 'LD Power Limit', 'type': 'float', 'value': 0, 'step': 0.001, 'decimals': 6, 'limits': (0, float("inf")),
'unit': 'mW', 'lock': False, 'target': 'laser', 'action': 'set_ld_pwr_limit'},
{'name': 'Responsitivity', 'type': 'float', 'value': 0, 'step': 0.001, 'decimals': 6, 'limits': (0, float("inf")),
'unit': 'mA/W', 'lock': False, 'target': 'laser', 'action': 'set_pd_mon_responsitivity'},
{'name': 'Dark Current', 'type': 'float', 'value': 0, 'step': 1, 'decimals': 6, 'limits': (0, float("inf")),
'unit': 'uA', 'lock': False, 'target': 'laser', 'action': 'set_pd_mon_dark_current'},
]},
]
@ -368,49 +389,52 @@ class MainWindow(QtWidgets.QMainWindow):
]},
{'name': 'Output Config', 'expanded': True, 'type': 'group', 'children': [
{'name': 'Control Method', 'type': 'mutex', 'limits': ['Constant Current', 'Temperature PID'],
'target_action_pair': [['thermostat', 'set_constant_current_control_mode'], ['thermostat', 'set_pid_control_mode']], 'children': [
{'name': 'Set Current', 'type': 'float', 'value': 0, 'step': 0.001, 'limits': (-1, 1), 'triggerOnShow': True,
'decimals': 6, 'suffix': 'A', 'siPrefix': True, 'lock': False, 'target': 'thermostat', 'action': 'set_tec_i_out'},
{'name': 'Set Temperature', 'type': 'float', 'value': 25, 'step': 0.1, 'limits': (-273, 300),
'format': '{value:.4f} °C', 'lock': False, 'target': 'thermostat', 'action': 'set_temperature_setpoint'},
'target_action_pair': [['thermostat', 'set_constant_current_control_mode'], ['thermostat', 'set_pid_control_mode']], 'children': [
{'name': 'Set Current', 'type': 'float', 'value': 0, 'step': 1, 'limits': (-1000, 1000), 'triggerOnShow': True, 'decimals': 6,
'unit': 'mA', 'lock': False, 'target': 'thermostat', 'action': 'set_tec_i_out'},
{'name': 'Set Temperature', 'type': 'float', 'value': 25, 'step': 0.0001, 'limits': (-273, 300), 'format': '{value:.4f}',
'unit': '°C', 'lock': False, 'target': 'thermostat', 'action': 'set_temperature_setpoint'},
]},
{'name': 'Limits', 'expanded': False, 'type': 'group', 'children': [
{'name': 'Max Cooling Current', 'type': 'float', 'value': 0, 'step': 0.001, 'decimals': 6, 'limits': (0, 1),
'suffix': 'A', 'siPrefix': True, 'lock': False, 'target': 'thermostat', 'action': 'set_tec_max_cooling_i'},
{'name': 'Max Heating Current', 'type': 'float', 'value': 0, 'step': 0.001, 'decimals': 6, 'limits': (0, 1),
'suffix': 'A', 'siPrefix': True, 'lock': False, 'target': 'thermostat', 'action': 'set_tec_max_heating_i'},
{'name': 'Max Voltage Difference', 'type': 'float', 'value': 0, 'step': 0.1, 'limits': (0, 5),
'suffix': 'V', 'siPrefix': True, 'lock': False, 'target': 'thermostat', 'action': 'set_tec_max_v'},
{'name': 'Max Cooling Current', 'type': 'float', 'value': 0, 'step': 1, 'decimals': 6, 'limits': (0, 1000),
'unit': 'mA', 'lock': False, 'target': 'thermostat', 'action': 'set_tec_max_cooling_i'},
{'name': 'Max Heating Current', 'type': 'float', 'value': 0, 'step': 1, 'decimals': 6, 'limits': (0, 1000),
'unit': 'mA', 'lock': False, 'target': 'thermostat', 'action': 'set_tec_max_heating_i'},
{'name': 'Max Voltage Difference', 'type': 'float', 'value': 0, 'step': 0.1, 'limits': (0, 4),
'unit': 'V', 'lock': False, 'target': 'thermostat', 'action': 'set_tec_max_v'},
]},
{'name': 'Default Power On', 'type': 'bool', 'value': False, 'lock': False, 'target': 'thermostat', 'action': 'set_default_pwr_on'},
]},
# TODO Temperature ADC Filter Settings
{'name': 'Temperature Monitor Config', 'expanded': False, 'type': 'group', 'children': [
{'name': 'Upper Limit', 'type': 'float', 'value': 0, 'step': 1, 'decimals': 6, 'limits': (-273, 300),
'suffix': '°C', 'lock': False, 'target': 'thermostat', 'action': 'set_temp_mon_upper_limit'},
'unit': '°C', 'lock': False, 'target': 'thermostat', 'action': 'set_temp_mon_upper_limit'},
{'name': 'Lower Limit', 'type': 'float', 'value': 0, 'step': 1, 'decimals': 6, 'limits': (-273, 300),
'suffix': '°C', 'lock': False, 'target': 'thermostat', 'action': 'set_temp_mon_lower_limit'},
'unit': '°C', 'lock': False, 'target': 'thermostat', 'action': 'set_temp_mon_lower_limit'},
]},
{'name': 'Thermistor Settings','expanded': False, 'type': 'group', 'children': [
{'name': 'T₀', 'type': 'float', 'value': 0, 'step': 1, 'decimals': 6, 'limits': (-273, 300),
'suffix': '°C', 'lock': False, 'target': 'thermostat', 'action': 'set_sh_t0'},
'unit': '°C', 'lock': False, 'target': 'thermostat', 'action': 'set_sh_t0'},
{'name': 'R₀', 'type': 'float', 'value': 0, 'step': 1, 'decimals': 6,
'suffix': 'Ω', 'siPrefix': True, 'lock': False, 'target': 'thermostat', 'action': 'set_sh_r0'},
{'name': 'B', 'type': 'float', 'value': 3950, 'step': 1, 'suffix': 'K', 'decimals': 4, 'lock': False, 'target': 'thermostat', 'action': 'set_sh_beta'},
'unit': '', 'lock': False, 'target': 'thermostat', 'action': 'set_sh_r0'},
{'name': 'B', 'type': 'float', 'value': 3950, 'step': 1, 'decimals': 4,
'unit': 'K', 'lock': False, 'target': 'thermostat', 'action': 'set_sh_beta'},
]},
{'name': 'PID Config', 'expanded': False, 'type': 'group', 'children': [
{'name': 'Kp', 'type': 'float', 'step': 0.1, 'suffix': '', 'lock': False, 'target': 'thermostat', 'action': 'set_pid_kp'},
{'name': 'Ki', 'type': 'float', 'step': 0.1, 'suffix': 'Hz', 'lock': False, 'target': 'thermostat', 'action': 'set_pid_ki'},
{'name': 'Kd', 'type': 'float', 'step': 0.1, 'suffix': 's', 'lock': False, 'target': 'thermostat', 'action': 'set_pid_kd'},
{'name': 'Kp', 'type': 'float', 'step': 0.1, 'lock': False, 'target': 'thermostat', 'action': 'set_pid_kp'},
{'name': 'Ki', 'type': 'float', 'step': 0.1, 'unit': 'Hz', 'lock': False, 'target': 'thermostat', 'action': 'set_pid_ki'},
{'name': 'Kd', 'type': 'float', 'step': 0.1, 'unit': 's', 'lock': False, 'target': 'thermostat', 'action': 'set_pid_kd'},
{'name': "PID Output Clamping", 'expanded': True, 'type': 'group', 'children': [
{'name': 'Minimum', 'type': 'float', 'step': 100, 'limits': (-1, 1), 'decimals': 6, 'suffix': 'A', 'lock': False, 'target': 'thermostat', 'action': 'set_pid_output_min'},
{'name': 'Maximum', 'type': 'float', 'step': 100, 'limits': (-1, 1), 'decimals': 6, 'suffix': 'A', 'lock': False, 'target': 'thermostat', 'action': 'set_pid_output_max'},
{'name': 'Minimum', 'type': 'float', 'step': 1, 'limits': (-1000, 1000), 'decimals': 6,
'unit': 'mA', 'lock': False, 'target': 'thermostat', 'action': 'set_pid_output_min'},
{'name': 'Maximum', 'type': 'float', 'step': 1, 'limits': (-1000, 1000), 'decimals': 6,
'unit': 'mA', 'lock': False, 'target': 'thermostat', 'action': 'set_pid_output_max'},
]},
{'name': 'PID Auto Tune', 'expanded': False, 'type': 'group', 'children': [
{'name': 'Target Temperature', 'type': 'float', 'value': 20, 'step': 0.1, 'format': '{value:.4f} °C'},
{'name': 'Test Current', 'type': 'float', 'value': 1000, 'decimals': 6, 'step': 100, 'limits': (-3000, 3000), 'suffix': 'mA'},
{'name': 'Temperature Swing', 'type': 'float', 'value': 0.0, 'step': 0.1, 'prefix': '±', 'format': '{value:.4f} °C'},
{'name': 'Lookback', 'type': 'float', 'value': 5.0, 'step': 0.1, 'format': '{value:.4f} s'},
{'name': 'Target Temperature', 'type': 'float', 'value': 20.0, 'step': 0.1, 'unit': '°C', 'format': '{value:.4f}'},
{'name': 'Test Current', 'type': 'float', 'value': 1000, 'decimals': 6, 'step': 100, 'limits': (-3000, 3000), 'unit': 'mA'},
{'name': 'Temperature Swing', 'type': 'float', 'value': 0.0, 'step': 0.001, 'prefix': '±', 'unit': '°C', 'format': '{value:.4f}'},
{'name': 'Lookback', 'type': 'float', 'value': 5.0, 'step': 0.1, 'unit': 's', 'format': '{value:.4f}'},
{'name': 'Run', 'type': 'action', 'tip': 'Run'},
]},
]},
@ -449,9 +473,34 @@ class MainWindow(QtWidgets.QMainWindow):
def _setValuewithLock(self, value):
if not self.opts.get("lock", None):
self.setValue(value)
if self.opts.get("unit", None) is not None:
self.setValue(siConvert(value, self.opts.get("unit", None)))
else:
self.setValue(value)
Parameter.setValuewithLock = _setValuewithLock
def _add_unit_to_title(param_tree):
def _traverse(param):
if param["type"] == "group" or param["type"] == "mutex":
for param in param["children"]:
_add_unit_to_title(param)
else:
if "unit" in param.keys():
if not("title" in param.keys()):
param["title"] = param["name"]
param["title"] = param["title"] + " ({:})".format(param["unit"])
if isinstance(param_tree, list):
for param in param_tree:
_traverse(param)
else:
_traverse(param_tree)
_add_unit_to_title(self.LASER_DIODE_STATUS)
_add_unit_to_title(self.LASER_DIODE_PARAMETERS)
_add_unit_to_title(self.THERMOSTAT_STATUS)
_add_unit_to_title(self.THERMOSTAT_PARAMETERS)
self.params = [
Parameter.create(name=f"Laser Diode Status", type='group', value=0, children=self.LASER_DIODE_STATUS),
Parameter.create(name=f"Laser Diode Parameters", type='group', value=1, children=self.LASER_DIODE_PARAMETERS),
@ -908,6 +957,11 @@ class MainWindow(QtWidgets.QMainWindow):
""" cmd translation from non-mutex type parameter"""
if inner_param.opts.get("target", None) is not None:
if inner_param.opts.get("action", None) is not None:
if inner_param.opts.get("unit", None) is not None:
# siParse is buggy on ° character
if not inner_param.opts["unit"] == "°C":
_, _, suffix = siParse(str(data)+inner_param.opts["unit"])
data = siEval(str(data)+inner_param.opts["unit"], suffix=suffix)
cmd = getattr(getattr(self.kirdy, inner_param.opts["target"]), inner_param.opts["action"])
param.child(*param.childPath(inner_param)).setOpts(lock=True)
await cmd(data)

View File

@ -182,10 +182,12 @@ impl LdDrive {
pub fn set_pd_responsitivity(&mut self, responsitivity: pd_mon_params::ResponsitivityUnit) {
self.settings.pd_mon_params.set(responsitivity);
self.set_ld_power_limit(self.settings.ld_pwr_limit)
}
pub fn set_pd_dark_current(&mut self, i_dark: ElectricCurrent) {
self.settings.pd_mon_params.set_i_dark(i_dark);
self.set_ld_power_limit(self.settings.ld_pwr_limit)
}
pub fn set_ld_power_limit(&mut self, pwr_limit: Power) {

View File

@ -211,11 +211,14 @@ impl LdPwrExcProtector {
pub fn pwr_on_and_arm_protection() {
if let Some(ref mut wdg) = LdPwrExcProtector::get() {
wdg.alarm_status = Status::default();
LdPwrExcProtector::pwr_on();
// Interrupt should be enabled after power on to tackle the following edge case:
// Pd_Mon pin voltage has already exceed threshold before LD Power is on.
LdPwrExcProtector::enable_watchdog_interrupt();
if !wdg.alarm_status.pwr_excursion {
wdg.alarm_status = Status::default();
LdPwrExcProtector::clear_interrupt_bit();
LdPwrExcProtector::pwr_on();
// Interrupt should be enabled after power on to tackle the following edge case:
// Pd_Mon pin voltage has already exceed threshold before LD Power is on.
LdPwrExcProtector::enable_watchdog_interrupt();
}
}
}

View File

@ -101,10 +101,10 @@ fn main() -> ! {
device_settings_flash = config;
laser.set_pd_transconductance(config.pd_mon_fin_gain * config.pd_mon_transconductance);
debug!("Found Device Settings");
device_settings = device_settings_flash;
}
Ok(None) => {
debug!("Flash does not have Device Settings");
continue;
info!("Flash does not have Device Settings");
}
Err(e) => {
debug!("Cannot Store Flash: {:?}", e);
@ -147,7 +147,6 @@ fn main() -> ! {
}
wd.feed();
device_settings = device_settings_flash;
thermostat.load_settings_from_summary(thermostat_settings);
laser.load_settings_from_summary(laser_settings);
}