hitl_tester.test_cases.bms.test_2590_milprf_full

This file is a work in progress!

See: MIL-PRF-32383 (2590)

The following tests must pass to meet the BB-2590 requirements

3.5.3 Capacity tests

Test | Requirement | Test Method | Discharge Rate (A) | Minimum Capacity (Ah) Capacity | 3.5.3 | 4.7.2.3 | 2 | 9.8 Cycle life (224th cycle) | 3.5.4 | 4.7.2.4 | 2 | 9.3 High rate | 3.5.3 | 4.7.2.5 | 10 | 9.8 Low temperature | 3.5.3 | 4.7.2.7 | 2 | 8.9 High temperature | 3.5.3 | 4.7.2.8 | 2 | 9.8 Inrush current | 3.5.5 | 4.7.2.12 | See 3.5.5 | 9.7 Charge acceptance | 3.5.10.1 | 4.7.2.9 | 2 | 5.1 Retention of charge | 3.5.10.2 | 4.7.2.10 | 2 | 9.7 Pulse | 3.5.3 | 4.7.2.11 | See 4.7.2.11 | 19.5 Extreme low temperature | 3.6.1 | 4.7.3.2 | 2 | 8.6 Extreme high temperature | 3.6.1 | 4.7.3.3 | 2 | 9.8 Temperature shock (post- test) | 3.6.3 | 4.7.3.5 | 2 | 9.8 Mechanical shock (post-test) | 3.6.4 | 4.7.3.6 | 2 | 9.8 Vibration (during test) | 3.6.5 | 4.7.3.7 | 0.8 | 9.7 Battery storage life | 3.6.8 | 4.7.3.10 | 2 | 9.3 Overcharge/electric leakage (post-test) | 3.7.2.2 | 4.7.4.7 | 2 | 9.8 Short circuit protection (post-test) | 3.7.2.3 | 4.7.4.8 | 2 | 9.7

High temperature temporary cut off | 3.7.2.4 | 4.7.4.9 | 2 | 9.7

