コード例 #1
0
 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')):
     print 'in Alt Exceed'
     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)
コード例 #2
0
    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))
コード例 #3
0
    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))
コード例 #4
0
    def test_dual_ap(self):
        # Two result in just "Engaged" state still
        ase1 = M(array=np.ma.array(data=[0, 0, 1, 1, 0, 0]),
                 values_mapping={
                     1: 'Engaged',
                     0: '-'
                 },
                 name='AP (1) Engaged')
        ase2 = M(array=np.ma.array(data=[0, 0, 0, 1, 1, 0]),
                 values_mapping={
                     1: 'Engaged',
                     0: '-'
                 },
                 name='AP (2) Engaged')
        ase3 = None

        node = self.node_class()
        node.derive(ase1, ase2, ase3)

        expected = M(array=np.ma.array(data=[0, 0, 1, 1, 1, 0]),
                     values_mapping={
                         0: '-',
                         1: 'Engaged'
                     },
                     name='AP Engaged',
                     frequency=1,
                     offset=0.1)

        np.testing.assert_array_equal(expected.array, node.array)
コード例 #5
0
 def derive(self,
            gl=M('Gear (L) Position'),
            gn=M('Gear (N) Position'),
            gr=M('Gear (R) Position'),
            gc=M('Gear (C) Position')):
     up_state = vstack_params_where_state(
         (gl, 'Up'),
         (gn, 'Up'),
         (gr, 'Up'),
         (gc, 'Up'),
     ).all(axis=0)
     down_state = vstack_params_where_state(
         (gl, 'Down'),
         (gn, 'Down'),
         (gr, 'Down'),
         (gc, 'Down'),
     ).all(axis=0)
     transit_state = vstack_params_where_state(
         (gl, 'In Transit'),
         (gn, 'In Transit'),
         (gr, 'In Transit'),
         (gc, 'In Transit'),
     ).any(axis=0)
     param = first_valid_parameter(gl, gn, gr, gc)
     self.array = np_ma_masked_zeros_like(param.array)
     self.array[repair_mask(up_state, repair_duration=None)] = 'Up'
     self.array[repair_mask(down_state, repair_duration=None)] = 'Down'
     self.array[repair_mask(transit_state,
                            repair_duration=None)] = 'In Transit'
     self.array = nearest_neighbour_mask_repair(self.array)
コード例 #6
0
 def derive(self,
            any_running=M('Eng (*) Any Running'),
            eng_oei=M('One Engine Inoperative'),
            autorotation=S('Autorotation')):
     aeo = np.ma.logical_not(eng_oei.array == 'OEI')
     for section in autorotation:
         aeo[section.slice] = False
     self.array = np.ma.logical_and(any_running.array == 'Running', aeo)
コード例 #7
0
 def derive(self,
            gl=M('Gear (L) On Ground'),
            gr=M('Gear (R) On Ground'),
            vert_spd=P('Vertical Speed'),
            torque=P('Eng (*) Torque Avg'),
            ac_series=A('Series'),
            collective=P('Collective')):
     pass
コード例 #8
0
 def derive(self,
            ian_final=P('IAN Final Approach Course'),
            alt_aal=P('Altitude AAL For Flight Phases'),
            apps=S('Approach Information'),
            ils_freq=P('ILS Frequency'),
            app_src_capt=M('Displayed App Source (Capt)'),
            app_src_fo=M('Displayed App Source (FO)')):
     pass
コード例 #9
0
 def derive(self,
            baro_cpt=P('Baro Correction (Capt)'),
            baro_fo=P('Baro Correction (FO)'),
            baro_sel=M('Baro Setting Selection'),
            baro_sel_cpt=M('Baro Setting Selection (Capt)'),
            baro_sel_fo=M('Baro Setting Selection (FO)'),
            baro_cor_isis=P('Baro Correction (ISIS)'),
            fasts=S('Fast')):
     pass
