Compare commits

...

6 Commits

Author SHA1 Message Date
linuswck 4d332328c0 gui: Limit min PD Mon Pwr y axis range to 0-100uW 2024-10-23 12:24:57 +08:00
linuswck edb8099a26 Add pglive patch for setting y-axis min range span 2024-10-23 12:24:57 +08:00
linuswck 49dc8a9b96 gui: autotuner only set_i when relay state changes
- Previously, autotuner issues set_i cmd for each report obj received
- These change increase the maximum autotune-able PID update rate with GUI
2024-10-22 18:09:39 +08:00
linuswck a1b7538295 driver: Fix disconnection bug at high cmd rate
- Disconnection issue can be triggered by setting high polling rate
(500Hz) and then interactive with gui elements
2024-10-22 18:09:28 +08:00
linuswck 8f5a294a53 gui: Fix graphs' refresh rate to be 10Hz
- Reduce unnecessary CPU usage
2024-10-22 10:57:55 +08:00
linuswck 87b0e1bf67 gui: rm sIPrefix from PD Current readings 2024-10-21 11:15:15 +08:00
4 changed files with 112 additions and 42 deletions

View File

@ -62,6 +62,7 @@
inherit pname version; inherit pname version;
hash = "sha256-jqj8X6H1N5mJQ4OrY5ANqRB0YJByqg/bNneEALWmH1A="; hash = "sha256-jqj8X6H1N5mJQ4OrY5ANqRB0YJByqg/bNneEALWmH1A=";
}; };
patches = ./patches/0001-Add-option-for-setting-min-range-span-for-y-axis.patch;
buildInputs = [ pkgs.python3Packages.poetry-core ]; buildInputs = [ pkgs.python3Packages.poetry-core ];
propagatedBuildInputs = with pkgs.python3Packages; [ pyqtgraph numpy ]; propagatedBuildInputs = with pkgs.python3Packages; [ pyqtgraph numpy ];
}; };

View File

@ -0,0 +1,61 @@
From 59a14e06320fd42d56cd7d953da337c0fe4357fa Mon Sep 17 00:00:00 2001
From: linuswck <linuswck@m-labs.hk>
Date: Tue, 22 Oct 2024 17:30:36 +0800
Subject: [PATCH] Add option for setting min range span for y-axis
---
pglive/sources/live_axis_range.py | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/pglive/sources/live_axis_range.py b/pglive/sources/live_axis_range.py
index afbd806..9b62ba5 100644
--- a/pglive/sources/live_axis_range.py
+++ b/pglive/sources/live_axis_range.py
@@ -5,7 +5,7 @@ from typing import Optional, List, Tuple, Dict
class LiveAxisRange:
def __init__(self, roll_on_tick: int = 1, offset_left: float = 0., offset_right: float = 0., offset_top: float = 0.,
- offset_bottom: float = 0., fixed_range: Optional[List[float]] = None) -> None:
+ offset_bottom: float = 0., fixed_range: Optional[List[float]] = None, min_y_range_span: Optional[List[float]] = [None, None]) -> None:
self.roll_on_tick = roll_on_tick
self.offset_left = offset_left
self.offset_right = offset_right
@@ -21,6 +21,7 @@ class LiveAxisRange:
self.final_x_range = [0., 0.]
self.final_y_range = [0., 0.]
self.ignored_data_connectors: List[str] = []
+ self.min_y_range_span = min_y_range_span
def get_x_range(self, data_connector, tick: int) -> List[float]:
x, _ = data_connector.plot.getData()
@@ -116,6 +117,13 @@ class LiveAxisRange:
# therefore in that case we must set some range
final_range[0] -= 0.4
final_range[1] += 0.4
+
+ if self.min_y_range_span[0] is not None:
+ final_range[0] = min(self.min_y_range_span[0], final_range[0])
+
+ if self.min_y_range_span[1] is not None:
+ final_range[1] = max(self.min_y_range_span[1], final_range[1])
+
if self.final_y_range != final_range:
self.final_y_range = final_range
return self.final_y_range
@@ -139,6 +147,13 @@ class LiveAxisRange:
# therefore in that case we must set some range
final_range[0] -= 0.4
final_range[1] += 0.4
+
+ if self.min_y_range_span[0] is not None:
+ final_range[0] = min(self.min_y_range_span[0], final_range[0])
+
+ if self.min_y_range_span[1] is not None:
+ final_range[1] = max(self.min_y_range_span[1], final_range[1])
+
if self.final_y_range != final_range:
self.final_y_range = final_range
return self.final_y_range
--
2.44.1