3.5.6.2 Charge enable test 3.7.2.5 High temperature permanent cut off 4.6.1 Constant potential, current limited. 4.6.3 Alternate charging. 4.7.4.4 Nail penetration, cell.

  1"""
  2This file is a work in progress!
  3
  4See: MIL-PRF-32383 (2590)
  5
  6The following tests must pass to meet the BB-2590 requirements
  7
  83.5.3 Capacity tests
  9---------------------------------------------------------------------------------------------------------------------
 10Test                                    | Requirement | Test Method | Discharge Rate (A) | Minimum Capacity (Ah)
 11Capacity                                | 3.5.3       | 4.7.2.3     | 2                  | 9.8
 12Cycle life (224th cycle)                | 3.5.4       | 4.7.2.4     | 2                  | 9.3
 13High rate                               | 3.5.3       | 4.7.2.5     | 10                 | 9.8
 14Low temperature                         | 3.5.3       | 4.7.2.7     | 2                  | 8.9
 15High temperature                        | 3.5.3       | 4.7.2.8     | 2                  | 9.8
 16Inrush current                          | 3.5.5       | 4.7.2.12    | See 3.5.5          | 9.7
 17Charge acceptance                       | 3.5.10.1    | 4.7.2.9     | 2                  | 5.1
 18Retention of charge                     | 3.5.10.2    | 4.7.2.10    | 2                  | 9.7
 19Pulse                                   | 3.5.3       | 4.7.2.11    | See 4.7.2.11       | 19.5
 20Extreme low temperature                 | 3.6.1       | 4.7.3.2     | 2                  | 8.6
 21Extreme high temperature                | 3.6.1       | 4.7.3.3     | 2                  | 9.8
 22Temperature shock (post- test)          | 3.6.3       | 4.7.3.5     | 2                  | 9.8
 23Mechanical shock (post-test)            | 3.6.4       | 4.7.3.6     | 2                  | 9.8
 24Vibration (during test)                 | 3.6.5       | 4.7.3.7     | 0.8                | 9.7
 25Battery storage life                    | 3.6.8       | 4.7.3.10    | 2                  | 9.3
 26Overcharge/electric leakage (post-test) | 3.7.2.2     | 4.7.4.7     | 2                  | 9.8
 27Short circuit protection (post-test)    | 3.7.2.3     | 4.7.4.8     | 2                  | 9.7
 28High temperature temporary cut off      | 3.7.2.4     | 4.7.4.9     | 2                  | 9.7
 29---------------------------------------------------------------------------------------------------------------------
 30
 313.5.6.2 Charge enable test
 323.7.2.5 High temperature permanent cut off
 334.6.1 Constant potential, current limited.
 344.6.3 Alternate charging.
 354.7.4.4 Nail penetration, cell.
 36"""
 37
 38from __future__ import annotations
 39
 40from time import sleep
 41
 42import pytest
 43
 44from hitl_tester.modules.bms.bms_hw import BMSHardware, UserInterrupt
 45from hitl_tester.modules.bms.thermal_chamber import ThermalChamber
 46from hitl_tester.modules.bms_types import DischargeType
 47from hitl_tester.modules.logger import logger
 48
 49bms_hardware = BMSHardware(pytest.flags)  # type: ignore[arg-type]
 50bms_hardware.init()
 51
 52
 53# Capacity tests
 54
 55MAX_THERMAL_CHAMBER_TIME = 30 * 60
 56CHARGE_CURRENT_A = 3.0
 57
 58
 59def standard_charge():
 60    """
 61    Charge batteries in accordance with 4.3.1 for not greater than three hours.
 62    4.3.1 = 23 ± 5°C (73.4°F) ambient pressure/relative humidity, with 2+ hours between charge and discharge.
 63    """
 64    bms_hardware.voltage = 33.6
 65    bms_hardware.ov_protection = 34
 66    bms_hardware.current = CHARGE_CURRENT_A
 67    bms_hardware.termination_current = 0.100
 68    bms_hardware.max_time = 3 * 3600  # not greater than three hours
 69    bms_hardware.sample_interval = 10
 70    bms_hardware.minimum_readings = 3
 71
 72    # Run the Charge cycle
 73    bms_hardware.run_li_charge_cycle()
 74
 75
 76def standard_rest(seconds: int = 2 * 3600):
 77    """Stabilize the batteries for 2+ hours."""
 78    bms_hardware.max_time = seconds
 79    bms_hardware.sample_interval = 60
 80    bms_hardware.run_resting_cycle()
 81
 82
 83def standard_charge_rest():
 84    """Charge batteries in accordance with 4.3.1 for not greater than three hours with 2+ hours rest."""
 85    standard_charge()
 86    standard_rest()
 87
 88
 89def standard_discharge(current: float = 2, voltage_cutoff: int = 20) -> float:
 90    """Discharge at 2A unitl 20V."""
 91    bms_hardware.max_time = 8 * 3600  # 8 hours
 92    bms_hardware.sample_interval = 10
 93    bms_hardware.discharge_type = DischargeType.CONSTANT_CURRENT
 94    bms_hardware.current = current
 95    bms_hardware.uv_protection = voltage_cutoff - 1
 96    bms_hardware.voltage = voltage_cutoff
 97    return float(bms_hardware.run_discharge_cycle())
 98
 99