コード例 #10
0
 def setUp(self):
     self.node_class = RotorBrakeEngaged
     self.values_mapping = {1: 'Engaged', 0: '-'}
     self.brk1 = M('Rotor Brake (1) Engaged',
                   np.ma.array(data=[0, 0, 1, 1, 0, 0]),
                   values_mapping=self.values_mapping)
     self.brk2 = M('Rotor Brake (2) Engaged',
                   np.ma.array(data=[0, 0, 0, 1, 1, 0]),
                   values_mapping=self.values_mapping)
コード例 #11
0
 def derive(self,
            gear_L=M('Gear (L) Up In Transit'),
            gear_N=M('Gear (N) Up In Transit'),
            gear_R=M('Gear (R) Up In Transit'),
            gear_C=M('Gear (C) Up In Transit')):
     combine_params = [(x, 'Retracting')
                       for x in (gear_L, gear_R, gear_N, gear_C) if x]
     if len(combine_params):
         self.array = vstack_params_where_state(*combine_params).any(axis=0)
コード例 #12
0
    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
コード例 #13
0
    def derive(self,
               eng_1_oei=M('Eng (1) One Engine Inoperative'),
               eng_2_oei=M('Eng (2) One Engine Inoperative'),
               autorotation=S('Autorotation')):

        oei = vstack_params_where_state((eng_1_oei, 'Active'),
                                        (eng_2_oei, 'Active')).any(axis=0)
        for section in autorotation:
            oei[section.slice] = False
        self.array = oei
コード例 #14
0
    def derive(self,
               brk1=M('Rotor Brake (1) Engaged'),
               brk2=M('Rotor Brake (2) Engaged')):

        stacked = vstack_params_where_state(
            (brk1, 'Engaged'),
            (brk2, 'Engaged'),
        )
        self.array = stacked.any(axis=0)
        self.array.mask = stacked.mask.any(axis=0)
コード例 #15
0
 def derive(
         self,
         tcas_ops=S('TCAS Operational'),
         tcas_ta1=M('TCAS TA'),
         tcas_ta2=M('TCAS All Threat Traffic'),
         tcas_ta3=M('TCAS Traffic Alert'),
         tcas_ta4=M('TCAS TA (1)'),
         tcas_ras=S('TCAS Resolution Advisory'),
 ):
     pass
コード例 #16
0
 def derive(self,
            accel_lon=P('Acceleration Longitudinal Offset Removed'),
            eng_running=M('Eng (*) All Running'),
            groundeds=S('Grounded'),
            eng_n1=P('Eng (*) N1 Max'),
            toff_acc=KTI('Takeoff Acceleration Start'),
            toff_rwy_hdg=S('Takeoff Runway Heading'),
            seg_type=A('Segment Type'),
            toga=M('Takeoff And Go Around')):
     pass
コード例 #17
0
 def derive(self,
            ase1=M('ASE (1) Engaged'),
            ase2=M('ASE (2) Engaged'),
            ase3=M('ASE (3) Engaged')):
     stacked = vstack_params_where_state(
         (ase1, 'Engaged'),
         (ase2, 'Engaged'),
         (ase3, 'Engaged'),
     )
     self.array = stacked.sum(axis=0)
     self.offset = offset_select('mean', [ase1, ase2, ase3])
コード例 #18
0
 def derive(self,
            gl=M('Gear (L) In Transit'),
            gn=M('Gear (N) In Transit'),
            gr=M('Gear (R) In Transit'),
            gc=M('Gear (C) In Transit')):
     # Join all available gear parameters and use whichever are available.
     self.array = vstack_params_where_state(
         (gl, 'In Transit'),
         (gn, 'In Transit'),
         (gr, 'In Transit'),
         (gc, 'In Transit'),
     ).any(axis=0)
コード例 #19
0
    def test_derive(self):
        # combine individal (L/R/C/N) params
        left = M('Gear (L) Up',
                 array=np.ma.array([0] * 15 + [1] * 30 + [0] * 15),
                 values_mapping=self.values_mapping)
        right = M('Gear (R) Up',
                  array=np.ma.array([0] * 14 + [1] * 31 + [0] * 15),
                  values_mapping=self.values_mapping)
        node = self.node_class()
        node.derive(left, None, right, None)

        np.testing.assert_array_equal(node.array, self.expected.array)
        self.assertEqual(node.values_mapping, self.values_mapping)
