def test_dive_legs_travel_gas(self): """ Test dive legs calculation with travel gas """ ean36 = gas(36, 0, depth=0) ean26 = gas(26, 0, depth=30) air = gas(21, 0, depth=40) gas_list = GasList(air) gas_list.travel_gas.append(ean36) gas_list.travel_gas.append(ean26) stops = [ Stop(18, 1), Stop(15, 1), Stop(12, 2), Stop(9, 3), Stop(6, 5), ] profile = DiveProfile(ProfileType.PLANNED, gas_list, 60, 25) legs = dive_legs(profile, stops, 20) self.assertEqual(5 + 10, len(legs)) self.assertEqual((0, 30, 1.5, ean36, False), legs[0]) self.assertEqual((30, 40, 0.5, ean26, False), legs[1]) self.assertEqual((40, 60, 1, air, False), legs[2]) self.assertEqual((60, 60, 22, air, False), legs[3]) self.assertEqual((60, 18, 4.2, air, False), legs[4]) deco_legs = legs[5::2] for s, l in zip(stops, deco_legs): self.assertEqual((s.depth, s.depth, s.time, air, True), l) self.assertEqual((6, 0, 0.6, air, True), legs[-1])
def test_gas_volume(self): """ Test gas volume calculation """ air = gas(21, 0) ean50 = gas(50, 0, depth=22) gas_list = GasList(air) gas_list.deco_gas.append(ean50) legs = [ (0, 40, 2, air, False), # 3b * 2min * 30min/l = 180l (40, 40, 20, air, False), # 5b * 20min * 30min/l = 3000l (40, 22, 5, air, False), # 4.1b * 5min * 30min/l = 615l (22, 9, 2, ean50, False), # 2.55b * 2min * 30min/l = 153l (9, 9, 1, ean50, True), # 1.9b * 1min * 30min/l = 57l (9, 6, 1, ean50, True), # 1.75b * 1min * 30min/l = 52.5l (6, 6, 3, ean50, True), # 1.6b * 3min * 30min/l = 144l (6, 0, 1, ean50, True), # 1.3b * 1min * 30min/l = 39l ] cons = gas_volume(gas_list, legs, 30) self.assertEqual(2, len(cons)) self.assertEqual(3795, cons['Air']) self.assertEqual(445.5, cons['EAN50'])
def test_dive_slate_travel_no_deco(self): """ Test dive slate creation (with travel gas, no deco gas) """ ean32 = gas(32, 0, depth=0) ean30 = gas(30, 0, depth=33) ean27 = gas(27, 0, depth=37) gas_list = GasList(ean27) gas_list.travel_gas.append(ean32) gas_list.travel_gas.append(ean30) depth = 45 time = 35 descent_rate = 20 profile = DiveProfile(ProfileType.PLANNED, gas_list, depth, time) stops = [ Stop(18, 1), Stop(15, 1), Stop(12, 2), Stop(9, 3), Stop(6, 5), ] legs = dive_legs(profile, stops, descent_rate) slate = dive_slate(profile, stops, legs, descent_rate) self.assertEqual((0, None, 0, ean32), slate[0], slate) self.assertEqual((33, None, 2, ean30), slate[1], slate) self.assertEqual((37, None, 2, ean27), slate[2], slate) self.assertEqual((45, None, 35, None), slate[3], slate) self.assertEqual((18, 1, 39, None), slate[4], slate) self.assertEqual((9, 3, 46, None), slate[7], slate)
def test_gas_basic(self): """ Test basic gas data creation """ e = kd.Gas(id='air', name='Air', o2=21, he=0, depth=0) self.assertEquals(e, kd.gas(21, 0)) e = kd.Gas(id='o2', name='O2', o2=100, he=0, depth=6) self.assertEquals(e, kd.gas(100, 0, depth=6))
def test_parse_gas_list(self): """ Test parsing gas list """ air = gas(21, 0) ean50 = gas(50, 0, None) gas_list = parse_gas_list('air@0', 'ean50') self.assertEqual([], gas_list.travel_gas) self.assertEqual(air, gas_list.bottom_gas) self.assertEqual([ean50], gas_list.deco_gas)
def test_gas_trimix(self): """ Test trimix gas data creation """ # 21/35, 18/45 and 15/55 e = kd.Gas(id='tx2135', name='TX 21/35', o2=21, he=35, depth=0) self.assertEquals(e, kd.gas(21, 35)) e = kd.Gas(id='tx1845', name='TX 18/45', o2=18, he=45, depth=0) self.assertEquals(e, kd.gas(18, 45)) e = kd.Gas(id='tx1555', name='TX 15/55', o2=15, he=55, depth=30) self.assertEquals(e, kd.gas(15, 55, depth=30))
def test_bottom_gas_mix_with_travel(self): """ Test bottom gas mix depth update when travel gas mix added """ ean50 = gas(50, 0, None) air = gas(21, 0, None) gas_list = GasList(air) gas_list.travel_gas = [ean50] result = gas_mix_depth_update(gas_list, 1.4, 1.6) self.assertEqual(1, len(result.travel_gas)) self.assertEqual(0, result.travel_gas[0].depth) self.assertEqual(18, result.bottom_gas.depth)
def test_gas_ean(self): """ Test nitrox gas data creation """ e = kd.Gas(id='ean32', name='EAN32', o2=32, he=0, depth=33) self.assertEquals(e, kd.gas(32, 0, depth=33)) e = kd.Gas(id='ean40', name='EAN40', o2=40, he=0, depth=0) self.assertEquals(e, kd.gas(40, 0)) e = kd.Gas(id='ean50', name='EAN50', o2=50, he=0, depth=22) self.assertEquals(e, kd.gas(50, 0, depth=22)) e = kd.Gas(id='ean80', name='EAN80', o2=80, he=0, depth=0) self.assertEquals(e, kd.gas(80, 0))
def test_deco_dive_plan(self): """ Test deco dive plan """ ean50 = gas(50, 0, 22) ean80 = gas(80, 0, 9) air = gas(21, 0, depth=0) gas_list = GasList(air) gas_list.deco_gas.append(ean50) gas_list.deco_gas.append(ean80) plan = DivePlan() plan_deco_dive(plan, gas_list, 45, 35) self.assertEqual(4, len(plan.profiles)) pt = ProfileType profiles = [pt.PLANNED, pt.LOST_GAS, pt.EXTENDED, pt.EXTENDED_LOST_GAS] types = [p.type for p in plan.profiles] self.assertEqual(profiles, types) expected = [45, 45, 50, 50] depths = [p.depth for p in plan.profiles] self.assertEqual(expected, depths) expected = [35, 35, 38, 38] times = [p.time for p in plan.profiles] self.assertEqual(expected, times) expected = [1.155, 1.155, 1.26, 1.26] pp_o2 = [p.pp_o2 for p in plan.profiles] self.assertEqual(expected, pp_o2) expected = [56.67] * 4 mod = [round(p.mod, 2) for p in plan.profiles] self.assertEqual(expected, mod) # check deco gas idx = [i for i, _ in enumerate(profiles)] expected = [[ean50, ean80], []] * 2 for i, v in zip(idx, expected): deco_gas = plan.profiles[i].gas_list.deco_gas self.assertEqual(v, deco_gas) # check bottom gas expected = [air] * 4 bottom_gas = [p.gas_list.bottom_gas for p in plan.profiles] self.assertEqual(expected, bottom_gas)
def test_deco_gas_mix(self): """ Test deco gas mix depth update """ air = gas(21, 0, None) ean50 = gas(50, 0, None) o2 = gas(100, 0, 6) gas_list = GasList(air) gas_list.deco_gas = [ean50, o2] result = gas_mix_depth_update(gas_list, 1.4, 1.6) m1, m2 = result.deco_gas self.assertEqual(22, m1.depth) self.assertEqual(6, m2.depth)
def test_dive_legs(self): """ Test dive legs calculation """ air = gas(21, 0) gas_list = GasList(air) stops = [ Stop(18, 1), Stop(15, 1), Stop(12, 2), Stop(9, 3), Stop(6, 5), ] profile = DiveProfile(ProfileType.PLANNED, gas_list, 60, 25) legs = dive_legs(profile, stops, 20) self.assertEqual(3 + 10, len(legs)) self.assertEqual((0, 60, 3, air, False), legs[0]) self.assertEqual((60, 60, 22, air, False), legs[1]) self.assertEqual((60, 18, 4.2, air, False), legs[2]) deco_legs = legs[3::2] for s, l in zip(stops, deco_legs): self.assertEqual((s.depth, s.depth, s.time, air, True), l) self.assertEqual((6, 0, 0.6, air, True), legs[-1])
def test_gas_vol_info_warn(self): """ Test gas volume requirements analysis (warning) """ air = gas(21, 0) ean50 = gas(50, 0, depth=22) mixes = (air, 2000), (ean50, 100) gas_vol = OrderedDict(mixes) mixes = (air, 1999), (ean50, 99) min_gas_vol = OrderedDict(mixes) info = gas_vol_info(gas_vol, min_gas_vol) self.assertEqual(info[0], 'WARN: Gas mix Air volume NOT OK.') self.assertEqual(info[1], 'WARN: Gas mix EAN50 volume NOT OK.')
def parse_gas(t, travel=False): """ Parse gas mix. :param t: Gas mix string. :param travel: True if travel gas mix. """ t = t.upper() v = RE_GAS.search(t) m = None if v: n = v.group('name') p = v.group('o2') if p is None: if n == 'AIR': o2 = 21 elif n == 'O2': o2 = 100 else: return None else: o2 = int(p) p = v.group('he') he = 0 if p is None else int(p) p = v.group('depth') depth = None if p is None else int(p) #tank = v.group('tank') m = gas(o2, he, depth=depth) return m
def test_dive_slate(self): """ Test dive slate creation """ ean27 = gas(27, 0, depth=0) ean50 = gas(50, 0, depth=22) ean80 = gas(80, 0, depth=10) gas_list = GasList(ean27) gas_list.deco_gas.append(ean50) gas_list.deco_gas.append(ean80) depth = 45 time = 35 descent_rate = 20 profile = DiveProfile(ProfileType.PLANNED, gas_list, depth, time) stops = [ Stop(18, 1), Stop(15, 1), Stop(12, 2), Stop(9, 3), Stop(6, 5), ] legs = dive_legs(profile, stops, descent_rate) slate = dive_slate(profile, stops, legs, descent_rate) msg = '\n{}\n{}'.format(pformat(legs), pformat(slate)) self.assertEqual(8, len(slate), msg) self.assertEqual((45, None, 35, ean27), slate[0], slate) self.assertEqual((22, None, 37, ean50), slate[1], slate) # runtime = 38.7 self.assertEqual((18, 1, 39, None), slate[2], slate) # runtime = 38.7 + 1.3 self.assertEqual((15, 1, 40, None), slate[3], slate) # runtime = 40 + 2.3 self.assertEqual((12, 2, 42, None), slate[4], slate) # runtime = 42.3 + 3.3 self.assertEqual((9, 3, 46, ean80), slate[5], slate) # runtime = 45.6 + 5.3 self.assertEqual((6, 5, 51, None), slate[6], slate) # runtime = 50.9 + 0.6 = 51.4999... self.assertEqual((0, None, 51, None), slate[7], slate)
def test_travel_gas_mix(self): """ Test travel gas mix depth update """ ean50 = gas(50, 0, None) ean32 = gas(32, 0, None) air = gas(21, 0, None) gas_list = GasList(air) gas_list.travel_gas = [ean50, ean32] result = gas_mix_depth_update(gas_list, 1.4, 1.6) self.assertEqual(2, len(result.travel_gas)) self.assertEqual(0, result.travel_gas[0].depth) self.assertEqual(18, result.travel_gas[1].depth) self.assertEqual(33, result.bottom_gas.depth)
def test_bottom_gas_mix_no_travel(self): """ Test bottom gas mix depth update when no travel gas mix """ air = gas(21, 0, None) gas_list = GasList(air) result = gas_mix_depth_update(gas_list, 1.4, 1.6) self.assertEqual(0, result.bottom_gas.depth)
def test_dive_legs_overhead_switch(self): """ Test calculating overhead part of a deco dive (gas mix switch) """ ean50 = gas(50, 0, depth=22) air = gas(21, 0, depth=40) gas_list = GasList(air) gas_list.deco_gas.append(ean50) legs = [ (0, 40, 2, air, False), (40, 40, 20, air, False), (40, 22, 5, air, False), (22, 9, 2, ean50, False), # up to first gas mix switch (9, 9, 1, ean50, True), (9, 6, 1, ean50, True), (6, 6, 3, ean50, True), (6, 0, 1, ean50, True), ] oh_legs = dive_legs_overhead(gas_list, legs) self.assertEqual(legs[:3], oh_legs)
def test_deco_stops(self, f_c): """ Test deco dive plan deco stops calculator Verify that - gas mixes are passed correctly to the deco engine - decompression stops are returned - default parameters are passed correctly to deco engine The test is DecoTengu library specific. """ engine = mock.MagicMock() f_c.return_value = engine gas_list = GasList(gas(27, 0, depth=33)) gas_list.travel_gas.append(gas(32, 0, depth=0)) gas_list.deco_gas.append(gas(50, 0, depth=22)) gas_list.deco_gas.append(gas(80, 0, depth=10)) plan = DivePlan() p = DiveProfile(ProfileType.PLANNED, gas_list, 45, 35) stops = deco_stops(plan, p) args = engine.add_gas.call_args_list print(dir(args[0])) # check travel gas self.assertEqual(((0, 32, 0), {'travel': True}), args[0]) # check bottom gas self.assertEqual(((33, 27, 0), ), args[1]) # check deco gas self.assertEqual(((22, 50, 0), ), args[2]) self.assertEqual(((10, 80, 0), ), args[3]) # check deco stops are returned self.assertEqual(engine.deco_table, stops) self.assertFalse(engine.last_stop_6m) self.assertEqual(0.3, engine.model.gf_low) self.assertEqual(0.85, engine.model.gf_high)
def test_dive_legs_deco_gas(self): """ Test dive legs calculation with deco gas """ air = gas(21, 0) ean50 = gas(50, 0, 22) ean80 = gas(80, 0, 10) o2 = gas(100, 0, 6) gas_list = GasList(air) gas_list.deco_gas.extend((ean50, ean80, o2)) stops = [ Stop(18, 1), Stop(15, 1), Stop(12, 2), Stop(9, 3), Stop(6, 5), ] profile = DiveProfile(ProfileType.PLANNED, gas_list, 60, 25) legs = dive_legs(profile, stops, 20) self.assertEqual(4 + 10, len(legs)) self.assertEqual((0, 60, 3, air, False), legs[0]) self.assertEqual((60, 60, 22, air, False), legs[1]) self.assertEqual((60, 22, 3.8, air, False), legs[2]) self.assertEqual((22, 18, 0.4, ean50, False), legs[3]) self.assertEqual((18, 18, 1, ean50, True), legs[4]) self.assertEqual((18, 15, 0.3, ean50, True), legs[5]) self.assertEqual((15, 15, 1, ean50, True), legs[6]) self.assertEqual((15, 12, 0.3, ean50, True), legs[7]) self.assertEqual((12, 12, 2, ean50, True), legs[8]) self.assertEqual((12, 9, 0.3, ean50, True), legs[9]) self.assertEqual((9, 9, 3, ean80, True), legs[10]) self.assertEqual((9, 6, 0.3, ean80, True), legs[11]) self.assertEqual((6, 6, 5, o2, True), legs[-2]) self.assertEqual((6, 0, 0.6, o2, True), legs[-1])
def test_dive_legs_overhead_deco(self): """ Test calculating overhead part of a deco dive (first deco stop) """ ean50 = gas(50, 0, depth=22) air = gas(21, 0, depth=40) gas_list = GasList(air) gas_list.deco_gas.append(ean50) legs = [ (0, 40, 2, air, False), (40, 40, 20, air, False), (40, 24, 5, air, False), # up to first deco stop (24, 24, 1, air, True), (24, 21, 1, air, True), (21, 21, 1, ean50, True), # ... (9, 9, 1, ean50, True), (9, 9, 1, ean50, True), (9, 6, 1, ean50, True), # ... ] oh_legs = dive_legs_overhead(gas_list, legs) self.assertEqual(legs[:3], oh_legs)
def _get_gas(self, header, gas_no): """ Get gas information from OSTC dive header. :Parameters: header Dive header information. gas_no Gas number to get (1-6). """ getter = GAS_GETTERS.get(gas_no) if getter is None: raise ValueError('invalid gas mix number') o2, he = getter(header) return kd.gas(o2=o2, he=he)
def _get_profile(self, header, dive_data): """ Parse OSTC dive samples. """ # ostc starts dive below at a depth, so add (0, 0) sample yield kd.Sample(depth=0.0, time=0, gas=self._get_gas(header, header.gas)) for i, sample in enumerate(dive_data, 1): temp = C2K(sample.temp) if sample.temp else None setpoint = B2Pa(sample.setpoint / 100.0) if sample.setpoint else None # deco info deco_time = sample.deco_time * 60.0 if sample.deco_depth else None deco_depth = sample.deco_depth if sample.deco_depth else None deco_alarm = False if sample.alarm is not None: deco_alarm = sample.alarm in (2, 3) gas = None if sample.current_gas is not None: gas = self._get_gas(header, sample.current_gas) elif sample.gas_set_o2 is not None: gas = kd.gas(sample.gas_set_o2, sample.gas_set_he) yield kd.Sample(depth=sample.depth, time=(i * header.sampling), alarm=('deco', ) if deco_alarm else None, temp=temp, setpoint=setpoint, setpointby='user' if setpoint else None, deco_time=deco_time, deco_depth=deco_depth, gas=gas) yield kd.Sample(depth=0.0, time=(i + 1) * header.sampling)
def test_dive_legs_overhead_no_deco(self): """ Test calculating overhead part of a deco dive (no deco gas mix) """ air = gas(21, 0, depth=40) gas_list = GasList(air) legs = [ (0, 40, 2, air, False), (40, 40, 20, air, False), (40, 24, 5, air, False), # up to first deco stop (24, 24, 1, air, True), (24, 21, 1, air, True), (21, 21, 1, air, True), # ... (9, 9, 1, air, True), (9, 9, 1, air, True), (9, 6, 1, air, True), # ... ] oh_legs = dive_legs_overhead(gas_list, legs) self.assertEqual(legs[:3], oh_legs)
def test_deco_stops_param_change(self, f_c): """ Test deco dive plan deco stops calculator default params change Verify that dive decompression parameters are passed correctly to the deco engine. This test is DecoTengu decompression library specific. """ engine = mock.MagicMock() f_c.return_value = engine gas_list = GasList(gas(27, 0, depth=33)) plan = DivePlan() plan.gf_low = 10 plan.gf_high = 95 plan.last_stop_6m = True p = DiveProfile(ProfileType.PLANNED, gas_list, 45, 35) deco_stops(plan, p) self.assertTrue(engine.last_stop_6m) self.assertEqual(0.1, engine.model.gf_low) self.assertEqual(0.95, engine.model.gf_high)