100def set_temperature(temp: float | None = None):
101    assert isinstance(bms_hardware.thermal_chamber, ThermalChamber)
102    if temp is None:
103        bms_hardware.thermal_chamber.set_room_temperature()
104    else:
105        bms_hardware.thermal_chamber.internal_units = "C"
106        bms_hardware.thermal_chamber.set_point_temperature = -20.0
107    bms_hardware.thermal_chamber.block_until_set_point_reached(MAX_THERMAL_CHAMBER_TIME)
108
109
110def test_capacity_discharge():
111    """Regular discharge at 2A. Permitted up to 3 cycles to pass."""
112    standard_charge_rest()
113    capacity = standard_discharge()
114    assert capacity >= 9.8  # confirm capacity meets minimums
115
116
117def test_cycle():
118    """Cycle the battery 224 times."""
119    capacity = 0.0  # Appease linter
120    for i in range(8):
121        for j in range(26):  # Cycle 1-26: charge, rest 5+ minutes, discharge @ 2A, rest 5+ minutes
122            logger.write_info_to_report(f"Starting cycle {i*28 + j + 1} / 224")
123            standard_charge()
124            standard_rest(5 * 60)  # Stabilize the batteries for 5+ minutes
125            standard_discharge()
126            standard_rest(5 * 60)  # Stabilize the batteries for 5+ minutes
127
128        # Cycle 27: charge, rest 4+ hours, discharge @ 2A, rest 4+ hours
129        logger.write_info_to_report(f"Starting cycle {i*28 + 27} / 224")
130        standard_charge()
131        standard_rest(4 * 3600)  # Stabilize the batteries for 4+ hours
132        standard_discharge()
133        standard_rest(4 * 3600)  # Stabilize the batteries for 4+ hours
134
135        # Cycle 28: charge, electric leakage test, rest 4+ hours, discharge @ 2A, record capacity.
136        logger.write_info_to_report(f"Starting cycle {i*28 + 28} / 224")
137        standard_charge()
138        logger.write_info_to_report("Pausing. Perform Electric leakage test (4.7.4.7d), then press CTRL-Z to continue.")
139        UserInterrupt.interrupt_requested.clear()
140        standard_rest(4 * 3600)  # Stabilize the batteries for 4+ hours
141        capacity = standard_discharge()
142
143    assert capacity >= 9.3  # confirm capacity meets minimums
144
145
146def test_high_rate_discharge():
147    """Discharge at 10A."""
148    standard_charge_rest()
149    capacity = standard_discharge(current=10)
150    assert capacity >= 9.8  # confirm capacity meets minimums
151
152
153def test_low_temperature_discharge():
154    """Discharge at -20C."""
155    standard_charge()
156    set_temperature(-20.0)
157    standard_rest(4 * 3600)  # Stabilize the batteries for 4+ hours
158    capacity = standard_discharge()
159    set_temperature()
160    assert capacity >= 8.9  # confirm capacity meets minimums
161
162
163def test_high_temperature_discharge():
164    """Discharge at 50C."""
165    standard_charge()
166    set_temperature(50.0)
167    standard_rest(4 * 3600)  # Stabilize the batteries for 4+ hours
168    capacity = standard_discharge()
169    set_temperature()
170    assert capacity >= 9.8  # confirm capacity meets minimums
171
172
173# Inrush Current (9.7 Ah)
174#   - discharge at 5A for 5 minutes
175#   - 3.5.5 lists the discharge currents for 1 second
176#   - discharge at 10A for 4:59 minutes
177#   - discharge at 5A for 5 minutes
178#   - 3.5.5 lists the discharge currents for 1 second
179#   - discharge at 10A for 4:59 minutes
180#   - discharge at 5A until cutoff
181#   - discharge as above
182
183
184def thread_inrush_current():
185    delays = (10, 10, 10, 20, 40, 75, 150, 250, 275, 150, 299000)
186    # use nanosleep
187    for i in range(2):
188        bms_hardware.load.amps = 5
189        # nanosleep 5 minutes
190        bms_hardware.load.amps = 60
191        # 0.2 ms
192        bms_hardware.load.amps = 22.5
193        # sleep 9.8 ms
194
195        bms_hardware.load.amps = 20
196        for delay in delays:
197            # sleep delay
198            bms_hardware.load.amps -= 1
199
200
201def test_inrush_current():
202    """"""
203    standard_charge_rest()
204    # FIXME(JA): create thread
205    capacity = standard_discharge(current=5)
206    # FIXME(JA): kill thread
207    assert capacity >= 9.7  # confirm capacity meets minimums
208
209
210def test_charge_acceptance():
211    """"""
212    standard_discharge()
213    for temp in (-20.0, 50.0):
214        set_temperature(temp)
215        standard_rest(4 * 3600)
216        standard_charge_rest()
217        capacity = standard_discharge()
218    assert capacity >= 5.1  # confirm capacity meets minimums
219
220
221def retention_of_charge():
222    """"""
223    standard_charge_rest()
224    set_temperature(50.0)
225    standard_rest(7 * 24 * 3600)
226    set_temperature()
227    standard_rest()
228    capacity = standard_discharge()
229    assert capacity >= 9.7  # confirm capacity meets minimums
230
231
232def thread_pulse_discharge():
233    """Battery sections connected in parallel, 36 A for 5 seconds followed by 25 seconds off."""
234    while True:  # FIXME(JA): use threading event
235        bms_hardware.load.amps = 36
236        sleep(5)
237        bms_hardware.load.disable()
238        sleep(25)
239        bms_hardware.load.enable()
240
241
242def test_pulse_discharge():
243    # - cutoff = 10V (parallel mode)
244    standard_charge_rest()
245    # FIXME(JA): create thread
246    capacity = standard_discharge(current=36, voltage_cutoff=10)
247    # FIXME(JA): kill thread
248    assert capacity >= 19.5  # confirm capacity meets minimums
249
250
251def test_extreme_low_temperature_discharge():
252    """Discharge at -20C."""
253    standard_charge()
254    set_temperature(-30.0)
255    standard_rest(4 * 3600)  # Stabilize the batteries for 4+ hours
256    capacity = standard_discharge()
257    set_temperature()
258    assert capacity >= 8.6  # confirm capacity meets minimums
259
260
261def test_extreme_high_temperature_discharge():
262    """Discharge at 50C."""
263    standard_charge()
264    set_temperature(55.0)
265    standard_rest(4 * 3600)  # Stabilize the batteries for 4+ hours
266    capacity = standard_discharge()
267    set_temperature()
268    assert capacity >= 9.8  # confirm capacity meets minimums
269
270
271"""
272Temperature shock (post-test) (9.8 Ah)
273  - charge + rest (may use 4.6.3)
274  - method 503 Procedure 1-B of MIL-STD-810 (75C+ -59C-) (2+ hours at either end, diurnal cycling not required) 
275  - Capacity discharge test
276
277Mechanical shock (post-test) (9.8 Ah)
278  - Capacity discharge test
279
280Vibration (during test) (9.7 Ah)
281  - charge + rest (may use 4.6.3)
282  - discharge @ 0.8A
283
284Battery storage life (9.3 Ah)
285  a. discharge @ 1A (0.5C, C=2A)
286  b. charge @ 100+ mA until 220- mAh
287  c. store @ 60C +- 5C, 85%+ relative humidity for 622+ hours (~26 days)
288  d. rest at 23 ± 5°C, normal humidity for 2+ hours
289  e. charge + rest (may use 4.6.3)
290  f. electric leak test
291  g. discharge @ 2A
292  - repeat efg for two more cycles (3 charge/discharge)
293
294Overcharge/electric leakage (post-test) (differs from actual) >>>>>>>>>>>>>>>>
295  - Charge in parallel mode at 4 A with a charge voltage limit of 40 V. 
296  - discharge @ 2A = 9.8 Ah
297
298Short circuit protection (post-test) (9.7 Ah)
299  - charge + rest (may use 4.6.3)
300  - measure OCV
301  - short for 1 hour
302  - measure OCV
303  - rest for 2+ hours
304  - charge + rest (may use 4.6.3)
305  - discharge @ 2A
306
307High temperature temporary cut off (9.7 Ah)
308  - charge + rest (may use 4.6.3)
309  a. store @ 75C +- 5C for 2+ hours
310  b. measure voltage
311  c. wait for it to cool to 40C +0/-5C
312  d. measure voltage
313  - repeat a-d five times (total 6)
314  - Capacity discharge test
315"""  # pylint: disable=all
316
317# Other tests
318# 3.5.6.2 Charge enable test
319# 3.7.2.5 High temperature permanent cut off
320# 4.6.1 Constant potential, current limited.
321# 4.6.3 Alternate charging.
322# 4.7.4.4 Nail penetration, cell.
323
324# cell tests pg 21
MAX_THERMAL_CHAMBER_TIME = 1800
CHARGE_CURRENT_A = 3.0
def standard_charge():
60def standard_charge():
61    """
62    Charge batteries in accordance with 4.3.1 for not greater than three hours.
63    4.3.1 = 23 ± 5°C (73.4°F) ambient pressure/relative humidity, with 2+ hours between charge and discharge.
64    """
65    bms_hardware.voltage = 33.6
66    bms_hardware.ov_protection = 34
67    bms_hardware.current = CHARGE_CURRENT_A
68    bms_hardware.termination_current = 0.100
69    bms_hardware.max_time = 3 * 3600  # not greater than three hours
70    bms_hardware.sample_interval = 10
71    bms_hardware.minimum_readings = 3
72
73    # Run the Charge cycle
74    bms_hardware.run_li_charge_cycle()