コード例 #20
0
    def test_derive__combine(self):
        # combine individal (L/R/C/N) params
        left = M('Gear (L) Down In Transit',
                 array=np.ma.array([0] * 46 + [1] * 9 + [0] * 5),
                 values_mapping=self.values_mapping)
        right = M('Gear (R) Down In Transit',
                  array=np.ma.array([0] * 45 + [1] * 9 + [0] * 6),
                  values_mapping=self.values_mapping)
        node = self.node_class()
        node.derive(left, None, right, None)

        np.testing.assert_array_equal(node.array, self.expected.array)
        self.assertEqual(node.values_mapping, self.values_mapping)
コード例 #21
0
    def test_derive__gear_position(self):
        gl = M('Gear (L) Position',
               array=np.ma.array([3] * 5 + [2] * 10 + [1] * 30 + [2] * 10 +
                                 [3] * 5),
               values_mapping=self.values_mapping)
        gr = M('Gear (R) Position',
               array=np.ma.array([3] * 6 + [2] * 9 + [1] * 30 + [2] * 9 +
                                 [3] * 6),
               values_mapping=self.values_mapping)
        node = self.node_class()
        node.derive(gl, None, gr, None)

        np.testing.assert_array_equal(node.array, self.expected.array)
        self.assertEqual(node.values_mapping, self.values_mapping)
コード例 #22
0
 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')):
     pass
コード例 #23
0
 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'),
            afr_takeoff_pilot=A('AFR Takeoff Pilot')):
     pass
コード例 #24
0
 def derive(self,
            eng2_np=P('Eng (2) Np'),
            eng1_n1=P('Eng (1) N1'),
            eng2_n1=P('Eng (2) N1'),
            groundeds=S('Grounded'),
            prop_brake=M('Propeller Brake')):
     pass
コード例 #25
0
 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)
コード例 #26
0
    def derive(self,
               alt_rad=P('Altitude Radio'),
               alt_agl=P('Altitude AGL'),
               gog=M('Gear On Ground'),
               rtr=S('Rotors Turning')):
        # When was the gear in the air?
        gear_off_grounds = runs_of_ones(gog.array == 'Air')

        if alt_rad and alt_agl and rtr:
            # We can do a full analysis.
            # First, confirm that the rotors were turning at this time:
            gear_off_grounds = slices_and(gear_off_grounds, rtr.get_slices())

            # When did the radio altimeters indicate airborne?
            airs = slices_remove_small_gaps(
                np.ma.clump_unmasked(
                    np.ma.masked_less_equal(alt_agl.array, 1.0)),
                time_limit=AIRBORNE_THRESHOLD_TIME_RW,
                hz=alt_agl.frequency)
            # Both is a reliable indication of being in the air.
            for air in airs:
                for goff in gear_off_grounds:
                    # Providing they relate to each other :o)
                    if slices_overlap(air, goff):
                        start_index = max(air.start, goff.start)
                        end_index = min(air.stop, goff.stop)

                        better_begin = index_at_value(
                            alt_rad.array,
                            1.0,
                            _slice=slice(
                                max(start_index - 5 * alt_rad.frequency, 0),
                                start_index + 5 * alt_rad.frequency))
                        if better_begin:
                            begin = better_begin
                        else:
                            begin = start_index

                        better_end = index_at_value(
                            alt_rad.array,
                            1.0,
                            _slice=slice(
                                max(end_index + 5 * alt_rad.frequency, 0),
                                end_index - 5 * alt_rad.frequency, -1))
                        if better_end:
                            end = better_end
                        else:
                            end = end_index

                        duration = end - begin
                        if (duration /
                                alt_rad.hz) > AIRBORNE_THRESHOLD_TIME_RW:
                            self.create_phase(slice(begin, end))
        else:
            # During data validation we can select just sensible flights;
            # short hops make parameter validation tricky!
            self.create_phases(
                slices_remove_small_gaps(
                    slices_remove_small_slices(gear_off_grounds,
                                               time_limit=30)))
