Exemplo n.º 1
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)))
    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)))
Exemplo n.º 3
0
 def derive(self, ra=M('TCAS RA'), off=KTI('Liftoff'), td=KTI('Touchdown') ):
     ras_local = ra.array
     ras_slices = library.runs_of_ones(ras_local)
     
     # put together runs separated by short drop-outs        
     ras_slicesb = library.slices_remove_small_gaps(ras_slices, time_limit=2, hz=1)        
     for ra_slice in ras_slicesb:                    
         is_post_liftoff =  (ra_slice.start - off.get_first().index) > 10 
         is_pre_touchdown = (td.get_first().index - ra_slice.start ) > 10 
         duration = ra_slice.stop-ra_slice.start                     
         if is_post_liftoff and is_pre_touchdown  and 3.0 <= duration < 300.0: #ignore if too short to do anything
             #print ' ra section', ra_slice
             self.create_phase( ra_slice )    
     return
Exemplo n.º 4
0
 def derive(self, ra=M('TCAS RA'), off=KTI('Liftoff'), td=KTI('Touchdown') ):
     ras_local = ra.array
     ras_slices = library.runs_of_ones(ras_local)
     
     # put together runs separated by short drop-outs        
     ras_slicesb = library.slices_remove_small_gaps(ras_slices, time_limit=2, hz=1)        
     for ra_slice in ras_slicesb:                    
         is_post_liftoff =  (ra_slice.start - off.get_first().index) > 10 
         is_pre_touchdown = (td.get_first().index - ra_slice.start ) > 10 
         duration = ra_slice.stop-ra_slice.start                     
         if is_post_liftoff and is_pre_touchdown  and 3.0 <= duration < 300.0: #ignore if too short to do anything
             #print ' ra section', ra_slice
             self.create_phase( ra_slice )    
     return
 def derive(self,
            gl=M('Gear (L) Up'),
            gn=M('Gear (N) Up'),
            gr=M('Gear (R) Up'),
            gc=M('Gear (C) Up')):
     # Join all available gear parameters and use whichever are available.
     self.array = vstack_params_where_state(
         (gl, 'Up'),
         (gn, 'Up'),
         (gr, 'Up'),
         (gc, 'Up'),
     ).all(axis=0)
     # remove any spikes
     _slices = runs_of_ones(self.array == 'Down')
     _slices = slices_remove_small_gaps(_slices, 2, self.hz)
     for _slice in _slices:
         self.array[_slice] = 'Down'
def _split_on_eng_params(slice_start_secs, slice_stop_secs, split_params_min,
                         split_params_frequency):
    '''
    Find split using engine parameters.

    :param slice_start_secs: Start of slow slice in seconds.
    :type slice_start_secs: int or float
    :param slice_stop_secs: Stop of slow slice in seconds.
    :type slice_stop_secs: int or float
    :param split_params_min: Minimum of engine parameters.
    :type split_params_min: np.ma.MaskedArray
    :param split_params_frequency: Frequency of split_params_min.
    :type split_params_frequency: int or float
    :returns: Split index in seconds and value of split_params_min at this
        index.
    :rtype: (int or float, int or float)
    '''
    slice_start = slice_start_secs * split_params_frequency
    slice_stop = slice_stop_secs * split_params_frequency
    split_params_slice = slice(np.round(slice_start, 0),
                               np.round(slice_stop, 0))
    split_index, split_value = min_value(split_params_min,
                                         _slice=split_params_slice)

    if split_index is None:
        return split_index, split_value

    eng_min_slices = slices_remove_small_slices(slices_remove_small_gaps(
        runs_of_ones(split_params_min[split_params_slice] == split_value),
        time_limit=60,
        hz=split_params_frequency),
                                                hz=split_params_frequency)

    if not eng_min_slices:
        return split_index, split_value

    split_index = eng_min_slices[0].start + \
        ((eng_min_slices[0].stop - eng_min_slices[0].start) / 2) + slice_start
    split_index = round(split_index / split_params_frequency)
    return split_index, split_value