Charge batteries in accordance with 4.3.1 for not greater than three hours. 4.3.1 = 23 ± 5°C (73.4°F) ambient pressure/relative humidity, with 2+ hours between charge and discharge.

def standard_rest(seconds: int = 7200):
77def standard_rest(seconds: int = 2 * 3600):
78    """Stabilize the batteries for 2+ hours."""
79    bms_hardware.max_time = seconds
80    bms_hardware.sample_interval = 60
81    bms_hardware.run_resting_cycle()

Stabilize the batteries for 2+ hours.

def standard_charge_rest():
84def standard_charge_rest():
85    """Charge batteries in accordance with 4.3.1 for not greater than three hours with 2+ hours rest."""
86    standard_charge()
87    standard_rest()

Charge batteries in accordance with 4.3.1 for not greater than three hours with 2+ hours rest.

def standard_discharge(current: float = 2, voltage_cutoff: int = 20) -> float:
90def standard_discharge(current: float = 2, voltage_cutoff: int = 20) -> float:
91    """Discharge at 2A unitl 20V."""
92    bms_hardware.max_time = 8 * 3600  # 8 hours
93    bms_hardware.sample_interval = 10
94    bms_hardware.discharge_type = DischargeType.CONSTANT_CURRENT
95    bms_hardware.current = current
96    bms_hardware.uv_protection = voltage_cutoff - 1
97    bms_hardware.voltage = voltage_cutoff
98    return float(bms_hardware.run_discharge_cycle())

