def test_midnight_rollover(self): """ When a flight starts just before midnight, the majority of the flight will be in the next day so the fallback_dt needs to adjust throughout the data otherwise it will try to force the next day's flight to appear to that of the previous. """ # fallback is at start of the recording dt = datetime(2012, 12, 12, 23, 59, 58, tzinfo=pytz.utc) hdf = MockHDF({ 'Hour': P('Hour', np.ma.array([23, 23] + [0] * 18)), 'Minute': P('Minute', np.ma.array([59, 59] + [0] * 18)), 'Second': P('Minute', np.ma.array([58, 59] + list(range(18)))), # last two seconds and start of next flight }, duration=20) res, precise_timestamp, _ = _calculate_start_datetime(hdf, dt, dt) # result is the exact start of the data for the timestamp (not a day before!) self.assertEqual(res, datetime(2012, 12, 12, 23, 59, 58, tzinfo=pytz.utc)) self.assertFalse(precise_timestamp)
def derive(self, tcas_ctl = M('TCAS Combined Control'), tcas_up = M('TCAS Up Advisory'), tcas_down = M('TCAS Down Advisory'), tcas_vert = M('TCAS Vertical Control'), vertspd = P('Vertical Speed'), ra_sections = S('TCAS RA Sections'), raduration = KPV('TCAS RA Warning Duration'), ): standard_vert_accel = 8.0 * 60 # 8 ft/sec^2, converted to ft/min^2 standard_vert_accel_reversal = 11.2 * 60 # ft/sec^2 ==> ft/min^2 standard_response_lag = 5.0 # seconds standard_response_lag_reversal = 2.5 # seconds self.array = vertspd.array * 0 #make a copy, mask and zero out self.array.mask = True required_fpm_array = vertspd.array * 0 for ra in ra_sections: self.debug('TCAS RA Standard Response: in sections') #initialize response state ra_ctl_prev = tcas_ctl.array[ra.start_edge] # used to check if the command has changed up_prev = tcas_ctl.array[ra.start_edge] # used to check if the command has changed down_prev = tcas_ctl.array[ra.start_edge] # used to check if the command has changed initial_vert_spd = vertspd.array[ra.start_edge] std_vert_spd = initial_vert_spd # current standard response vert speed in fpm required_fpm = None # nominal vertical speed in fpm required by the RA lag_end = ra.start_edge + standard_response_lag # time pilot response lag ends acceleration = standard_vert_accel for t in range(int(ra.start_edge), int(ra.stop_edge)+1): # set required_fpm for initial ra or a change in command if ra_ctl_prev!=tcas_ctl.array[t] or up_prev!=tcas_up.array[t] or down_prev!=tcas_down.array[t]: if tcas_ctl.array[t] == 'Up Advisory Corrective' or tcas_up.array[t].lower()!='no up advisory': required_fpm = tcas_vert_spd_up(tcas_up.array[t], vertspd.array[t], tcas_vert.array[t]) elif tcas_ctl.array[t] == 'Down Advisory Corrective' or tcas_down.array[t].lower()!='no down advisory': required_fpm = tcas_vert_spd_down(tcas_down.array[t], vertspd.array[t], tcas_vert.array[t]) else: required_fpm = vertspd.array[t] if tcas_vert.array[t]=='Reversal': lag_end = t + standard_response_lag_reversal acceleration = standard_vert_accel_reversal initial_vert_spd = std_vert_spd if required_fpm is None: self.warning('TCAS RA Standard Response: No required_fpm found. Take a look! '+str(t)) std_vert_spd= update_std_vert_spd(t, lag_end, tcas_ctl.array[t], tcas_up.array[t], tcas_down.array[t], acceleration, required_fpm, std_vert_spd, initial_vert_spd, vertspd.array[t]) self.array.data[t] = std_vert_spd self.array.mask[t] = False required_fpm_array[t] = required_fpm ra_ctl_prev = tcas_ctl.array[t] up_prev = tcas_up.array[t] down_prev = tcas_down.array[t] #end of time loop within ra section return
def derive(self, pitch=P('Pitch'), climbs=S('Initial Climb')): for climb in climbs: masked_pitch = mask_outside_slices(pitch.array, [climb.slice]) pitch_index = np.ma.argmax( masked_pitch <= -10) or np.ma.argmin(masked_pitch) scaling_factor = abs(masked_pitch[pitch_index]) / 10 window_threshold = -10.00 * scaling_factor min_window_threshold = -8.00 * scaling_factor window_size = 32 window_threshold_step = 0.050 * scaling_factor diffs = np.ma.ediff1d(masked_pitch[climb.slice.start:pitch_index]) diffs_exist = diffs.data.size >= 2 big_diff_index = -1 while diffs_exist: sig_pitch_threshold = window_threshold / window_size for i, d in enumerate(diffs): # Look for the first big negative pitch spike if diffs[i:i + window_size].sum() < window_threshold: # Find the first significant negative value within the # spike and make that the starting point of the phase big_diff_index = np.ma.argmax( diffs[i:i + window_size] < sig_pitch_threshold) + i break # Bail on match or total failure if big_diff_index != -1 or window_size < 2: break # Shrink window size instead of looking for insignificant # spikes and scale window/pitch thresholds accordingly if window_threshold >= min_window_threshold: window_size /= 2 min_window_threshold /= 2 window_threshold /= 2 window_threshold_step /= 2 sig_pitch_threshold *= 2 else: window_threshold += window_threshold_step if big_diff_index != -1: self.create_section( slice(climb.slice.start + big_diff_index, pitch_index)) # Worst case fallback, this should happen extremely rarely # and would trigger all events related to this phase else: self.create_section(slice(climb.slice.start, climb.slice.stop))
def test_year_00_uses_fallback_year(self): # Ensure that a timebase error is not raised due to old date! #Other than the year 2000 or possibly 2100, no date values # can be all 0's dt = datetime(2012, 12, 12, 12, 12, 10, tzinfo=pytz.utc) # Test only without second and empty year hdf = MockHDF({ 'Year': P('Year', np.ma.array([0, 0, 0, 0])), 'Month': P('Month', np.ma.array([11, 11, 11, 11])), 'Day': P('Day', np.ma.array([11, 11, 11, 11])), 'Hour': P('Hour', np.ma.array([11, 11, 11, 11], mask=[True, False, False, False])), 'Minute': P('Minute', np.ma.array([11, 11]), frequency=0.5), }, duration=4) # add a masked invalid value hdf['Year'].array[2] = 50 hdf['Year'].array[2] = np.ma.masked res = _calculate_start_datetime(hdf, dt, dt) print hdf, dt self.assertEqual(res, datetime(2012, 11, 11, 11, 11, 10, tzinfo=pytz.utc))
def test_airborne_helicopter_radio_refinement(self): ''' Confirms that the beginning and end are trimmed to match the radio signal, not the (smoothed) AGL data. ''' gog = M(name='Gear On Ground', array=np.ma.array([0]*3+[1]*5+[0]*10+[1]*5, dtype=int), frequency=1, offset=0, values_mapping={1:'Ground', 0:'Air'}) agl = P(name='Altitude AGL', array=np.ma.array([0.0]*6+[20.0]*12+[0.0]*5, dtype=float)) rad = P(name='Altitude Radio', array=np.ma.array([0.0]*7+[10.0]*10+[0.0]*6, dtype=float)) rtr = buildsection('Rotors Turning', 0, 40) node = Airborne() node.derive(rad, agl, gog, rtr) self.assertEqual(node[0].start_edge, 6.1) self.assertEqual(node[0].stop_edge, 16.9)
def derive(self, tcas_sens=P('TCAS Sensitivity Level'), ra_sections=S('TCAS RA Sections') ): _change_points = change_indexes(tcas_sens.array.data) #returns array index for cp in _change_points: _value = tcas_sens.array.data[cp] if tcas_sens.array.mask[cp]: _name = 'TCAS Sensitivity|masked' else: _name = 'TCAS Sensitivity|' + tcas_sens.array[cp] kpv = KeyPointValue(index=cp, value=_value, name=_name) self.append(kpv)
def test_derive_masked(self): ''' ''' flight_number = FlightNumber() number_param = P( 'Flight Number', array=np.ma.array([0, 0, 0, 0, 36, 36, 0, 0, 0, 0], mask=[True] * 4 + [False] * 2 + [True] * 4)) flight_number.derive(number_param) self.assertEqual(flight_number.value, '36')
def derive(self, ra_sections=S('TCAS RA Sections'), tcas_ctl=M('TCAS Combined Control'), tcas_up = M('TCAS Up Advisory'), tcas_down = M('TCAS Down Advisory'), std=P('TCAS RA Standard Response'), vertspd=P('Vertical Speed') ): for ra in ra_sections: exceedance=0 deviation=0 for t in range(int(ra.start_edge), int(ra.stop_edge)): if tcas_ctl.array[t] == 'Down Advisory Corrective' or tcas_down.array[t].lower()!='no down advisory': deviation = max(vertspd.array[t] - std.array[t], 0) elif tcas_ctl.array[t] == 'Up Advisory Corrective' or tcas_up.array[t].lower()!='no up advisory': deviation = max(std.array[t] - vertspd.array[t], 0) else: deviation = abs(vertspd.array[t] - std.array[t]) deviation = max( deviation-250, 0 ) # allow 250 fpm buffer #print 't vert std DEV', t, vertspd.array[t], std.array[t], deviation if deviation and deviation!=0: exceedance += deviation #print 'Alt Exceed', exceedance exceedance = exceedance / 60.0 # min to sec self.create_kpv(ra.start_edge, exceedance)
def test_airborne_helicopter_cant_fly_without_rotor_turning(self): gog = M(name='Gear On Ground', array=np.ma.array([1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], dtype=int), values_mapping={1:'Ground', 0:'Air'}) agl = P(name='Altitude AGL', array=np.ma.array([0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 2, 0], dtype=float), frequency=0.2) rtr = buildsection('Rotors Turning', 0, 0) node = Airborne() node.derive(agl, agl, gog, rtr) self.assertEqual(len(node), 0)
def derive(self, alt_agl=P('Altitude AGL'), ias=P('Airspeed'), airs=S('Airborne'), pitch_rate=P('Pitch Rate')): for air in airs: trans_slices = slices_from_to(ias.array[air.slice], ROTOR_TRANSITION_SPEED_HIGH, ROTOR_TRANSITION_SPEED_LOW, threshold=1.0)[1] if trans_slices: for trans in shift_slices(trans_slices, air.slice.start): trans_end = index_at_value( ias.array, 0.0, _slice=slice(trans.stop, trans.stop + 20 * ias.frequency), endpoint='first_closing') self.create_phase(slice(trans.start, trans_end + 1))
def derive(self, alt_aal=P('Altitude AAL'), lat=P('Latitude Smoothed'), lon=P('Longitude Smoothed'), tdwns=KTI('Touchdown')): app_range = np_ma_masked_zeros_like(alt_aal.array) #Helicopter compuation does not rely on runways! stop_delay = 10 # To make sure the helicopter has stopped moving for tdwn in tdwns: end = tdwn.index endpoint = {'latitude': lat.array[int(end)], 'longitude': lon.array[int(end)]} prev_tdwn = tdwns.get_previous(end) begin = prev_tdwn.index + stop_delay if prev_tdwn else 0 this_leg = slices_int(begin, end+stop_delay) _, app_range[this_leg] = bearings_and_distances(lat.array[this_leg], lon.array[this_leg], endpoint) self.array = app_range
def test_airborne_helicopter_short(self): gog = M(name='Gear On Ground', array=np.ma.array([0]*3+[1]*5+[0]*10+[1]*5, dtype=int), frequency=1, offset=0, values_mapping={1:'Ground', 0:'Air'}) agl = P(name='Altitude AGL', array=np.ma.array([2.0, 0.0, 0.0]+[0.0]*4+[20.0]*10+[0.0]*6, dtype=float)) rtr = buildsection('Rotors Turning', 0, 40) node = Airborne() node.derive(agl, agl, gog, rtr) self.assertEqual(len(node), 1)
def test_nose_down_multiple_climbs(self): node = NoseDownAttitudeAdoption() pitch = np.concatenate([np.ones(15) * 2, np.linspace(2, -11, num=15), np.linspace(-11, 2, num=10), np.ones(20) * 2, np.linspace(2, -11, num=15), np.ones(10) * -11]) climbs = buildsections('Initial Climb', [10, 40], [60, 85]) node.derive(P('Pitch', pitch), climbs) self.assertEqual(len(node), 2) self.assertEqual(node[0], Section('Nose Down Attitude Adoption', slice(15, 28, None), 15, 28)) self.assertEqual(node[1], Section('Nose Down Attitude Adoption', slice(60, 73, None), 60, 73))
def test_2_sources(self): lat1 = P('Latitude (1)', array=np.ma.arange(-10, 10), frequency=0.5, offset=0.25) lat1.array[5:8] = np.ma.masked lat2 = P('Latitude (2)', array=np.ma.arange(-9.5, 10), frequency=0.5, offset=0.75) lat2.array[5:8] = np.ma.masked latitude = Latitude() latitude.get_derived((lat1, lat2, None)) expected = np.ma.arange(-9.75, 10, 0.5) expected[-1] = np.ma.masked np.testing.assert_array_equal(latitude.array, expected) np.testing.assert_array_equal(latitude.array.mask, expected.mask) assert latitude.offset == 0.5 assert latitude.frequency == 1
def derive(self, pilot_flying=M('Pilot Flying'), pitch_capt=P('Pitch (Capt)'), pitch_fo=P('Pitch (FO)'), roll_capt=P('Roll (Capt)'), roll_fo=P('Roll (FO)'), cc_capt=P('Control Column Force (Capt)'), cc_fo=P('Control Column Force (FO)'), ap1_eng=M('AP (1) Engaged'), ap2_eng=M('AP (2) Engaged'), takeoffs=S('Takeoff'), liftoffs=KTI('Liftoff'), rejected_toffs=S('Rejected Takeoff')): #TODO: Tidy phase = takeoffs or rejected_toffs or None if phase is None: # Nothing to do as no attempt to takeoff return lift = liftoffs.get_first() if liftoffs else None if lift and ap1_eng and ap2_eng: # check AP state at the floored index (just before lift) ap1 = ap1_eng.array[lift.index] == 'Engaged' ap2 = ap2_eng.array[lift.index] == 'Engaged' else: ap1 = ap2 = None args = (pilot_flying, pitch_capt, pitch_fo, roll_capt, roll_fo, cc_capt, cc_fo, phase.get_first(), ap1, ap2) self.set_flight_attr(self._determine_pilot(*args))
def derive(self, pilot_flying=M('Pilot Flying'), pitch_capt=P('Pitch (Capt)'), pitch_fo=P('Pitch (FO)'), roll_capt=P('Roll (Capt)'), roll_fo=P('Roll (FO)'), cc_capt=P('Control Column Force (Capt)'), cc_fo=P('Control Column Force (FO)'), ap1_eng=M('AP (1) Engaged'), ap2_eng=M('AP (2) Engaged'), landings=S('Landing'), touchdowns=KTI('Touchdown'), afr_landing_pilot=A('AFR Landing Pilot')): if afr_landing_pilot and afr_landing_pilot.value: self.set_flight_attr(afr_landing_pilot.value.replace('_', ' ').title()) return phase = landings.get_last() if landings else None tdwn = touchdowns.get_last() if touchdowns else None if tdwn and ap1_eng and ap2_eng: # check AP state at the floored index (just before tdwn) ap1 = ap1_eng.array[int(tdwn.index)] == 'Engaged' ap2 = ap2_eng.array[int(tdwn.index)] == 'Engaged' else: ap1 = ap2 = None args = (pilot_flying, pitch_capt, pitch_fo, roll_capt, roll_fo, cc_capt, cc_fo, phase, ap1, ap2) self.set_flight_attr(self._determine_pilot(*args))
def test_derive_ascii(self): flight_number_param = P('Flight Number', array=np.ma.masked_array(['ABC', 'DEF', 'DEF'], dtype=np.string_)) flight_number = FlightNumber() flight_number.set_flight_attr = Mock() flight_number.derive(flight_number_param) flight_number.set_flight_attr.assert_called_with('DEF') flight_number.set_flight_attr.reset_mock() # Entirely masked. flight_number_param.array[:] = np.ma.masked flight_number.derive(flight_number_param) flight_number.set_flight_attr.called = False
def test_derive_most_common_positive_float(self): flight_number = FlightNumber() neg_number_param = P( 'Flight Number', array=np.ma.array([-1,2,-4,10,20,40,11])) flight_number.derive(neg_number_param) self.assertEqual(flight_number.value, None) # TODO: Implement variance checks as below ##high_variance_number_param = P( ##'Flight Number', ##array=np.ma.array([2,2,4,4,4,7,7,7,4,5,4,7,910])) ##self.assertRaises(ValueError, flight_number.derive, high_variance_number_param) flight_number_param= P( 'Flight Number', array=np.ma.array([2,555.6,444,444,444,444,444,444,888,444,444, 444,444,444,444,444,444,7777,9100])) flight_number.set_flight_attr = Mock() flight_number.derive(flight_number_param) flight_number.set_flight_attr.assert_called_with('444')
def test_basic(self): # Although "basic" the synthetic radio altitude tests for noise rejection, transfer from radio to pressure altimetry and use of # the gear on ground signals. The wide tolerance is because the noise signal varies from run to run. alt_rad = P(name='Altitude Radio', array=(np.minimum( 6000, (1.0 - np.cos(np.arange(100) * 3.14 / 50)) * 4000 + np.random.rand(100) * 300)), frequency=2) alt_baro = P( name='Altitude STD', array=np.ma.array((1.0 - np.cos(np.arange(100) * 3.14 / 50)) * 4000 + 1000)) gog = M(name='Gear On Ground', array=np.ma.array([1] * 5 + [0] * 90 + [1] * 5), values_mapping={ 0: 'Air', 1: 'Ground' }) alt_aal = AltitudeAGL() alt_aal.derive(alt_rad, None, alt_baro, gog) self.assertLess(abs(np.max(alt_aal.array) - 8000), 300)
def test_derive(self): start = KTI(items=[ KeyTimeInstance(index=2, name='TCAS RA Start'), ]) param = P('AP Engaged', array=np.ma.arange(10) * 1.0, frequency=1., offset=0.) expected = [ KeyPointValue(index=2, value=2.0, name='TCAS RA Start Autopilot'), ] k = self.klass() k.derive(param, start) self.assertEqual(k, expected)
def derive(self, gear=M('Gear Down'), alt_aal=P('Altitude AAL'), touchdowns=KTI('Touchdown')): for touchdown in touchdowns: rough_index = index_at_value(gear.array.data, 0.5, slice(touchdown.index, 0, -1)) # index_at_value tries to be precise, but in this case we really # just want the index at the new flap setting. if rough_index: last_index = np.round(rough_index) alt_last = value_at_index(alt_aal.array, last_index) self.create_kpv(last_index, alt_last)
def test_derive(self): p1 = [26.51]*7 + [26.63] + [26.51]*7 + [26.4]*27 + [26.29] + [26.4]*7 p2 = [26.51]*14 + [26.63] + [26.4]*20 + [26.29] + [26.4]*14 oil_press1 = P('MGB Oil Press (1)', np.ma.array(p1)) oil_press2 = P('MGB Oil Press (2)', np.ma.array(p2)) mgb_oil_press = self.node_class() mgb_oil_press.derive(oil_press1, oil_press2) # array size should be double, 100 self.assertEqual(mgb_oil_press.array.size, (oil_press1.array.size*2)) self.assertEqual(mgb_oil_press.array.size, (oil_press2.array.size*2)) # Frequency should be doubled 2Hz self.assertEqual(mgb_oil_press.frequency, (oil_press1.frequency*2)) self.assertEqual(mgb_oil_press.frequency, (oil_press2.frequency*2)) # Offset should remain the same as the parameter. 0.0 self.assertEqual(mgb_oil_press.offset, oil_press1.offset) self.assertEqual(mgb_oil_press.offset, oil_press2.offset) # Element 7 will become 14 and values 26.63, 26.51 average to 26.57 self.assertEqual(oil_press1.array[7], 26.63) self.assertEqual(oil_press2.array[7], 26.51) self.assertEqual(mgb_oil_press.array[14], (oil_press1.array[7]+oil_press2.array[7])/2)
def test_nose_down_insufficient_pitch(self): node = NoseDownAttitudeAdoption() pitch = np.concatenate( [np.ones(15) * 2, np.linspace(2, -6, num=15), np.ones(10) * -6]) node.derive(P('Pitch', pitch), self.climbs, self.offshore) self.assertEqual(len(node), 1) self.assertEqual( node[0], Section('Nose Down Attitude Adoption', slice(15, 29, None), 15, 29))
def test_nose_down_basic(self): node = NoseDownAttitudeAdoption() pitch = np.concatenate( [np.ones(15) * 2, np.linspace(2, -11, num=15), np.ones(10) * -11]) node.derive(P('Pitch', pitch), self.climbs) self.assertEqual(len(node), 1) self.assertEqual( node[0], Section('Nose Down Attitude Adoption', slice(15, 28, None), 15, 28))
def derive(self, gnds=S('Grounded'), pitch=P('Pitch'), roll=P('Roll'), offshores=S('Offshore')): if offshores is not None: self.create_sections( slices_and(gnds.get_slices(edges=False), offshores.get_slices(edges=False))) return decks = [] for gnd in gnds: # The fourier transform for pitching motion... p = pitch.array[gnd.slice] if np.all(p.mask): continue n = float( len(p)) # Scaling the result to be independet of data length. fft_p = np.abs(np.fft.rfft(p - moving_average(p))) / n # similarly for roll r = roll.array[gnd.slice] if np.all(r.mask): continue fft_r = np.abs(np.fft.rfft(r - moving_average(r))) / n # What was the maximum harmonic seen? fft_max = np.ma.max(fft_p + fft_r) # Values of less than 0.1 were on the ground, and 0.34 on deck for the one case seen to date. if fft_max > 0.2: decks.append(gnd.slice) if decks: self.create_sections(decks)
def test_airborne_helicopter_overlap(self): gog = M(name='Gear On Ground', array=np.ma.array([1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1], dtype=int), values_mapping={1:'Ground', 0:'Air'}) agl = P(name='Altitude AGL', array=np.ma.array([0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 2, 0], dtype=float), frequency=0.2) rtr = buildsection('Rotors Turning', 0, 40) node = Airborne() node.derive(agl, agl, gog, rtr) self.assertEqual(len(node), 2) self.assertEqual(node[0].slice.start, 3.2) self.assertEqual(node[0].slice.stop, 6) self.assertEqual(node[1].slice.start, 8) self.assertEqual(node[1].slice.stop, 10.5)
def test_empty_year_no_seconds(self): # NB: 12's are the fallback_dt, 11's are the recorded time parameters dt = datetime(2012, 12, 12, 12, 12, 10, tzinfo=pytz.utc) # Test only without second and empty year hdf = MockHDF( { 'Month': P('Month', np.ma.array([11, 11, 11, 11])), 'Day': P('Day', np.ma.array([])), 'Hour': P( 'Hour', np.ma.array([11, 11, 11, 11], mask=[True, False, False, False])), 'Minute': P('Minute', np.ma.array([11, 11]), frequency=0.5), }, duration=4) res, precise_timestamp = _calculate_start_datetime(hdf, dt, dt) # 9th second as the first sample (10th second) was masked self.assertEqual(res, datetime(2012, 11, 12, 11, 11, 10, tzinfo=pytz.utc)) self.assertFalse(precise_timestamp)
def test_derive(self): t1 = [78.0]*14 + [78.5,78] + [78.5]*23 + [79.0]*5 + [79.5] + [79.0]*5 t2 = [78.0]*13 + [78.5,78.0, 77.5] + [78.5]*20 + [79.0]*14 oil_temp1 = P('MGB Oil Temp (1)', np.ma.array(t1)) oil_temp2 = P('MGB Oil Temp (2)', np.ma.array(t2)) mgb_oil_temp = self.node_class() mgb_oil_temp.derive(oil_temp1, oil_temp2) # array size should be double, 100 self.assertEqual(mgb_oil_temp.array.size, (oil_temp1.array.size*2)) self.assertEqual(mgb_oil_temp.array.size, (oil_temp2.array.size*2)) # Frequency should be doubled 2Hz self.assertEqual(mgb_oil_temp.frequency, (oil_temp1.frequency*2)) self.assertEqual(mgb_oil_temp.frequency, (oil_temp2.frequency*2)) # Offset should remain the same as the parameter. 0.0 self.assertEqual(mgb_oil_temp.offset, oil_temp1.offset) self.assertEqual(mgb_oil_temp.offset, oil_temp2.offset) # Element 44 will become 88 and values 79.5, 79 average to 79.25 self.assertEqual(oil_temp1.array[44], 79.5) self.assertEqual(oil_temp2.array[44], 79) self.assertEqual(mgb_oil_temp.array[88], (oil_temp1.array[44]+oil_temp2.array[44])/2)
def test_2_sources(self): long1 = P('Longitude (1)', array=np.ma.concatenate( (np.arange(160, 180), np.arange(-180, -170))), frequency=0.5, offset=0.25) long1.array[5:8] = np.ma.masked long2 = P('Longitude (2)', array=np.ma.concatenate( (np.arange(160.5, 180), np.arange(-179.5, -170))), frequency=0.5, offset=0.75) long2.array[5:8] = np.ma.masked longitude = Longitude() longitude.get_derived((long1, long2, None)) expected = np.ma.concatenate( (np.arange(160.25, 180, 0.5), np.arange(-179.75, -170, 0.5))) expected[-1] = np.ma.masked np.testing.assert_array_equal(longitude.array, expected) np.testing.assert_array_equal(longitude.array.mask, expected.mask) assert longitude.offset == 0.5 assert longitude.frequency == 1
def test_derive_basic(self): alt_agl = P(name='Altitude AGL', array=np.ma.concatenate((np.zeros(5), np.ones(30) * 10.0, np.zeros(5)))) alt_agl.array[14] = 20.0 alt_agl.array[17] = 60.0 hovers = buildsections('Hover', [6, 8], [24,26]) airs = buildsections('Airborne', [6, 26]) t_hf = buildsections('Transition Hover To Flight', [12, 15]) t_fh = buildsections('Transition Flight To Hover', [18, 20]) node = HoverTaxi() node.derive(alt_agl, airs, hovers, t_hf, t_fh) self.assertEqual(len(node), 2) self.assertEqual(node[0].slice.start, 9) self.assertEqual(node[0].slice.stop, 12) self.assertEqual(node[1].slice.start, 21) self.assertEqual(node[1].slice.stop, 24)