コード例 #27
0
 def hdf_getitem(self, key, **kwargs):
     if key == 'Airspeed':
         return Parameter('Airspeed',
                          array=airspeed_array,
                          frequency=airspeed_frequency)
     elif key == 'Frame Counter':
         return Parameter('Frame Counter',
                          array=dfc_array,
                          frequency=0.25)
     elif key == 'Heading':
         # TODO: Give heading specific data.
         return Parameter('Heading',
                          array=heading_array,
                          frequency=heading_frequency)
     elif key == 'Eng (1) N1' and eng_array is not None:
         return Parameter('Eng (1) N1',
                          array=eng_array,
                          frequency=eng_frequency)
     elif key == 'Segment Split':
         seg_split = M('Segment Split',
                       array=np.ma.zeros(len(heading_array), dtype=int),
                       frequency=heading_frequency,
                       values_mapping={
                           0: "-",
                           1: "Split"
                       })
         seg_split.array[390 / heading_frequency] = "Split"
         return seg_split
     else:
         raise KeyError
コード例 #28
0
    def test_derive(self):
        '''[[[State]]]
          0 = SL = 0 (Automatic)
          1 = SL = 1 (Standby)
          2 = SL = 2 (TA Only)
          3 = SL = 3
          ...
        '''
        values_mapping = {
            0: '0',
            1: '1',
        }
        tcas_sens = M('TCAS Sensitivity Level',
                      array=np.ma.array([0, 0, 1, 0, 0]),
                      values_mapping=values_mapping,
                      frequency=.25,
                      offset=0.)
        start = KTI(items=[
            KeyTimeInstance(index=2, name='TCAS RA Start'),
        ])
        node = tcas.TCASSensitivityAtTCASRAStart()
        node.derive(tcas_sens, start)

        expected = [
            KeyPointValue(index=2,
                          value=1,
                          name='TCAS RA Start Pilot Sensitivity Mode'),
        ]
        self.assertEqual(expected, node)
コード例 #29
0
 def derive(self,
            alt_aal=P('Altitude AAL'),
            alt_agl=P('Altitude AGL'),
            ac_type=A('Aircraft Type'),
            app=S('Approach And Landing'),
            hdg=P('Heading Continuous'),
            lat=P('Latitude Prepared'),
            lon=P('Longitude Prepared'),
            ils_loc=P('ILS Localizer'),
            ils_gs=S('ILS Glideslope'),
            ils_freq=P('ILS Frequency'),
            land_afr_apt=A('AFR Landing Airport'),
            land_afr_rwy=A('AFR Landing Runway'),
            lat_land=KPV('Latitude At Touchdown'),
            lon_land=KPV('Longitude At Touchdown'),
            precision=A('Precise Positioning'),
            fast=S('Fast'),
            u=P('Airspeed'),
            gspd=P('Groundspeed'),
            height_from_rig=P('Altitude ADH'),
            hdot=P('Vertical Speed'),
            roll=P('Roll'),
            heading=P('Heading'),
            distance_land=P('Distance To Landing'),
            tdwns=KTI('Touchdown'),
            offshore=M('Offshore'),
            takeoff=S('Takeoff')):
     pass
コード例 #30
0
 def test_derive(self):
     #based on FDS TestTCASRAWarningDuration()
     values_mapping = {
         0: 'A',
         1: 'B',
         2: 'Drop Track',
         3: 'Altitude Lost',
         4: 'Up Advisory Corrective',
         5: 'Down Advisory Corrective',
         6: 'G',
     }
     tcas = M(
         'TCAS Combined Control',
         array=np.ma.array([0, 1, 2, 3, 4, 5, 4, 5, 6]),
         values_mapping=values_mapping,
         frequency=1,
         offset=0,
     )
     #                                                                                       _,_, a,a,a,a,a,a,_
     #                                                                                                   u,d,u,d
     #                                                                                                3.5 4.5 5.5
     airborne = buildsection('Airborne', 2, 7)
     node = TCASRAStart()
     node.derive(
         tcas,
         airborne)  # create_ktis_on_state_change() offsets times by 0.5
     expected = [
         KeyTimeInstance(index=3.5, name='TCAS RA Start'),
     ]
     self.assertEqual(expected, node)