Discharge at 2A unitl 20V.

def set_temperature(temp: float | None = None):
101def set_temperature(temp: float | None = None):
102    assert isinstance(bms_hardware.thermal_chamber, ThermalChamber)
103    if temp is None:
104        bms_hardware.thermal_chamber.set_room_temperature()
105    else:
106        bms_hardware.thermal_chamber.internal_units = "C"
107        bms_hardware.thermal_chamber.set_point_temperature = -20.0
108    bms_hardware.thermal_chamber.block_until_set_point_reached(MAX_THERMAL_CHAMBER_TIME)
def test_capacity_discharge():
111def test_capacity_discharge():
112    """Regular discharge at 2A. Permitted up to 3 cycles to pass."""
113    standard_charge_rest()
114    capacity = standard_discharge()
115    assert capacity >= 9.8  # confirm capacity meets minimums

Regular discharge at 2A. Permitted up to 3 cycles to pass.

def test_cycle():
118def test_cycle():
119    """Cycle the battery 224 times."""
120    capacity = 0.0  # Appease linter
121    for i in range(8):
122        for j in range(26):  # Cycle 1-26: charge, rest 5+ minutes, discharge @ 2A, rest 5+ minutes
123            logger.write_info_to_report(f"Starting cycle {i*28 + j + 1} / 224")
124            standard_charge()
125            standard_rest(5 * 60)  # Stabilize the batteries for 5+ minutes
126            standard_discharge()
127            standard_rest(5 * 60)  # Stabilize the batteries for 5+ minutes
128
129        # Cycle 27: charge, rest 4+ hours, discharge @ 2A, rest 4+ hours
130        logger.write_info_to_report(f"Starting cycle {i*28 + 27} / 224")
131        standard_charge()
132        standard_rest(4 * 3600)  # Stabilize the batteries for 4+ hours
133        standard_discharge()
134        standard_rest(4 * 3600)  # Stabilize the batteries for 4+ hours
135
136        # Cycle 28: charge, electric leakage test, rest 4+ hours, discharge @ 2A, record capacity.
137        logger.write_info_to_report(f"Starting cycle {i*28 + 28} / 224")
138        standard_charge()
139        logger.write_info_to_report("Pausing. Perform Electric leakage test (4.7.4.7d), then press CTRL-Z to continue.")
140        UserInterrupt.interrupt_requested.clear()
141        standard_rest(4 * 3600)  # Stabilize the batteries for 4+ hours
142        capacity = standard_discharge()
143
144    assert capacity >= 9.3  # confirm capacity meets minimums

Cycle the battery 224 times.

