From 983286d47b9cad48317d7bcbb6ca297a54af3e3a Mon Sep 17 00:00:00 2001 From: Marko Mecina <marko.mecina@univie.ac.at> Date: Fri, 21 Jul 2023 13:23:47 +0200 Subject: [PATCH] add Python implementation of SMILE SXI IASW thermal PI controller --- Ccs/thermal_control.py | 113 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 Ccs/thermal_control.py diff --git a/Ccs/thermal_control.py b/Ccs/thermal_control.py new file mode 100644 index 0000000..0a2814e --- /dev/null +++ b/Ccs/thermal_control.py @@ -0,0 +1,113 @@ +""" +SMILE PI thermal control algorithm, Python implementation +""" + +import numpy as np +import time +import threading + + +def vctrl_ana_to_dig(v): + return int(v * 1024 / 3.3) + + +class ThermalController: + + ASW_PERIOD_MS = 125. + + VCTRLLOWERVOLT = 0.2 + VCTRLUPPERVOLT = 2.9 + MAXDELTAVOLTAGE = 0.25 + + def __init__(self, temp_ref, cp, ci, offset, exec_per, model=None): + + self.tempRef = temp_ref + self.coeffP = cp + self.coeffI = ci + self.offset = offset + + self.temp = 0 + self.voltCtrl = 0 + self.voltCtrlUint16 = vctrl_ana_to_dig(self.voltCtrl) + + self.tempOld = temp_ref + self.integOld = 0 + self.voltCtrlOld = 0 + + self.hctrl_par_exec_per = exec_per + self._algo_active = False + + self.model = model + self.log = [] + + self._thread = None + + def set_temp_ref(self, temp): + self.tempRef = temp + + def calculate_vctrl(self, temp): + + deltaTime = self.hctrl_par_exec_per * self.ASW_PERIOD_MS / 1000 + integ = self.integOld + deltaTime * (self.tempRef - self.tempOld) + voltCtrlRel = self.offset + self.coeffP * (self.tempRef - temp) + self.coeffI * integ + + if voltCtrlRel < 0: + self.voltCtrl = self.VCTRLLOWERVOLT + elif voltCtrlRel > 100: + self.voltCtrl = self.VCTRLUPPERVOLT + else: + self.voltCtrl = self.VCTRLLOWERVOLT + (voltCtrlRel / 100) * (self.VCTRLUPPERVOLT - self.VCTRLLOWERVOLT) + + if (self.voltCtrl - self.voltCtrlOld) > self.MAXDELTAVOLTAGE: + self.voltCtrl = self.voltCtrlOld + self.MAXDELTAVOLTAGE + elif (self.voltCtrlOld - self.voltCtrl) > self.MAXDELTAVOLTAGE: + self.voltCtrl = self.voltCtrlOld - self.MAXDELTAVOLTAGE + + self.tempOld = temp + self.integOld = integ + self.voltCtrlOld = self.voltCtrl + + self.voltCtrlUint16 = vctrl_ana_to_dig(self.voltCtrl) + + def start_algo(self): + + if self._thread is not None and self._thread.is_alive(): + print('TTC algo already running') + return + + self._thread = threading.Thread(target=self._algo_worker, name='TTCALGO') + # self._thread.daemon = True + self._algo_active = True + self._thread.start() + + def _algo_worker(self): + print('TTC algo started (period = {:.1f}s)'.format(self.ASW_PERIOD_MS / 1000 * self.hctrl_par_exec_per)) + while self._algo_active: + try: + t1 = time.time() + + if self.model is not None: + self.temp = self.model.T_noisy + self.calculate_vctrl(self.temp) + self.model.set_heater_power(vctrl=self.voltCtrl) + + self.log.append((t1, self.tempRef, self.temp, self.voltCtrl, self.coeffI*self.integOld, self.coeffP * (self.tempRef - self.temp))) + + else: + self.calculate_vctrl(self.temp) + + dt = (self.ASW_PERIOD_MS / 1000 * self.hctrl_par_exec_per) - (time.time() - t1) + if dt > 0: + time.sleep(dt) + + except Exception as err: + print(err) + self.stop_algo() + + print('TTC algo stopped') + + def stop_algo(self): + self._algo_active = False + + def save_log(self, fname): + np.savetxt(fname, np.array(self.log), header='time\tT_ref\tT\tV_ctrl\tcI*integ\tcP*(T_ref-T)') -- GitLab