From 957104a270769e80e27c0ccd7c6cc5edf53ffc3a Mon Sep 17 00:00:00 2001 From: su530201 <olivier.kaufmann@umons.ac.be> Date: Wed, 3 May 2023 22:40:27 +0200 Subject: [PATCH] Adds a Pwr component and refactors inject --- config_mb_2023_mux_2024.py | 7 +- dev/test_dps.py | 2 +- .../abstract_hardware_components.py | 131 +++++++++--------- hardware_components/ohmpi_card_3_15.py | 54 +++----- hardware_components/pwr_batt.py | 36 +++++ .../{raspberry_pi.py => raspberry_pi_i2c.py} | 0 hardware_system.py | 16 ++- test_mux_2024.py | 2 +- test_ohmpi_card_3_15.py | 3 +- 9 files changed, 137 insertions(+), 114 deletions(-) create mode 100644 hardware_components/pwr_batt.py rename hardware_components/{raspberry_pi.py => raspberry_pi_i2c.py} (100%) diff --git a/config_mb_2023_mux_2024.py b/config_mb_2023_mux_2024.py index cae61ec6..688d2f3c 100644 --- a/config_mb_2023_mux_2024.py +++ b/config_mb_2023_mux_2024.py @@ -18,12 +18,13 @@ OHMPI_CONFIG = { } HARDWARE_CONFIG = { - 'controller': {'model' : 'raspberry_pi' + 'controller': {'model' : 'raspberry_pi_i2c' }, + 'pwr': {'model' : 'pwr_batt', 'voltage': 12.}, 'tx' : {'model' : 'ohmpi_card_3_15', 'mcp_board_address': 0x20, - 'voltage_max': 12., # Maximum voltage [V] - 'current_max': 4800 / 50 / 2, # Maximum current [mA] + 'voltage_max': 12., # Maximum voltage supported by the TX board [V] + 'current_max': 4800 / 50 / 2, # Maximum current supported by the TX board [mA] 'r_shunt': 2 # Shunt resistance in Ohms }, 'rx' : {'model': 'ohmpi_card_3_15', diff --git a/dev/test_dps.py b/dev/test_dps.py index d52462fb..cb637258 100644 --- a/dev/test_dps.py +++ b/dev/test_dps.py @@ -19,4 +19,4 @@ DPS.serial.parity = 'N' # No parity DPS.mode = minimalmodbus.MODE_RTU # RTU mode DPS.write_register(0x0001, 40, 0) # (last number) 0 is for mA, 3 is for A -DPS.write_register(0x0000, 5, 0) self.DPS.write_register(0x0000, tx_volt, 2) \ No newline at end of file +DPS.write_register(0x0000, 5, 0) self.pwr.write_register(0x0000, tx_volt, 2) \ No newline at end of file diff --git a/hardware_components/abstract_hardware_components.py b/hardware_components/abstract_hardware_components.py index c1889ea3..4cfdb993 100644 --- a/hardware_components/abstract_hardware_components.py +++ b/hardware_components/abstract_hardware_components.py @@ -35,6 +35,58 @@ class ControllerAbstract(ABC): def _cpu_temp(self): pass +class PwrAbstract(ABC): + def __init__(self, **kwargs): + self.board_name = kwargs.pop('board_name', 'unknown Pwr hardware') + self.exec_logger = kwargs.pop('exec_logger', None) + if self.exec_logger is None: + self.exec_logger = create_stdout_logger('exec_mux') + self.soh_logger = kwargs.pop('soh_logger', None) + if self.soh_logger is None: + self.soh_logger = create_stdout_logger('soh_mux') + self.voltage_adjustable = kwargs.pop('voltage_adjustable', False) + self._voltage = np.nan + self._current_adjustable = kwargs.pop('current_adjustable', False) + self._current = np.nan + self._state = 'off' + + @property + @abstractmethod + def current(self): + # add actions to read the DPS current + return self._current + + @current.setter + @abstractmethod + def current(self, value, **kwargs): + # add actions to set the DPS current + pass + @abstractmethod + def turn_off(self): + self.exec_logger.debug(f'Switching {self.board_name} off') + self._state = 'off' + + @abstractmethod + def turn_on(self): + self.exec_logger.debug(f'Switching {self.board_name} on') + self._state = 'on' + + @property + @abstractmethod + def voltage(self): + # add actions to read the DPS voltage + return self._voltage + + @voltage.setter + @abstractmethod + def voltage(self, value): + assert isinstance(value, float) + if not self.voltage_adjustable: + self.exec_logger.warning(f'Voltage cannot be set on {self.board_name}...') + else: + # add actions to set the DPS voltage + self._voltage = value + class MuxAbstract(ABC): def __init__(self, **kwargs): self.board_name = kwargs.pop('board_name', 'unknown MUX hardware') @@ -159,10 +211,6 @@ class MuxAbstract(ABC): class TxAbstract(ABC): def __init__(self, **kwargs): self.board_name = kwargs.pop('board_name', 'unknown TX hardware') - polarity = kwargs.pop('polarity', 1) - if polarity is None: - polarity = 0 - self._polarity = polarity inj_time = kwargs.pop('inj_time', 1.) self.exec_logger = kwargs.pop('exec_logger', None) if self.exec_logger is None: @@ -171,13 +219,10 @@ class TxAbstract(ABC): if self.soh_logger is None: self.soh_logger = create_stdout_logger('soh_tx') self.controller = kwargs.pop('controller', None) + self.pwr = kwargs.pop('pwr', None) self._inj_time = None - self._dps_state = 'off' self._adc_gain = 1. self.inj_time = inj_time - self._voltage = 0. - self.voltage_adjustable = True - self._current_adjustable = False self.exec_logger.debug(f'{self.board_name} TX initialization') @property @@ -193,25 +238,22 @@ class TxAbstract(ABC): def adc_gain_auto(self): pass - @property - @abstractmethod - def current(self): - # add actions to read the TX current and return it - return None - - @current.setter - @abstractmethod - def current(self, value, **kwargs): - # add actions to set the DPS current - pass - @abstractmethod def current_pulse(self, **kwargs): pass @abstractmethod - def inject(self, state='on'): - assert state in ['on', 'off'] + def inject(self, polarity=1, inj_time=None): + assert polarity in [-1,0,1] + if inj_time is None: + inj_time = self._inj_time + if np.abs(polarity) > 0: + self.pwr.turn_on() + time.sleep(inj_time) + self.pwr.turn_off() + else: + self.pwr.turn_off() + time.sleep(inj_time) @property def inj_time(self): @@ -222,44 +264,13 @@ class TxAbstract(ABC): assert isinstance(value, float) self._inj_time = value - @property - def polarity(self): - return self._polarity - - @polarity.setter - def polarity(self, value): - assert value in [-1,0,1] - self._polarity = value - # add actions to set the polarity (switch relays) - - def turn_off(self): - self.exec_logger.debug(f'Switching DPS off') - self._dps_state = 'off' - - def turn_on(self): - self.exec_logger.debug(f'Switching DPS on') - self._dps_state = 'on' - - @property - def voltage(self): - return self._voltage - - @voltage.setter - def voltage(self, value): - assert isinstance(value, float) - if not self.voltage_adjustable: - self.exec_logger.warning(f'Voltage cannot be set on {self.board_name}...') - else: - self._voltage = value - # Add specifics to set DPS voltage - @property @abstractmethod def tx_bat(self): pass - def voltage_pulse(self, voltage=0., length=None, polarity=None): + def voltage_pulse(self, voltage=0., length=None, polarity=1): """ Generates a square voltage pulse Parameters @@ -273,15 +284,9 @@ class TxAbstract(ABC): """ if length is None: length = self.inj_time - if polarity is None: - polarity = self.polarity - self.polarity = polarity - self.voltage = voltage - self.exec_logger.debug(f'Voltage pulse of {polarity * voltage:.3f} V for {length:.3f} s') - self.inject(state='on') - time.sleep(length) - # self.tx_sync.clear() - self.inject(state='off') + self.pwr.voltage = voltage + self.exec_logger.debug(f'Voltage pulse of {polarity * self.pwr.voltage:.3f} V for {length:.3f} s') + self.inject(polarity=polarity, inj_time=length) class RxAbstract(ABC): diff --git a/hardware_components/ohmpi_card_3_15.py b/hardware_components/ohmpi_card_3_15.py index b4902677..eda9a80d 100644 --- a/hardware_components/ohmpi_card_3_15.py +++ b/hardware_components/ohmpi_card_3_15.py @@ -101,7 +101,7 @@ class Tx(TxAbstract): # DPH 5005 Digital Power Supply self.turn_on() time.sleep(TX_CONFIG['dps_switch_on_warm_up']) - self.DPS = None + self.pwr = None # I2C connexion to MCP23008, for current injection self.pin4 = self.mcp_board.get_pin(4) # Ohmpi_run @@ -145,33 +145,28 @@ class Tx(TxAbstract): assert TX_CONFIG['current_min'] <= value <= TX_CONFIG['current_max'] self.exec_logger.warning(f'Current pulse is not implemented for the {TX_CONFIG["model"]} board') - def inject(self, state='on'): - TxAbstract.inject(self, state=state) - # Add specifics here... - - @property - def polarity(self): - return TxAbstract.polarity.fget(self) - - @polarity.setter - def polarity(self, value): - TxAbstract.polarity.fset(self, value) - if value==1: + def inject(self, polarity=1, inj_time=None): + assert polarity in [-1,0,1] + if polarity==1: self.pin0.value = True self.pin1.value = False - elif value==-1: + time.sleep(0.005) # Max turn on time of 211EH relays = 5ms + elif polarity==-1: self.pin0.value = False self.pin1.value = True + time.sleep(0.005) # Max turn on time of 211EH relays = 5ms else: self.pin0.value = False self.pin1.value = False - #time.sleep(0.001) # TODO: check max switching time of relays + time.sleep(0.001) # Max turn off time of 211EH relays = 1ms + TxAbstract.inject(self, polarity=polarity, inj_time=None) + def turn_off(self): - pass + self.pwr.turn_off(self) def turn_on(self): - pass + self.pwr.turn_on(self) @property def tx_bat(self): @@ -179,7 +174,7 @@ class Tx(TxAbstract): self.exec_logger.debug(f'{self.board_name} cannot read battery voltage. Returning default battery voltage.') return TX_CONFIG['low_battery'] - def voltage_pulse(self, voltage=TX_CONFIG['default_voltage'], length=None, polarity=None): + def voltage_pulse(self, voltage=TX_CONFIG['default_voltage'], length=None, polarity=1): """ Generates a square voltage pulse Parameters @@ -194,28 +189,11 @@ class Tx(TxAbstract): if length is None: length = self.inj_time - if polarity is None: - polarity = self.polarity - self.polarity = polarity - self.voltage = voltage - self.exec_logger.debug(f'Voltage pulse of {polarity*voltage:.3f} V for {length:.3f} s') - self.inject(state='on') - time.sleep(length) - self.inject(state='off') + self.pwr.voltage = voltage + self.exec_logger.debug(f'Voltage pulse of {polarity*self.pwr.voltage:.3f} V for {length:.3f} s') + self.inject(polarity=polarity, inj_time=length) - @property - def voltage(self): - return self._voltage - @voltage.setter - def voltage(self, value): - assert isinstance(value, float) - value = np.max([TX_CONFIG['voltage_min'], np.min([value, TX_CONFIG['voltage_max']])]) - if not self.voltage_adjustable: - self.exec_logger.warning(f'Voltage cannot be set on {self.board_name}...') - else: - self._voltage = value - class Rx(RxAbstract): def __init__(self, **kwargs): kwargs.update({'board_name': os.path.basename(__file__).rstrip('.py')}) diff --git a/hardware_components/pwr_batt.py b/hardware_components/pwr_batt.py new file mode 100644 index 00000000..034f48b1 --- /dev/null +++ b/hardware_components/pwr_batt.py @@ -0,0 +1,36 @@ +from OhmPi.hardware_components.abstract_hardware_components import PwrAbstract +import numpy as np +import os + +class Pwr(PwrAbstract): + def __init__(self, **kwargs): + kwargs.update({'board_name': os.path.basename(__file__).rstrip('.py')}) + voltage = kwargs.pop('voltage', 12.) + super().__init__(**kwargs) + self.voltage_adjustable = False + self._voltage = voltage + self._current_adjustable = False + self._current = np.nan + + + @property + def current(self): + return self._current + + @current.setter + def current(self, value, **kwargs): + self.exec_logger.debug(f'Current cannot be set on {self.board_name}') + + def turn_off(self): + self.exec_logger.debug(f'{self.board_name} cannot be turned off') + + def turn_on(self): + self.exec_logger.debug(f'{self.board_name} is always on') + + @property + def voltage(self): + return PwrAbstract.voltage.fget(self) + + @voltage.setter + def voltage(self, value): + PwrAbstract.voltage.fset(self, value) \ No newline at end of file diff --git a/hardware_components/raspberry_pi.py b/hardware_components/raspberry_pi_i2c.py similarity index 100% rename from hardware_components/raspberry_pi.py rename to hardware_components/raspberry_pi_i2c.py diff --git a/hardware_system.py b/hardware_system.py index 3d091c9e..c850ed80 100644 --- a/hardware_system.py +++ b/hardware_system.py @@ -12,6 +12,7 @@ from OhmPi.config import HARDWARE_CONFIG from threading import Thread, Event, Barrier controller_module = importlib.import_module(f'OhmPi.hardware_components.{HARDWARE_CONFIG["controller"]["model"]}') +pwr_module = importlib.import_module(f'OhmPi.hardware_components.{HARDWARE_CONFIG["pwr"]["model"]}') tx_module = importlib.import_module(f'OhmPi.hardware_components.{HARDWARE_CONFIG["tx"]["model"]}') rx_module = importlib.import_module(f'OhmPi.hardware_components.{HARDWARE_CONFIG["rx"]["model"]}') MUX_CONFIG = {} @@ -55,10 +56,15 @@ class OhmPiHardware: data_logger=self.data_logger, soh_logger=self.soh_logger, controller=self.controller)) + self.pwr = kwargs.pop('pwr', pwr_module.Pwr(exec_logger=self.exec_logger, + data_logger=self.data_logger, + soh_logger=self.soh_logger, + controller=self.controller)) self.tx = kwargs.pop('tx', tx_module.Tx(exec_logger=self.exec_logger, data_logger=self.data_logger, soh_logger=self.soh_logger, controller=self.controller)) + self.tx.pwr = self.pwr self._cabling = kwargs.pop('cabling', {}) self.mux_boards = kwargs.pop('mux', {'mux_1': mux_module.Mux(id='mux_1', exec_logger=self.exec_logger, @@ -127,12 +133,12 @@ class OhmPiHardware: return 0. else: n_pulses = int(np.max(self.readings[:, 1])) - polarity = np.array([np.mean(self.readings[self.readings[:, 1] == i, 2]) for i in range(n_pulses + 1)]) + polarity = np.array([np.median(self.readings[self.readings[:, 1]==i, 2]) for i in range(n_pulses + 1)]) mean_vmn = [] mean_iab = [] for i in range(n_pulses + 1): - mean_vmn.append(np.mean(self.readings[self.readings[:, 1] == i, 4])) - mean_iab.append(np.mean(self.readings[self.readings[:, 1] == i, 3])) + mean_vmn.append(np.mean(self.readings[self.readings[:, 1]==i, 4])) + mean_iab.append(np.mean(self.readings[self.readings[:, 1]==i, 3])) mean_vmn = np.array(mean_vmn) mean_iab = np.array(mean_iab) sp = np.mean(mean_vmn[np.ix_(polarity==1)] - mean_vmn[np.ix_(polarity==-1)]) / 2 @@ -182,13 +188,12 @@ class OhmPiHardware: vab_max = np.abs(vab_max) vmn_min = np.abs(vmn_min) vab = np.min([np.abs(tx_volt), vab_max]) - self.tx.polarity = 1 self.tx.turn_on() if self.rx.sampling_rate*1000 > best_tx_injtime: sampling_rate = best_tx_injtime # TODO: check this... else: sampling_rate = self.tx.sampling_rate - self._vab_pulse(vab=vab, length=best_tx_injtime, sampling_rate=sampling_rate) + self._vab_pulse(vab=vab, length=best_tx_injtime, sampling_rate=sampling_rate) # TODO: use a square wave pulse? vmn = np.mean(self.readings[:,4]) iab = np.mean(self.readings[:,3]) # if np.abs(vmn) is too small (smaller than voltage_min), strategy is not constant and vab < vab_max , @@ -206,7 +211,6 @@ class OhmPiHardware: else: self.tx.exec_logger.debug(f'Constant strategy for setting VAB, using {vab} V') self.tx.turn_off() - self.tx.polarity = 0 rab = (np.abs(vab) * 1000.) / iab self.exec_logger.debug(f'RAB = {rab:.2f} Ohms') if vmn < 0: diff --git a/test_mux_2024.py b/test_mux_2024.py index 32c217fb..4d14cdb0 100644 --- a/test_mux_2024.py +++ b/test_mux_2024.py @@ -3,7 +3,7 @@ from utils import change_config import logging change_config('config_mb_2023_mux_2024.py', verbose=False) from OhmPi.hardware_components.mux_2024_rev_0_0 import Mux, MUX_CONFIG -from OhmPi.hardware_components import raspberry_pi as controller_module +from OhmPi.hardware_components import raspberry_pi_i2c as controller_module stand_alone_mux = False part_of_hardware_system = False diff --git a/test_ohmpi_card_3_15.py b/test_ohmpi_card_3_15.py index 9ca144e7..49f91f36 100644 --- a/test_ohmpi_card_3_15.py +++ b/test_ohmpi_card_3_15.py @@ -16,8 +16,7 @@ rx = Rx(exec_logger= exec_logger, soh_logger= soh_logger) print(f'TX current: {tx.current:.3f} mA') print(f'RX voltage: {rx.voltage:.3f} mV') -tx.polarity = 1 -tx.inject(state='on') +tx.inject(state='on', polarity=1) tx.adc_gain_auto() rx.adc_gain_auto() r = [] -- GitLab