def test_high_rate_discharge():
147def test_high_rate_discharge():
148    """Discharge at 10A."""
149    standard_charge_rest()
150    capacity = standard_discharge(current=10)
151    assert capacity >= 9.8  # confirm capacity meets minimums

Discharge at 10A.

def test_low_temperature_discharge():
154def test_low_temperature_discharge():
155    """Discharge at -20C."""
156    standard_charge()
157    set_temperature(-20.0)
158    standard_rest(4 * 3600)  # Stabilize the batteries for 4+ hours
159    capacity = standard_discharge()
160    set_temperature()
161    assert capacity >= 8.9  # confirm capacity meets minimums

Discharge at -20C.

def test_high_temperature_discharge():
164def test_high_temperature_discharge():
165    """Discharge at 50C."""
166    standard_charge()
167    set_temperature(50.0)
168    standard_rest(4 * 3600)  # Stabilize the batteries for 4+ hours
169    capacity = standard_discharge()
170    set_temperature()
171    assert capacity >= 9.8  # confirm capacity meets minimums

Discharge at 50C.

def thread_inrush_current():
185def thread_inrush_current():
186    delays = (10, 10, 10, 20, 40, 75, 150, 250, 275, 150, 299000)
187    # use nanosleep
188    for i in range(2):
189        bms_hardware.load.amps = 5
190        # nanosleep 5 minutes
191        bms_hardware.load.amps = 60
192        # 0.2 ms
193        bms_hardware.load.amps = 22.5
194        # sleep 9.8 ms
195
196        bms_hardware.load.amps = 20
197        for delay in delays:
198            # sleep delay
199            bms_hardware.load.amps -= 1
def test_inrush_current():
202def test_inrush_current():
203    """"""
204    standard_charge_rest()
205    # FIXME(JA): create thread
206    capacity = standard_discharge(current=5)
207    # FIXME(JA): kill thread
208    assert capacity >= 9.7  # confirm capacity meets minimums
def test_charge_acceptance():
211def test_charge_acceptance():
212    """"""
213    standard_discharge()
214    for temp in (-20.0, 50.0):
215        set_temperature(temp)
216        standard_rest(4 * 3600)
217        standard_charge_rest()
218        capacity = standard_discharge()
219    assert capacity >= 5.1  # confirm capacity meets minimums
def retention_of_charge():
222def retention_of_charge():
223    """"""
224    standard_charge_rest()
225    set_temperature(50.0)
226    standard_rest(7 * 24 * 3600)
227    set_temperature()
228    standard_rest()
229    capacity = standard_discharge()
230    assert capacity >= 9.7  # confirm capacity meets minimums
def thread_pulse_discharge():
233def thread_pulse_discharge():
234    """Battery sections connected in parallel, 36 A for 5 seconds followed by 25 seconds off."""
235    while True:  # FIXME(JA): use threading event
236        bms_hardware.load.amps = 36
237        sleep(5)
238        bms_hardware.load.disable()
239        sleep(25)
240        bms_hardware.load.enable()

Battery sections connected in parallel, 36 A for 5 seconds followed by 25 seconds off.

def test_pulse_discharge():
243def test_pulse_discharge():
244    # - cutoff = 10V (parallel mode)
245    standard_charge_rest()
246    # FIXME(JA): create thread
247    capacity = standard_discharge(current=36, voltage_cutoff=10)
248    # FIXME(JA): kill thread
249    assert capacity >= 19.5  # confirm capacity meets minimums
def test_extreme_low_temperature_discharge():
252def test_extreme_low_temperature_discharge():
253    """Discharge at -20C."""
254    standard_charge()
255    set_temperature(-30.0)
256    standard_rest(4 * 3600)  # Stabilize the batteries for 4+ hours
257    capacity = standard_discharge()
258    set_temperature()
259    assert capacity >= 8.6  # confirm capacity meets minimums

Discharge at -20C.

def test_extreme_high_temperature_discharge():
262def test_extreme_high_temperature_discharge():
263    """Discharge at 50C."""
264    standard_charge()
265    set_temperature(55.0)
266    standard_rest(4 * 3600)  # Stabilize the batteries for 4+ hours
267    capacity = standard_discharge()
268    set_temperature()
269    assert capacity >= 9.8  # confirm capacity meets minimums

Discharge at 50C.