def _segment_type_and_slice(speed_array, speed_frequency, heading_array,
                            heading_frequency, start, stop, eng_arrays,
                            aircraft_info, thresholds, hdf):
    """
    Uses the Heading to determine whether the aircraft moved about at all and
    the airspeed to determine if it was a full or partial flight.

    NO_MOVEMENT: When the aircraft is in the hanger,
    the altitude and airspeed can be tested and record values which look like
    the aircraft is in flight; however the aircraft is rarely moved and the
    heading sensor is a good indication that this is a hanger test.

    GROUND_ONLY: If the heading changed, the airspeed needs to have been above
    the threshold speed for flight for a minimum amount of time, currently 3
    minutes to determine. If not, this segment is identified as GROUND_ONLY,
    probably taxiing, repositioning on the ground or a rejected takeoff.

    START_ONLY: If the airspeed started slow but ended fast, we had a partial
    segment for the start of a flight.

    STOP_ONLY:  If the airspeed started fast but ended slow, we had a partial
    segment for the end of a flight.

    MID_FLIGHT: The airspeed started and ended fast - no takeoff or landing!

    START_AND_STOP: The airspeed started and ended slow, implying a complete
    flight.

    segment_type is one of:
    * 'NO_MOVEMENT' (didn't change heading)
    * 'GROUND_ONLY' (didn't go fast)
    * 'START_AND_STOP'
    * 'START_ONLY'
    * 'STOP_ONLY'
    * 'MID_FLIGHT'
    """

    speed_start = start * speed_frequency
    speed_stop = stop * speed_frequency
    speed_array = speed_array[speed_start:speed_stop]

    heading_start = start * heading_frequency
    heading_stop = stop * heading_frequency
    heading_array = heading_array[heading_start:heading_stop]

    # remove small gaps between valid data, e.g. brief data spikes
    unmasked_slices = slices_remove_small_gaps(
        np.ma.clump_unmasked(speed_array), 2, speed_frequency)
    # remove small slices to find 'consistent' valid data
    unmasked_slices = slices_remove_small_slices(unmasked_slices, 40,
                                                 speed_frequency)

    if unmasked_slices:
        # Check speed
        slow_start = speed_array[
            unmasked_slices[0].start] < thresholds['speed_threshold']
        slow_stop = speed_array[unmasked_slices[-1].stop -
                                1] < thresholds['speed_threshold']
        threshold_exceedance = np.ma.sum(
            speed_array > thresholds['speed_threshold']) / speed_frequency
        fast_for_long = threshold_exceedance > thresholds['min_duration']
    else:
        slow_start = slow_stop = fast_for_long = None

    # Find out if the aircraft moved
    if aircraft_info and aircraft_info['Aircraft Type'] == 'helicopter':
        # if any gear params use them
        gog = next(
            iter([
                hdf.get(name)
                for name in ('Gear On Ground', 'Gear (R) On Ground',
                             'Gear (L) On Ground')
            ]))
        if gog:
            gog_start_idx = start * gog.frequency
            gog_stop_idx = stop * gog.frequency
            gog_samples = 120 * gog.frequency
            gog_start = closest_unmasked_value(gog.array, gog_start_idx,
                                               gog_start_idx - gog_samples,
                                               gog_start_idx + gog_samples)
            gog_stop = closest_unmasked_value(gog.array, gog_stop_idx,
                                              gog_stop_idx - gog_samples,
                                              gog_stop_idx + gog_samples)
            if gog_start is not None and gog_stop is not None:
                # Use Gear on Ground rather than rotor speed as rotors may be
                # 90+% at beginning or end of segment.
                slow_start = (gog_start.value == 'Ground')
                slow_stop = (gog_stop.value == 'Ground')
            temp = np.ma.array(gog.array[gog_start_idx:gog_stop_idx].data,
                               mask=gog.array[gog_start_idx:gog_stop_idx].mask)
            gog_test = np.ma.masked_less(temp, 1.0)
            # We have seeen 12-second spurious gog='Air' signals during rotor rundown. Hence increased limit.
            did_move = slices_remove_small_slices(np.ma.clump_masked(gog_test),
                                                  time_limit=30,
                                                  hz=gog.frequency)
        else:
            hdiff = np.ma.abs(np.ma.diff(heading_array)).sum()
            did_move = hdiff > settings.HEADING_CHANGE_TAXI_THRESHOLD
    else:
        # Check Heading change for fixed wing.
        if eng_arrays is not None:
            heading_array = np.ma.masked_where(
                eng_arrays[heading_start:heading_stop] <
                settings.MIN_FAN_RUNNING, heading_array)
        hdiff = np.ma.abs(np.ma.diff(heading_array)).sum()
        did_move = hdiff > settings.HEADING_CHANGE_TAXI_THRESHOLD

    if not did_move or (not fast_for_long and eng_arrays is None):
        # added check for not fast for long and no engine params to avoid
        # lots of Herc ground runs
        logger.debug("Aircraft did not move.")
        segment_type = 'NO_MOVEMENT'
        # e.g. hanger tests, esp. if speed changes!
    elif slow_start and slow_stop and fast_for_long:
        logger.debug(
            "speed started below threshold, rose above and stopped below.")
        segment_type = 'START_AND_STOP'
    elif slow_start and threshold_exceedance:
        logger.debug("speed started below threshold and stopped above.")
        segment_type = 'START_ONLY'
    elif slow_stop and threshold_exceedance:
        logger.debug("speed started above threshold and stopped below.")
        segment_type = 'STOP_ONLY'
    elif not fast_for_long:
        logger.debug("speed was below threshold.")
        segment_type = 'GROUND_ONLY'  # e.g. RTO, re-positioning A/C
        #Q: report a go_fast?
    else:
        logger.debug("speed started and stopped above threshold.")
        segment_type = 'MID_FLIGHT'
    logger.info("Segment type is '%s' between '%s' and '%s'.", segment_type,
                start, stop)

    # ARINC 717 data has frames or superframes. ARINC 767 will be split
    # on a minimum boundary of 4 seconds for the analyser.
    boundary = 64 if hdf.superframe_present else 4
    segment = slice(start, stop)

    supf_start_secs, supf_stop_secs, array_start_secs, array_stop_secs = segment_boundaries(
        segment, boundary)

    start_padding = segment.start - supf_start_secs

    return segment_type, segment, array_start_secs
