hitl_tester.modules.bms.ngi_sim
Provides controls for the NGI N83624 Cell Simulator.
(c) 2020-2024 TurnAround Factor, Inc.
#
CUI DISTRIBUTION CONTROL
Controlled by: DLA J68 R&D SBIP
CUI Category: Small Business Research and Technology
Distribution/Dissemination Controls: PROTECTED BY SBIR DATA RIGHTS
POC: GOV SBIP Program Manager Denise Price, 571-767-0111
Distribution authorized to U.S. Government Agencies only, to protect information not owned by the
U.S. Government and protected by a contractor’s ‘limited rights’ statement, or received with the understanding that
it is not routinely transmitted outside the U.S. Government (determination made September 14, 2024). Other requests
for this document shall be referred to ACOR DLA Logistics Operations (J-68), 8725 John J. Kingman Rd., Suite 4317,
Fort Belvoir, VA 22060-6221
#
SBIR DATA RIGHTS
Contract No.:SP4701-23-C-0083
Contractor Name: TurnAround Factor, Inc.
Contractor Address: 10365 Wood Park Ct. Suite 313 / Ashland, VA 23005
Expiration of SBIR Data Rights Period: September 24, 2029
The Government's rights to use, modify, reproduce, release, perform, display, or disclose technical data or computer
software marked with this legend are restricted during the period shown as provided in paragraph (b)(4) of the Rights
in Noncommercial Technical Data and Computer Software--Small Business Innovative Research (SBIR) Program clause
contained in the above identified contract. No restrictions apply after the expiration date shown above. Any
reproduction of technical data, computer software, or portions thereof marked with this legend must also reproduce
the markings.
1""" 2Provides controls for the NGI N83624 Cell Simulator. 3 4# (c) 2020-2024 TurnAround Factor, Inc. 5# 6# CUI DISTRIBUTION CONTROL 7# Controlled by: DLA J68 R&D SBIP 8# CUI Category: Small Business Research and Technology 9# Distribution/Dissemination Controls: PROTECTED BY SBIR DATA RIGHTS 10# POC: GOV SBIP Program Manager Denise Price, 571-767-0111 11# Distribution authorized to U.S. Government Agencies only, to protect information not owned by the 12# U.S. Government and protected by a contractor’s ‘limited rights’ statement, or received with the understanding that 13# it is not routinely transmitted outside the U.S. Government (determination made September 14, 2024). Other requests 14# for this document shall be referred to ACOR DLA Logistics Operations (J-68), 8725 John J. Kingman Rd., Suite 4317, 15# Fort Belvoir, VA 22060-6221 16# 17# SBIR DATA RIGHTS 18# Contract No.:SP4701-23-C-0083 19# Contractor Name: TurnAround Factor, Inc. 20# Contractor Address: 10365 Wood Park Ct. Suite 313 / Ashland, VA 23005 21# Expiration of SBIR Data Rights Period: September 24, 2029 22# The Government's rights to use, modify, reproduce, release, perform, display, or disclose technical data or computer 23# software marked with this legend are restricted during the period shown as provided in paragraph (b)(4) of the Rights 24# in Noncommercial Technical Data and Computer Software--Small Business Innovative Research (SBIR) Program clause 25# contained in the above identified contract. No restrictions apply after the expiration date shown above. Any 26# reproduction of technical data, computer software, or portions thereof marked with this legend must also reproduce 27# the markings. 28""" 29 30import time 31 32from hitl_tester.modules.bms.cell import Cell 33from hitl_tester.modules.bms_types import UnderVoltageError, OverVoltageError, CellCompMode, NGIMode 34 35 36class NGICell(Cell): 37 """NGI N83624 Cell Simulator command wrapper.""" 38 39 @property 40 def measured_volts(self) -> float: 41 """Measures actual cell voltage.""" 42 with self._lock: 43 result = float(self._resource.query(f"MEAS{self.id}:VOLT?")) 44 return result if result != 9.91e37 else 0.0 # Convert NaN to 0 45 46 @property 47 def volts(self) -> float: 48 """Gets the last target voltage.""" 49 with self._lock: 50 return self._volts 51 52 @volts.setter 53 def volts(self, new_voltage: float): 54 """Sets the output voltage.""" 55 with self._lock: 56 if not self.disengage_safety_protocols and new_voltage <= self.data.uv_protection: 57 raise UnderVoltageError( 58 f"Undervoltage protection triggered at {time.strftime('%x %X')} on cell {self.id}. " 59 f"Voltage {new_voltage} is lower than {self.data.uv_protection}." 60 ) 61 if not self.disengage_safety_protocols and new_voltage >= self.data.ov_protection: 62 raise OverVoltageError( 63 f"Overvoltage protection triggered at {time.strftime('%x %X')} on cell {self.id}. " 64 f"Voltage {new_voltage} is higher than {self.data.ov_protection}." 65 ) 66 67 self._resource.write(f"CHAR{self.id}:VOLT {new_voltage}") 68 self._volts = new_voltage 69 70 @property 71 def ohms(self) -> float: 72 """Measures internal resistance of the cell""" 73 with self._lock: 74 return self._resistance # MEAS1:R? 75 76 @ohms.setter 77 def ohms(self, new_ohms: float): 78 """Sets the internal resistance of the cell.""" 79 with self._lock: 80 self._resistance = new_ohms 81 self._resource.write(f"CHAR{self.id}:R {new_ohms}") 82 83 @property 84 def amps(self) -> float: 85 """Measures cell current.""" 86 with self._lock: 87 result = float(self._resource.query(f"MEAS{self.id}:CURR?")) 88 return result if result != 9.91e37 else 0.0 # Convert NaN to 0 89 90 @amps.setter 91 def amps(self, new_amps: float): 92 """Sets the current limit of the cell.""" 93 with self._lock: 94 self._resource.write(f"CHAR{self.id}:OUTCURR {new_amps}") 95 96 @property 97 def compensation_mode(self) -> CellCompMode: 98 """Sets compensation mode""" 99 return CellCompMode.LLOCAL 100 101 @compensation_mode.setter 102 def compensation_mode(self, mode: CellCompMode): 103 """Sets compensation mode""" 104 105 def enable(self): 106 """Enables the cell.""" 107 self._resource.write(f"OUTP{self.id}:ONOFF 1") 108 self._thread_pause_flag.set() 109 110 def disable(self): 111 """Disables the cell.""" 112 self._thread_pause_flag.clear() 113 self._resource.write(f"OUTP{self.id}:ONOFF 0") 114 115 def reset(self): 116 """Resets the instrument""" 117 self._resource.write("*RST") 118 self._resource.write( # Set all channels to charge/discharge mode 119 f"OUTP:MODE {NGIMode.CHARGE}(@1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24)" 120 ) 121 time.sleep(6) # Needs delay after reset
37class NGICell(Cell): 38 """NGI N83624 Cell Simulator command wrapper.""" 39 40 @property 41 def measured_volts(self) -> float: 42 """Measures actual cell voltage.""" 43 with self._lock: 44 result = float(self._resource.query(f"MEAS{self.id}:VOLT?")) 45 return result if result != 9.91e37 else 0.0 # Convert NaN to 0 46 47 @property 48 def volts(self) -> float: 49 """Gets the last target voltage.""" 50 with self._lock: 51 return self._volts 52 53 @volts.setter 54 def volts(self, new_voltage: float): 55 """Sets the output voltage.""" 56 with self._lock: 57 if not self.disengage_safety_protocols and new_voltage <= self.data.uv_protection: 58 raise UnderVoltageError( 59 f"Undervoltage protection triggered at {time.strftime('%x %X')} on cell {self.id}. " 60 f"Voltage {new_voltage} is lower than {self.data.uv_protection}." 61 ) 62 if not self.disengage_safety_protocols and new_voltage >= self.data.ov_protection: 63 raise OverVoltageError( 64 f"Overvoltage protection triggered at {time.strftime('%x %X')} on cell {self.id}. " 65 f"Voltage {new_voltage} is higher than {self.data.ov_protection}." 66 ) 67 68 self._resource.write(f"CHAR{self.id}:VOLT {new_voltage}") 69 self._volts = new_voltage 70 71 @property 72 def ohms(self) -> float: 73 """Measures internal resistance of the cell""" 74 with self._lock: 75 return self._resistance # MEAS1:R? 76 77 @ohms.setter 78 def ohms(self, new_ohms: float): 79 """Sets the internal resistance of the cell.""" 80 with self._lock: 81 self._resistance = new_ohms 82 self._resource.write(f"CHAR{self.id}:R {new_ohms}") 83 84 @property 85 def amps(self) -> float: 86 """Measures cell current.""" 87 with self._lock: 88 result = float(self._resource.query(f"MEAS{self.id}:CURR?")) 89 return result if result != 9.91e37 else 0.0 # Convert NaN to 0 90 91 @amps.setter 92 def amps(self, new_amps: float): 93 """Sets the current limit of the cell.""" 94 with self._lock: 95 self._resource.write(f"CHAR{self.id}:OUTCURR {new_amps}") 96 97 @property 98 def compensation_mode(self) -> CellCompMode: 99 """Sets compensation mode""" 100 return CellCompMode.LLOCAL 101 102 @compensation_mode.setter 103 def compensation_mode(self, mode: CellCompMode): 104 """Sets compensation mode""" 105 106 def enable(self): 107 """Enables the cell.""" 108 self._resource.write(f"OUTP{self.id}:ONOFF 1") 109 self._thread_pause_flag.set() 110 111 def disable(self): 112 """Disables the cell.""" 113 self._thread_pause_flag.clear() 114 self._resource.write(f"OUTP{self.id}:ONOFF 0") 115 116 def reset(self): 117 """Resets the instrument""" 118 self._resource.write("*RST") 119 self._resource.write( # Set all channels to charge/discharge mode 120 f"OUTP:MODE {NGIMode.CHARGE}(@1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24)" 121 ) 122 time.sleep(6) # Needs delay after reset
NGI N83624 Cell Simulator command wrapper.
measured_volts: float
40 @property 41 def measured_volts(self) -> float: 42 """Measures actual cell voltage.""" 43 with self._lock: 44 result = float(self._resource.query(f"MEAS{self.id}:VOLT?")) 45 return result if result != 9.91e37 else 0.0 # Convert NaN to 0
Measures actual cell voltage.
volts: float
47 @property 48 def volts(self) -> float: 49 """Gets the last target voltage.""" 50 with self._lock: 51 return self._volts
Gets the last target voltage.
ohms: float
71 @property 72 def ohms(self) -> float: 73 """Measures internal resistance of the cell""" 74 with self._lock: 75 return self._resistance # MEAS1:R?
Measures internal resistance of the cell
amps: float
84 @property 85 def amps(self) -> float: 86 """Measures cell current.""" 87 with self._lock: 88 result = float(self._resource.query(f"MEAS{self.id}:CURR?")) 89 return result if result != 9.91e37 else 0.0 # Convert NaN to 0
Measures cell current.
compensation_mode: hitl_tester.modules.bms_types.CellCompMode
97 @property 98 def compensation_mode(self) -> CellCompMode: 99 """Sets compensation mode""" 100 return CellCompMode.LLOCAL
Sets compensation mode
def
enable(self):
106 def enable(self): 107 """Enables the cell.""" 108 self._resource.write(f"OUTP{self.id}:ONOFF 1") 109 self._thread_pause_flag.set()
Enables the cell.
def
disable(self):
111 def disable(self): 112 """Disables the cell.""" 113 self._thread_pause_flag.clear() 114 self._resource.write(f"OUTP{self.id}:ONOFF 0")
Disables the cell.
def
reset(self):
116 def reset(self): 117 """Resets the instrument""" 118 self._resource.write("*RST") 119 self._resource.write( # Set all channels to charge/discharge mode 120 f"OUTP:MODE {NGIMode.CHARGE}(@1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24)" 121 ) 122 time.sleep(6) # Needs delay after reset
Resets the instrument