View File

@ -879,7 +879,7 @@ class Kirdy:
else: else:
self._report_sig.emit(response) self._report_sig.emit(response)
else: else:
if self._msg_queue_get_report: if self._msg_queue_get_report and response["msg_type"] == 'Report':
self._msg_queue_get_report = False self._msg_queue_get_report = False
self._int_msg_queue.put_nowait_overwrite(response) self._int_msg_queue.put_nowait_overwrite(response)
except asyncio.exceptions.CancelledError: except asyncio.exceptions.CancelledError:

View File

@ -196,14 +196,16 @@ class Graphs:
ld_i_set_graph.setAxisItems({'left': ld_i_set_axis}) ld_i_set_graph.setAxisItems({'left': ld_i_set_axis})
ld_i_set_graph.addItem(self._ld_i_set_plot) ld_i_set_graph.addItem(self._ld_i_set_plot)
ld_i_set_graph.y_range_controller = LiveAxisRange(fixed_range=[0.0, 0.4]) ld_i_set_graph.y_range_controller = LiveAxisRange(fixed_range=[0.0, 0.4])
self.ld_i_set_connector = DataConnector(self._ld_i_set_plot, max_points=self.max_samples) self.ld_i_set_connector = DataConnector(self._ld_i_set_plot, plot_rate=10.0, update_rate=10.0, max_points=self.max_samples)
self.connectors += [self.ld_i_set_connector] self.connectors += [self.ld_i_set_connector]
pd_mon_pwr_axis = LiveAxis('left', text="Power", units="W") pd_mon_pwr_axis = LiveAxis('left', text="Power", units="W")
pd_mon_pwr_axis.showLabel() pd_mon_pwr_axis.showLabel()
pd_mon_pwr_graph.y_range_controller = LiveAxisRange(min_y_range_span=[0.0, 100 / 1000 / 1000])
pd_mon_pwr_graph.setAxisItems({'left': pd_mon_pwr_axis}) pd_mon_pwr_graph.setAxisItems({'left': pd_mon_pwr_axis})
pd_mon_pwr_graph.addItem(self._pd_mon_pwr_plot) pd_mon_pwr_graph.addItem(self._pd_mon_pwr_plot)
self.pd_mon_pwr_connector = DataConnector(self._pd_mon_pwr_plot, max_points=self.max_samples) self.pd_mon_pwr_connector = DataConnector(self._pd_mon_pwr_plot, plot_rate=10.0, update_rate=10.0, max_points=self.max_samples)
self.connectors += [self.pd_mon_pwr_connector] self.connectors += [self.pd_mon_pwr_connector]
tec_temp_axis = LiveAxis('left', text="Temperature", units="") tec_temp_axis = LiveAxis('left', text="Temperature", units="")
@ -211,8 +213,8 @@ class Graphs:
tec_temp_graph.setAxisItems({'left': tec_temp_axis}) tec_temp_graph.setAxisItems({'left': tec_temp_axis})
tec_temp_graph.addItem(self._tec_setpoint_plot) tec_temp_graph.addItem(self._tec_setpoint_plot)
tec_temp_graph.addItem(self._tec_temp_plot) tec_temp_graph.addItem(self._tec_temp_plot)
self.tec_setpoint_connector = DataConnector(self._tec_setpoint_plot, max_points=1) self.tec_setpoint_connector = DataConnector(self._tec_setpoint_plot, plot_rate=10.0, update_rate=10.0, max_points=1)
self.tec_temp_connector = DataConnector(self._tec_temp_plot, max_points=self.max_samples) self.tec_temp_connector = DataConnector(self._tec_temp_plot, plot_rate=10.0, update_rate=10.0, max_points=self.max_samples)
self.connectors += [self.tec_temp_connector, self.tec_setpoint_connector] self.connectors += [self.tec_temp_connector, self.tec_setpoint_connector]
tec_i_axis = LiveAxis('left', text="Current", units="A") tec_i_axis = LiveAxis('left', text="Current", units="A")
@ -221,8 +223,8 @@ class Graphs:
tec_i_graph.addLegend(brush=(50, 50, 200, 150)) tec_i_graph.addLegend(brush=(50, 50, 200, 150))
tec_i_graph.addItem(self._tec_i_target_plot) tec_i_graph.addItem(self._tec_i_target_plot)
tec_i_graph.addItem(self._tec_i_measure_plot) tec_i_graph.addItem(self._tec_i_measure_plot)
self.tec_i_target_connector = DataConnector(self._tec_i_target_plot, max_points=self.max_samples) self.tec_i_target_connector = DataConnector(self._tec_i_target_plot, plot_rate=10.0, update_rate=10.0, max_points=self.max_samples)
self.tec_i_measure_connector = DataConnector(self._tec_i_measure_plot, max_points=self.max_samples) self.tec_i_measure_connector = DataConnector(self._tec_i_measure_plot, plot_rate=10.0, update_rate=10.0, max_points=self.max_samples)
self.connectors += [self.tec_i_target_connector, self.tec_i_measure_connector] self.connectors += [self.tec_i_target_connector, self.tec_i_measure_connector]
def set_max_samples(self, max_samples): def set_max_samples(self, max_samples):
@ -403,7 +405,7 @@ class MainWindow(QtWidgets.QMainWindow):
LASER_DIODE_PARAMETERS = [ LASER_DIODE_PARAMETERS = [
{'name': 'Readings', 'expanded': True, 'type': 'group', 'children': [ {'name': 'Readings', 'expanded': True, 'type': 'group', 'children': [
{'name': 'LD Current Set', 'type': 'float', 'unit': 'mA', 'readonly': True, "compactHeight": False}, {'name': 'LD Current Set', 'type': 'float', 'unit': 'mA', 'readonly': True, "compactHeight": False},
{'name': 'PD Current', 'type': 'float', 'unit': 'uA', 'siPrefix': True, 'readonly': True, "compactHeight": False}, {'name': 'PD Current', 'type': 'float', 'unit': 'uA', 'readonly': True, "compactHeight": False},
{'name': 'PD Power', 'type': 'float', 'unit': 'mW', 'readonly': True, "compactHeight": False}, {'name': 'PD Power', 'type': 'float', 'unit': 'mW', 'readonly': True, "compactHeight": False},
{'name': 'LF Mod Termination (50 Ohm)', 'type': 'list', 'limits': ['On', 'Off'], 'readonly': True, "compactHeight": False} {'name': 'LF Mod Termination (50 Ohm)', 'type': 'list', 'limits': ['On', 'Off'], 'readonly': True, "compactHeight": False}
]}, ]},
@ -814,8 +816,10 @@ class MainWindow(QtWidgets.QMainWindow):
tree.setParameters(self.params[1], showTop=False) tree.setParameters(self.params[1], showTop=False)
self.params[1].sigTreeStateChanged.connect(self.send_command) self.params[1].sigTreeStateChanged.connect(self.send_command)
self.prev_autotuner_state = None
@asyncSlot() @asyncSlot()
async def autotune(param): async def autotune(param):
self.prev_autotuner_state = None
match self.autotuner.state(): match self.autotuner.state():
case PIDAutotuneState.STATE_OFF: case PIDAutotuneState.STATE_OFF:
settings = await self.kirdy.device.get_settings_summary() settings = await self.kirdy.device.get_settings_summary()
@ -825,6 +829,7 @@ class MainWindow(QtWidgets.QMainWindow):
param.parent().child('Temperature Swing').value(), param.parent().child('Temperature Swing').value(),
1.0 / settings['thermostat']['temp_adc_settings']['rate'], 1.0 / settings['thermostat']['temp_adc_settings']['rate'],
param.parent().child('Lookback').value()) param.parent().child('Lookback').value())
print(param.parent().child('Lookback').value())
self.autotuner.setReady() self.autotuner.setReady()
param.setOpts(title="Stop") param.setOpts(title="Stop")
self.kirdy.task_dispatcher(self.kirdy.thermostat.set_constant_current_control_mode()) self.kirdy.task_dispatcher(self.kirdy.thermostat.set_constant_current_control_mode())
@ -878,40 +883,43 @@ class MainWindow(QtWidgets.QMainWindow):
self.info_box.setWindowTitle("Command fails to execute") self.info_box.setWindowTitle("Command fails to execute")
self.info_box.show() self.info_box.show()
@pyqtSlot(dict) @asyncSlot(dict)
def autotune_tick(self, report): async def autotune_tick(self, report):
match self.autotuner.state(): self.autotuner.run(report['thermostat']['temperature'], report['ts']/1000)
case PIDAutotuneState.STATE_READY | PIDAutotuneState.STATE_RELAY_STEP_UP | PIDAutotuneState.STATE_RELAY_STEP_DOWN: if self.prev_autotuner_state != self.autotuner.state():
self.autotuner.run(report['thermostat']['temperature'], report['ts']/1000) match self.autotuner.state():
self.kirdy.task_dispatcher(self.kirdy.thermostat.set_tec_i_out(self.autotuner.output())) case PIDAutotuneState.STATE_READY | PIDAutotuneState.STATE_RELAY_STEP_UP | PIDAutotuneState.STATE_RELAY_STEP_DOWN:
case PIDAutotuneState.STATE_SUCCEEDED: await self.kirdy.thermostat.set_tec_i_out(self.autotuner.output())
kp, ki, kd = self.autotuner.get_tec_pid() self.prev_autotuner_state = self.autotuner.state()
self.autotuner.setOff() case PIDAutotuneState.STATE_SUCCEEDED:
self.params[1].child('PID Config', 'PID Auto Tune', 'Run').setOpts(title="Run") kp, ki, kd = self.autotuner.get_tec_pid()
self.kirdy.task_dispatcher(self.kirdy.thermostat.set_pid_kp(kp)) self.autotuner.setOff()
self.kirdy.task_dispatcher(self.kirdy.thermostat.set_pid_ki(ki)) self.params[1].child('PID Config', 'PID Auto Tune', 'Run').setOpts(title="Run")
self.kirdy.task_dispatcher(self.kirdy.thermostat.set_pid_kd(kd)) self.kirdy.task_dispatcher(self.kirdy.thermostat.set_pid_kp(kp))
self.kirdy.task_dispatcher(self.kirdy.thermostat.set_pid_control_mode()) self.kirdy.task_dispatcher(self.kirdy.thermostat.set_pid_ki(ki))
self.kirdy.task_dispatcher(self.kirdy.thermostat.set_temperature_setpoint(self.params[1].child('PID Config', 'PID Auto Tune', 'Target Temperature').value())) self.kirdy.task_dispatcher(self.kirdy.thermostat.set_pid_kd(kd))
self.kirdy_handler.report_update_sig.disconnect(self.autotune_tick) self.kirdy.task_dispatcher(self.kirdy.thermostat.set_pid_control_mode())
self.background_task_lbl.setText("Ready.") self.kirdy.task_dispatcher(self.kirdy.thermostat.set_temperature_setpoint(self.params[1].child('PID Config', 'PID Auto Tune', 'Target Temperature').value()))
self.loading_spinner.stop() self.kirdy_handler.report_update_sig.disconnect(self.autotune_tick)
self.loading_spinner.hide() self.background_task_lbl.setText("Ready.")
self.info_box.setWindowTitle("PID AutoTune Success") self.loading_spinner.stop()
self.info_box.setText("PID Config has been loaded to Thermostat.\nRegulating temperature.") self.loading_spinner.hide()
self.info_box.show() self.info_box.setWindowTitle("PID AutoTune Success")
self.info_box.setText("PID Config has been loaded to Thermostat.\nRegulating temperature.")
case PIDAutotuneState.STATE_FAILED: self.info_box.show()
self.autotuner.setOff() self.prev_autotuner_state = None
self.params[1].child('PID Config', 'PID Auto Tune', 'Run').setOpts(title="Run") case PIDAutotuneState.STATE_FAILED:
self.kirdy.task_dispatcher(self.kirdy.thermostat.set_tec_i_out(0.0)) self.autotuner.setOff()
self.kirdy_handler.report_update_sig.disconnect(self.autotune_tick) self.params[1].child('PID Config', 'PID Auto Tune', 'Run').setOpts(title="Run")
self.background_task_lbl.setText("Ready.") self.kirdy.task_dispatcher(self.kirdy.thermostat.set_tec_i_out(0.0))
self.loading_spinner.stop() self.kirdy_handler.report_update_sig.disconnect(self.autotune_tick)
self.loading_spinner.hide() self.background_task_lbl.setText("Ready.")
self.info_box.setWindowTitle("PID Autotune Failed") self.loading_spinner.stop()
self.info_box.setText("PID Autotune is failed.") self.loading_spinner.hide()
self.info_box.show() self.info_box.setWindowTitle("PID Autotune Failed")
self.info_box.setText("PID Autotune is failed.")
self.info_box.show()
self.prev_autotuner_state = None
@pyqtSlot(bool) @pyqtSlot(bool)
def _on_connection_changed(self, result): def _on_connection_changed(self, result):