Exemplo n.º 8
0
def _segment_type_and_slice(speed_array, speed_frequency,
                            heading_array, heading_frequency,
                            start, stop, eng_arrays,
                            aircraft_info, thresholds, hdf):
    """
    Uses the Heading to determine whether the aircraft moved about at all and
    the airspeed to determine if it was a full or partial flight.

    NO_MOVEMENT: When the aircraft is in the hanger,
    the altitude and airspeed can be tested and record values which look like
    the aircraft is in flight; however the aircraft is rarely moved and the
    heading sensor is a good indication that this is a hanger test.

    GROUND_ONLY: If the heading changed, the airspeed needs to have been above
    the threshold speed for flight for a minimum amount of time, currently 3
    minutes to determine. If not, this segment is identified as GROUND_ONLY,
    probably taxiing, repositioning on the ground or a rejected takeoff.

    START_ONLY: If the airspeed started slow but ended fast, we had a partial
    segment for the start of a flight.

    STOP_ONLY:  If the airspeed started fast but ended slow, we had a partial
    segment for the end of a flight.

    MID_FLIGHT: The airspeed started and ended fast - no takeoff or landing!

    START_AND_STOP: The airspeed started and ended slow, implying a complete
    flight.

    segment_type is one of:
    * 'NO_MOVEMENT' (didn't change heading)
    * 'GROUND_ONLY' (didn't go fast)
    * 'START_AND_STOP'
    * 'START_ONLY'
    * 'STOP_ONLY'
    * 'MID_FLIGHT'
    """

    speed_start = start * speed_frequency
    speed_stop = stop * speed_frequency
    speed_array = speed_array[speed_start:speed_stop]

    heading_start = start * heading_frequency
    heading_stop = stop * heading_frequency
    heading_array = heading_array[heading_start:heading_stop]

    # remove small gaps between valid data, e.g. brief data spikes
    unmasked_slices = slices_remove_small_gaps(
        np.ma.clump_unmasked(speed_array), 2, speed_frequency)
    # remove small slices to find 'consistent' valid data
    unmasked_slices = slices_remove_small_slices(
        unmasked_slices, 25, speed_frequency)

    if unmasked_slices:
        # Check speed
        slow_start = speed_array[unmasked_slices[0].start] < thresholds['speed_threshold']
        slow_stop = speed_array[unmasked_slices[-1].stop - 1] < thresholds['speed_threshold']
        threshold_exceedance = np.ma.sum(
            speed_array > thresholds['speed_threshold']) / speed_frequency
        fast_for_long = threshold_exceedance > thresholds['min_duration']
    else:
        slow_start = slow_stop = fast_for_long = None

    # Find out if the aircraft moved
    if aircraft_info and aircraft_info['Aircraft Type'] == 'helicopter':
        # if any gear params use them
        gog = next(iter([hdf.get(name) for name in ('Gear On Ground', 'Gear (R) On Ground', 'Gear (L) On Ground')]))
        if gog:
            gog_start = start * gog.frequency
            gog_stop = stop * gog.frequency
            temp = np.ma.array(gog.array[gog_start:gog_stop].data, mask=gog.array[gog_start:gog_stop].mask)
            gog_test = np.ma.masked_less(temp, 1.0)
            # We have seeen 12-second spurious gog='Air' signals during rotor rundown. Hence increased limit.
            did_move = slices_remove_small_slices(np.ma.clump_masked(gog_test),
                                                  time_limit=30, hz=gog.frequency)
        else:
            hdiff = np.ma.abs(np.ma.diff(heading_array)).sum()
            did_move = hdiff > settings.HEADING_CHANGE_TAXI_THRESHOLD
    else:
        # Check Heading change for fixed wing.
        if eng_arrays is not None:
            heading_array = np.ma.masked_where(eng_arrays[heading_start:heading_stop] < settings.MIN_FAN_RUNNING, heading_array)
        hdiff = np.ma.abs(np.ma.diff(heading_array)).sum()
        did_move = hdiff > settings.HEADING_CHANGE_TAXI_THRESHOLD

    if not did_move or (not fast_for_long and eng_arrays is None):
        # added check for not fast for long and no engine params to avoid
        # lots of Herc ground runs
        logger.debug("Aircraft did not move.")
        segment_type = 'NO_MOVEMENT'
        # e.g. hanger tests, esp. if speed changes!
    elif not fast_for_long:
        logger.debug("speed was below threshold.")
        segment_type = 'GROUND_ONLY'  # e.g. RTO, re-positioning A/C
        #Q: report a go_fast?
    elif slow_start and slow_stop:
        logger.debug(
            "speed started below threshold, rose above and stopped below.")
        segment_type = 'START_AND_STOP'
    elif slow_start:
        logger.debug("speed started below threshold and stopped above.")
        segment_type = 'START_ONLY'
    elif slow_stop:
        logger.debug("speed started above threshold and stopped below.")
        segment_type = 'STOP_ONLY'
    else:
        logger.debug("speed started and stopped above threshold.")
        segment_type = 'MID_FLIGHT'
    logger.info("Segment type is '%s' between '%s' and '%s'.",
                segment_type, start, stop)

    # ARINC 717 data has frames or superframes. ARINC 767 will be split
    # on a minimum boundary of 4 seconds for the analyser.
    boundary = 64 if hdf.superframe_present else 4
    segment = slice(start, stop)

    supf_start_secs, supf_stop_secs, array_start_secs, array_stop_secs = segment_boundaries(segment, boundary)

    start_padding = segment.start - supf_start_secs

    return segment_type, segment, array_start_secs