def derive(self,
               src_1=P('Latitude (1)'),
               src_2=P('Latitude (2)'),
               src_3=P('Latitude (3)')):

        sources = [
            source for source in [src_1, src_2, src_3] if source is not None \
            and np.count_nonzero(source.array) > len(source.array)/2
        ]

        if len(sources) == 1:
            self.offset = sources[0].offset
            self.frequency = sources[0].frequency
            self.array = sources[0].array

        elif len(sources) == 2:
            self.array, self.frequency, self.offset = blend_two_parameters(
                sources[0], sources[1])

        elif len(sources) > 2:
            self.offset = 0.0
            self.frequency = 1.0
            self.array = blend_parameters(sources,
                                          offset=self.offset,
                                          frequency=self.frequency)
        else:
            self.array = np_ma_masked_zeros_like(src_1.array)
def check_derived_array(param_name, result, duration):
    # check that the right number of results were returned
    # Allow a small tolerance. For example if duration in seconds
    # is 2822, then there will be an array length of  1411 at 0.5Hz and 706
    # at 0.25Hz (rounded upwards). If we combine two 0.25Hz
    # parameters then we will have an array length of 1412.
    expected_length = duration * result.frequency
    if result.array is None:
        logger.debug("No array set; creating a fully masked array for %s",
                     param_name)
        array_length = expected_length
        # Where a parameter is wholly masked, we fill the HDF
        # file with masked zeros to maintain structure.
        result.array = \
            np_ma_masked_zeros_like(np.ma.arange(expected_length))
    else:
        array_length = len(result.array)
    length_diff = array_length - expected_length
    if length_diff == 0:
        pass
    elif 0 < length_diff < 5:
        logger.debug(
            "Cutting excess data for parameter '%s'. Expected length was '%s' while resulting "
            "array length was '%s'.", param_name, expected_length,
            len(result.array))
        result.array = result.array[:expected_length]
    else:
        #pdb.set_trace()
        raise ValueError("Array length mismatch for parameter "
                         "'%s'. Expected '%s', resulting array length '%s'." %
                         (param_name, expected_length, array_length))
    return result
    def derive(self,
               src_1=P('Longitude (1)'),
               src_2=P('Longitude (2)'),
               src_3=P('Longitude (3)')):

        sources = [
            source for source in [src_1, src_2, src_3] if source is not None \
            and np.count_nonzero(source.array) > len(source.array)/2
        ]
        if len(sources) > 1:
            for source in sources:
                source.array = repair_mask(
                    straighten_longitude(source.array) + 180.0)

        if len(sources) == 1:
            self.offset = sources[0].offset
            self.frequency = sources[0].frequency
            self.array = sources[0].array

        elif len(sources) == 2:
            blended, self.frequency, self.offset = blend_two_parameters(
                sources[0], sources[1])
            self.array = blended % 360 - 180.0

        elif len(sources) > 2:
            self.offset = 0.0
            self.frequency = 1.0
            blended = blend_parameters(sources,
                                       offset=self.offset,
                                       frequency=self.frequency)
            self.array = blended % 360 - 180.0

        else:
            self.array = np_ma_masked_zeros_like(src_1.array)
 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)
Esempio n. 5
0
        def seek_deck(rad, hdot, min_idx, rad_hz):
            def one_direction(rad, hdot, sence, rad_hz):
                # Stairway to Heaven is getting a bit old. Getting with the times?
                b_diffs = hdot / 60
                r_diffs = np.ma.ediff1d(rad, to_begin=b_diffs[0])
                diffs = np.ma.where(
                    np.ma.abs(r_diffs - b_diffs) > 6.0, b_diffs, r_diffs)
                height = integrate(diffs,
                                   frequency=rad_hz,
                                   direction=sence,
                                   repair=False)
                return height

            height_from_rig = np_ma_masked_zeros_like(rad)
            if len(rad[:min_idx]) > 0:
                height_from_rig[:min_idx] = one_direction(
                    rad[:min_idx], hdot[:min_idx], "backwards", rad_hz)
            if len(rad[min_idx:]) > 0:
                height_from_rig[min_idx:] = one_direction(
                    rad[min_idx:], hdot[min_idx:], "forwards", rad_hz)
            '''
            # And we are bound to want to know the rig height somewhere, so here's how to work that out.
            rig_height = rad[0]-height_from_rig[0]
            # I checked this and it seems pretty consistent.
            # See Library\Projects\Helicopter FDM\Algorithm Development\Rig height estimates from Bond initial test data.xlsx
            #lat=hdf['Latitude'].array[app_slice][-1]
            #lon=hdf['Longitude'].array[app_slice][-1]
            #print(lat, lon, rig_height)
            '''
            return height_from_rig
Esempio n. 6
0
def manage_parameter_length(param_name, duration, result):
	# check that the right number of results were returned
	# Allow a small tolerance. For example if duration in seconds
	# is 2822, then there will be an array length of  1411 at 0.5Hz and 706
	# at 0.25Hz (rounded upwards). If we combine two 0.25Hz
	# parameters then we will have an array length of 1412.
	expected_length = duration * result.frequency
	if result.array is None:
		# Where a parameter is wholly masked, fill the HDF file with masked zeros to maintain structure.
		array_length = expected_length
		result.array = np_ma_masked_zeros_like(np.ma.arange(expected_length))
	else:
		array_length = len(result.array)
		
	length_diff = array_length - expected_length
	if length_diff == 0:
		pass
	elif 0 < length_diff < 5:
		logger.warning("Cutting excess data for parameter '%s'. Expected length was '%s' while resulting "
					   "array length was '%s'.", param_name, expected_length, len(result.array))
		result.array = result.array[:expected_length]
	else:
		raise ValueError("Array length mismatch for parameter '%s'. Expected '%s', resulting array "
						 "length '%s'." % (param_name, expected_length, array_length))            	
	return result
Esempio n. 7
0
    def derive(
            self,
            rad=P('Altitude Radio'),
            hdot=P('Vertical Speed'),
    ):
        def seek_deck(rad, hdot, min_idx, rad_hz):
            def one_direction(rad, hdot, sence, rad_hz):
                # Stairway to Heaven is getting a bit old. Getting with the times?
                b_diffs = hdot / 60
                r_diffs = np.ma.ediff1d(rad, to_begin=b_diffs[0])
                diffs = np.ma.where(
                    np.ma.abs(r_diffs - b_diffs) > 6.0, b_diffs, r_diffs)
                height = integrate(diffs,
                                   frequency=rad_hz,
                                   direction=sence,
                                   repair=False)
                return height

            height_from_rig = np_ma_masked_zeros_like(rad)
            if len(rad[:min_idx]) > 0:
                height_from_rig[:min_idx] = one_direction(
                    rad[:min_idx], hdot[:min_idx], "backwards", rad_hz)
            if len(rad[min_idx:]) > 0:
                height_from_rig[min_idx:] = one_direction(
                    rad[min_idx:], hdot[min_idx:], "forwards", rad_hz)
            '''
            # And we are bound to want to know the rig height somewhere, so here's how to work that out.
            rig_height = rad[0]-height_from_rig[0]
            # I checked this and it seems pretty consistent.
            # See Library\Projects\Helicopter FDM\Algorithm Development\Rig height estimates from Bond initial test data.xlsx
            #lat=hdf['Latitude'].array[app_slice][-1]
            #lon=hdf['Longitude'].array[app_slice][-1]
            #print(lat, lon, rig_height)
            '''
            return height_from_rig

        self.array = np_ma_masked_zeros_like(rad.array)
        rad_peak_idxs, rad_peak_vals = cycle_finder(rad.array, min_step=150.0)

        if len(rad_peak_idxs) < 4:
            return

        slice_idxs = zip(rad_peak_idxs[:-2], rad_peak_idxs[1:-1],
                         rad_peak_idxs[2:], rad_peak_vals[1:])
        for slice_idx in slice_idxs[1:-1]:
            this_deck_slice = slice(slice_idx[0] + 1, slice_idx[2] - 1)
            if slice_idx[3] > 5.0:
                # We didn't land in this period
                continue
            else:
                self.array[this_deck_slice] = seek_deck(
                    rad.array[this_deck_slice], hdot.array[this_deck_slice],
                    slice_idx[1] - slice_idx[0], rad.frequency)
                '''
    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
Esempio n. 9
0
    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
Esempio n. 10
0
        def seek_deck(rad, hdot, min_idx, rad_hz):
            def one_direction(rad, hdot, sence, rad_hz):
                # Stairway to Heaven is getting a bit old. Getting with the times?
                # Vertical Speed / 60 = Pressure alt V/S in feet per second
                b_diffs = hdot / 60

                # Rate of change on radalt array = Rad alt V/S in feet per second
                r_diffs = np.ma.ediff1d(rad * rad_hz, to_begin=b_diffs[0])

                # Difference between ROC greater than 6fps will mean flying over
                # the deck; use pressure alt roc when that happens and radio alt
                # roc in all other cases
                diffs = np.ma.where(
                    np.ma.abs(r_diffs - b_diffs) > 6.0 * rad_hz, b_diffs,
                    r_diffs)

                height = integrate(diffs,
                                   frequency=rad_hz,
                                   direction=sence,
                                   repair=False)
                return height

            height_from_rig = np_ma_masked_zeros_like(rad)
            if len(rad[:min_idx]) > 0:
                height_from_rig[:min_idx] = one_direction(
                    rad[:min_idx], hdot[:min_idx], "backwards", rad_hz)
            if len(rad[min_idx:]) > 0:
                height_from_rig[min_idx:] = one_direction(
                    rad[min_idx:], hdot[min_idx:], "forwards", rad_hz)
            '''
            # And we are bound to want to know the rig height somewhere, so here's how to work that out.
            rig_height = rad[0]-height_from_rig[0]
            # I checked this and it seems pretty consistent.
            # See Library\Projects\Helicopter FDM\Algorithm Development\Rig height estimates from Bond initial test data.xlsx
            #lat=hdf['Latitude'].array[app_slice][-1]
            #lon=hdf['Longitude'].array[app_slice][-1]
            #print(lat, lon, rig_height)
            '''
            return height_from_rig
        def seek_deck(rad, hdot, min_idx, rad_hz):

            def one_direction(rad, hdot, sence, rad_hz):
                # Stairway to Heaven is getting a bit old. Getting with the times?
                # Vertical Speed / 60 = Pressure alt V/S in feet per second
                b_diffs = hdot/60

                # Rate of change on radalt array = Rad alt V/S in feet per second
                r_diffs = np.ma.ediff1d(rad*rad_hz, to_begin=b_diffs[0])

                # Difference between ROC greater than 6fps will mean flying over
                # the deck; use pressure alt roc when that happens and radio alt
                # roc in all other cases
                diffs = np.ma.where(np.ma.abs(r_diffs-b_diffs)>6.0*rad_hz, b_diffs, r_diffs)

                height = integrate(diffs,
                                   frequency=rad_hz,
                                   direction=sence,
                                   repair=False)
                return height

            height_from_rig = np_ma_masked_zeros_like(rad)
            if len(rad[:min_idx]) > 0:
                height_from_rig[:min_idx] = one_direction(rad[:min_idx], hdot[:min_idx], "backwards", rad_hz)
            if len(rad[min_idx:]) > 0:
                height_from_rig[min_idx:] = one_direction(rad[min_idx:], hdot[min_idx:], "forwards", rad_hz)

            '''
            # And we are bound to want to know the rig height somewhere, so here's how to work that out.
            rig_height = rad[0]-height_from_rig[0]
            # I checked this and it seems pretty consistent.
            # See Library\Projects\Helicopter FDM\Algorithm Development\Rig height estimates from Bond initial test data.xlsx
            #lat=hdf['Latitude'].array[app_slice][-1]
            #lon=hdf['Longitude'].array[app_slice][-1]
            #print(lat, lon, rig_height)
            '''
            return height_from_rig
Esempio n. 12
0
    def derive(
        self,
        air_spd=P('Airspeed'),
        flap=P('Flap'),
        #conf=P('Configuration'),
        gw=P('Gross Weight Smoothed'),
        touchdowns=KTI('Touchdown'),
        series=A('Series'),
        family=A('Family'),
        engine=A('Engine Series'),
        engine_type=A('Engine Type'),
        eng_np=P('Eng (*) Np Avg'),
        vref=P('Vref'),
        afr_vref=A('AFR Vref'),
        approaches=S('Approach And Landing')):

        if vref:
            # Use recorded Vref parameter:
            self.array = vref.array
        elif afr_vref:
            # Use provided Vref from achieved flight record:
            afr_vspeed = afr_vref
            self.array = np.ma.zeros(len(air_spd.array), np.double)
            self.array.mask = True
            for approach in approaches:
                self.array[approach.slice] = afr_vspeed.value
        else:
            # Use speed card lookups

            x = map(lambda x: x.value
                    if x else None, (series, family, engine, engine_type))

            vspeed_class_test = get_vspeed_map_mitre(*x)

            if vspeed_class_test:
                vspeed_class = vspeed_class_test
            else:
                vspeed_class = get_vspeed_map(*x)

            if gw is not None:  # and you must have eng_np
                try:
                    # Allow up to 2 superframe values to be repaired:
                    # (64 * 2 = 128 + a bit)
                    repaired_gw = repair_mask(gw.array,
                                              repair_duration=130,
                                              copy=True,
                                              extrapolate=True)
                except:
                    self.warning(
                        "'Airspeed Reference' will be fully masked "
                        "because 'Gross Weight Smoothed' array could not be "
                        "repaired.")
                    return

                setting_param = flap  #or conf
                vspeed_table = vspeed_class()
                for approach in approaches:
                    _slice = approach.slice
                    '''TODO: Only uses max Vref setting, doesn't account for late config changes'''
                    index = np.ma.argmax(setting_param.array[_slice])
                    setting = setting_param.array[_slice][index]
                    weight = repaired_gw[_slice][
                        index] if gw is not None else None
                    if setting in vspeed_table.vref_settings:
                        ##and is_index_within_slice(touchdowns.get_last().index, _slice):
                        # setting in vspeed table:
                        vspeed = vspeed_table.vref(setting, weight)
                    else:
                        ''' Do not like the default of using max Vref for go arounds... '''
                        ## No landing and max setting not in vspeed table:
                        #if setting_param.name == 'Flap':
                        #setting = max(get_flap_map(series.value, family.value))
                        #else:
                        #setting = max(get_conf_map(series.value, family.value).keys())
                        #vspeed = vspeed_table.vref(setting, weight)
                        self.warning(
                            "'Airspeed Reference' will be fully masked "
                            "because Vref lookup table does not have corresponding values."
                        )
                        return
                    self.array = np_ma_masked_zeros_like(air_spd.array)
                    self.array[_slice] = vspeed
Esempio n. 13
0
    def derive(self,
               air_spd=P('Airspeed'),
               flap=P('Flap'),
               #conf=P('Configuration'),
               gw=P('Gross Weight Smoothed'),
               touchdowns=KTI('Touchdown'),
               series=A('Series'),
               family=A('Family'),
               engine=A('Engine Series'),
               engine_type=A('Engine Type'),
               eng_np=P('Eng (*) Np Avg'),
               vref=P('Vref'),
               afr_vref=A('AFR Vref'),
               approaches=S('Approach And Landing')):

        if vref:
            # Use recorded Vref parameter:
            self.array=vref.array
        elif afr_vref:
            # Use provided Vref from achieved flight record:
            afr_vspeed = afr_vref
            self.array = np.ma.zeros(len(air_spd.array), np.double)
            self.array.mask = True
            for approach in approaches:
                self.array[approach.slice] = afr_vspeed.value
        else:
            # Use speed card lookups
            

            x = map(lambda x: x.value if x else None, (series, family, engine, engine_type))

            vspeed_class_test = get_vspeed_map_mitre(*x)
            
            if vspeed_class_test:
                vspeed_class = vspeed_class_test
            else:
                vspeed_class = get_vspeed_map(*x)

                        
            
            
            if gw is not None:  # and you must have eng_np
                try:
                    # Allow up to 2 superframe values to be repaired:
                    # (64 * 2 = 128 + a bit)
                    repaired_gw = repair_mask(gw.array, repair_duration=130,
                                              copy=True, extrapolate=True)
                except:
                    self.warning("'Airspeed Reference' will be fully masked "
                                 "because 'Gross Weight Smoothed' array could not be "
                                 "repaired.")
                    return

                setting_param = flap #or conf
                vspeed_table = vspeed_class()
                for approach in approaches:
                    _slice = approach.slice
                    '''TODO: Only uses max Vref setting, doesn't account for late config changes'''
                    index = np.ma.argmax(setting_param.array[_slice])
                    setting = setting_param.array[_slice][index]
                    weight = repaired_gw[_slice][index] if gw is not None else None
                    if setting in vspeed_table.vref_settings:
                       ##and is_index_within_slice(touchdowns.get_last().index, _slice):
                        # setting in vspeed table:
                        vspeed = vspeed_table.vref(setting, weight)
                    else:
                        ''' Do not like the default of using max Vref for go arounds... '''
                        ## No landing and max setting not in vspeed table:
                        #if setting_param.name == 'Flap':
                            #setting = max(get_flap_map(series.value, family.value))
                        #else:
                            #setting = max(get_conf_map(series.value, family.value).keys())
                            #vspeed = vspeed_table.vref(setting, weight)
                        self.warning("'Airspeed Reference' will be fully masked "
                                 "because Vref lookup table does not have corresponding values.")
                        return
                    self.array = np_ma_masked_zeros_like(air_spd.array)
                    self.array[_slice] = vspeed
    def derive(self, rad=P('Altitude Radio'),
               hdot=P('Vertical Speed'),
               ):

        def seek_deck(rad, hdot, min_idx, rad_hz):

            def one_direction(rad, hdot, sence, rad_hz):
                # Stairway to Heaven is getting a bit old. Getting with the times?
                # Vertical Speed / 60 = Pressure alt V/S in feet per second
                b_diffs = hdot/60

                # Rate of change on radalt array = Rad alt V/S in feet per second
                r_diffs = np.ma.ediff1d(rad*rad_hz, to_begin=b_diffs[0])

                # Difference between ROC greater than 6fps will mean flying over
                # the deck; use pressure alt roc when that happens and radio alt
                # roc in all other cases
                diffs = np.ma.where(np.ma.abs(r_diffs-b_diffs)>6.0*rad_hz, b_diffs, r_diffs)

                height = integrate(diffs,
                                   frequency=rad_hz,
                                   direction=sence,
                                   repair=False)
                return height

            height_from_rig = np_ma_masked_zeros_like(rad)
            if len(rad[:min_idx]) > 0:
                height_from_rig[:min_idx] = one_direction(rad[:min_idx], hdot[:min_idx], "backwards", rad_hz)
            if len(rad[min_idx:]) > 0:
                height_from_rig[min_idx:] = one_direction(rad[min_idx:], hdot[min_idx:], "forwards", rad_hz)

            '''
            # And we are bound to want to know the rig height somewhere, so here's how to work that out.
            rig_height = rad[0]-height_from_rig[0]
            # I checked this and it seems pretty consistent.
            # See Library\Projects\Helicopter FDM\Algorithm Development\Rig height estimates from Bond initial test data.xlsx
            #lat=hdf['Latitude'].array[app_slice][-1]
            #lon=hdf['Longitude'].array[app_slice][-1]
            #print(lat, lon, rig_height)
            '''
            return height_from_rig

        # Prepare a masked array filled with zeros for the parameter (same length as radalt array)
        self.array = np_ma_masked_zeros_like(rad.array)
        rad_peak_idxs, rad_peak_vals = cycle_finder(rad.array, min_step=150.0)


        if len(rad_peak_idxs)<4:
            return

        slice_idxs = list(zip(rad_peak_idxs[:-2], rad_peak_idxs[1:-1],
                              rad_peak_idxs[2:], rad_peak_vals[1:]))
        for slice_idx in slice_idxs[1:-1]:
            this_deck_slice = slice(slice_idx[0]+1, slice_idx[2]-1)
            if slice_idx[3] > 5.0:
                # We didn't land in this period
                continue
            else:
                self.array[this_deck_slice] = seek_deck(rad.array[this_deck_slice],
                                                        hdot.array[this_deck_slice],
                                                        slice_idx[1]-slice_idx[0],
                                                        rad.frequency)
                '''
def derive_parameters(hdf, node_mgr, process_order):
    '''
    Derives parameters in process_order. Dependencies are sourced via the
    node_mgr.

    :param hdf: Data file accessor used to get and save parameter data and
        attributes
    :type hdf: hdf_file
    :param node_mgr: Used to determine the type of node in the process_order
    :type node_mgr: NodeManager
    :param process_order: Parameter / Node class names in the required order to
        be processed
    :type process_order: list of strings
    '''
    # store all derived params that aren't masked arrays
    params = {}
    approach_list = ApproachNode(restrict_names=False)
    # duplicate storage, but maintaining types
    kpv_list = KeyPointValueNode(restrict_names=False)
    kti_list = KeyTimeInstanceNode(restrict_names=False)
    # 'Node Name' : node()  pass in node.get_accessor()
    section_list = SectionNode()
    flight_attrs = []
    duration = hdf.duration

    for param_name in process_order:
        if param_name in node_mgr.hdf_keys:
            continue

        elif node_mgr.get_attribute(param_name) is not None:
            # add attribute to dictionary of available params
            ###params[param_name] = node_mgr.get_attribute(param_name)
            #TODO: optimise with only one call to get_attribute
            continue

        #NB raises KeyError if Node is "unknown"
        node_class = node_mgr.derived_nodes[param_name]

        # build ordered dependencies
        deps = []
        node_deps = node_class.get_dependency_names()
        for dep_name in node_deps:
            if dep_name in params:  # already calculated KPV/KTI/Phase
                deps.append(params[dep_name])
            elif node_mgr.get_attribute(dep_name) is not None:
                deps.append(node_mgr.get_attribute(dep_name))
            elif dep_name in node_mgr.hdf_keys:
                # LFL/Derived parameter
                # all parameters (LFL or other) need get_aligned which is
                # available on DerivedParameterNode
                try:
                    dp = derived_param_from_hdf(
                        hdf.get_param(dep_name, valid_only=True))
                except KeyError:
                    # Parameter is invalid.
                    dp = None
                deps.append(dp)
            else:  # dependency not available
                deps.append(None)
        if all([d is None for d in deps]):
            raise RuntimeError("No dependencies available - Nodes cannot "
                               "operate without ANY dependencies available! "
                               "Node: %s" % node_class.__name__)

        # initialise node
        node = node_class()
        # shhh, secret accessors for developing nodes in debug mode
        node._p = params
        node._h = hdf
        node._n = node_mgr
        logger.info("Processing %s `%s`", get_node_type(node), param_name)
        # Derive the resulting value

        result = node.get_derived(deps)
        del node._p
        del node._h
        del node._n

        if node.node_type is KeyPointValueNode:
            #Q: track node instead of result here??
            params[param_name] = result
            for one_hz in result.get_aligned(P(frequency=1, offset=0)):
                if not (0 <= one_hz.index <= duration + 4):
                    raise IndexError(
                        "KPV '%s' index %.2f is not between 0 and %d" %
                        (one_hz.name, one_hz.index, duration))
                kpv_list.append(one_hz)
        elif node.node_type is KeyTimeInstanceNode:
            params[param_name] = result
            for one_hz in result.get_aligned(P(frequency=1, offset=0)):
                if not (0 <= one_hz.index <= duration + 4):
                    raise IndexError(
                        "KTI '%s' index %.2f is not between 0 and %d" %
                        (one_hz.name, one_hz.index, duration))
                kti_list.append(one_hz)
        elif node.node_type is FlightAttributeNode:
            params[param_name] = result
            try:
                # only has one Attribute result
                flight_attrs.append(Attribute(result.name, result.value))
            except:
                logger.warning(
                    "Flight Attribute Node '%s' returned empty "
                    "handed.", param_name)
        elif issubclass(node.node_type, SectionNode):
            aligned_section = result.get_aligned(P(frequency=1, offset=0))
            for index, one_hz in enumerate(aligned_section):
                # SectionNodes allow slice starts and stops being None which
                # signifies the beginning and end of the data. To avoid
                # TypeErrors in subsequent derive methods which perform
                # arithmetic on section slice start and stops, replace with 0
                # or hdf.duration.
                fallback = lambda x, y: x if x is not None else y

                duration = fallback(duration, 0)

                start = fallback(one_hz.slice.start, 0)
                stop = fallback(one_hz.slice.stop, duration)
                start_edge = fallback(one_hz.start_edge, 0)
                stop_edge = fallback(one_hz.stop_edge, duration)

                slice_ = slice(start, stop)
                one_hz = Section(one_hz.name, slice_, start_edge, stop_edge)
                aligned_section[index] = one_hz

                if not (0 <= start <= duration and 0 <= stop <= duration + 4):
                    msg = "Section '%s' (%.2f, %.2f) not between 0 and %d"
                    raise IndexError(msg %
                                     (one_hz.name, start, stop, duration))
                if not 0 <= start_edge <= duration:
                    msg = "Section '%s' start_edge (%.2f) not between 0 and %d"
                    raise IndexError(msg % (one_hz.name, start_edge, duration))
                if not 0 <= stop_edge <= duration + 4:
                    msg = "Section '%s' stop_edge (%.2f) not between 0 and %d"
                    raise IndexError(msg % (one_hz.name, stop_edge, duration))
                section_list.append(one_hz)
            params[param_name] = aligned_section
        elif issubclass(node.node_type, DerivedParameterNode):
            if duration:
                # check that the right number of results were returned Allow a
                # small tolerance. For example if duration in seconds is 2822,
                # then there will be an array length of  1411 at 0.5Hz and 706
                # at 0.25Hz (rounded upwards). If we combine two 0.25Hz
                # parameters then we will have an array length of 1412.
                expected_length = duration * result.frequency
                if result.array is None:
                    logger.warning(
                        "No array set; creating a fully masked "
                        "array for %s", param_name)
                    array_length = expected_length
                    # Where a parameter is wholly masked, we fill the HDF
                    # file with masked zeros to maintain structure.
                    result.array = \
                        np_ma_masked_zeros_like(np.ma.arange(expected_length))
                else:
                    array_length = len(result.array)
                length_diff = array_length - expected_length
                if length_diff == 0:
                    pass
                elif 0 < length_diff < 5:
                    logger.warning(
                        "Cutting excess data for parameter '%s'. "
                        "Expected length was '%s' while resulting "
                        "array length was '%s'.", param_name, expected_length,
                        len(result.array))
                    result.array = result.array[:expected_length]
                else:
                    raise ValueError(
                        "Array length mismatch for parameter "
                        "'%s'. Expected '%s', resulting array "
                        "length '%s'." %
                        (param_name, expected_length, array_length))

            hdf.set_param(result)
            # Keep hdf_keys up to date.
            node_mgr.hdf_keys.append(param_name)
        elif issubclass(node.node_type, ApproachNode):
            aligned_approach = result.get_aligned(P(frequency=1, offset=0))
            for approach in aligned_approach:
                # Does not allow slice start or stops to be None.
                valid_turnoff = (not approach.turnoff
                                 or (0 <= approach.turnoff <= duration))
                valid_slice = ((0 <= approach.slice.start <= duration)
                               and (0 <= approach.slice.stop <= duration))
                valid_gs_est = (not approach.gs_est or
                                ((0 <= approach.gs_est.start <= duration) and
                                 (0 <= approach.gs_est.stop <= duration)))
                valid_loc_est = (not approach.loc_est or
                                 ((0 <= approach.loc_est.start <= duration) and
                                  (0 <= approach.loc_est.stop <= duration)))
                if not all(
                    [valid_turnoff, valid_slice, valid_gs_est, valid_loc_est]):
                    raise ValueError('ApproachItem contains index outside of '
                                     'flight data: %s' % approach)
                approach_list.append(approach)
            params[param_name] = aligned_approach
        else:
            raise NotImplementedError("Unknown Type %s" % node.__class__)
        continue
    return kti_list, kpv_list, section_list, approach_list, flight_attrs
Esempio n. 16
0
def derive_parameters_mitre(hdf, node_mgr, process_order, precomputed_parameters={}):
    '''
    Derives the parameter values and if limits are available, applies
    parameter validation upon each param before storing the resulting masked
    array back into the hdf file.
    
    :param hdf: Data file accessor used to get and save parameter data and attributes
    :type hdf: hdf_file
    :param node_mgr: Used to determine the type of node in the process_order
    :type node_mgr: NodeManager
    :param process_order: Parameter / Node class names in the required order to be processed
    :type process_order: list of strings
    '''
    params    = precomputed_parameters   # dictionary of derived params that aren't masked arrays

    approach_list = ApproachNode(restrict_names=False)
    kpv_list = KeyPointValueNode(restrict_names=False) # duplicate storage, but maintaining types
    kti_list = KeyTimeInstanceNode(restrict_names=False)
    section_list = SectionNode()  # 'Node Name' : node()  pass in node.get_accessor()
    flight_attrs = []
    duration = hdf.duration
    
    for param_name in process_order:
        if param_name in node_mgr.hdf_keys:
            logger.debug('  derive_: hdf '+param_name)            
            continue        
        elif node_mgr.get_attribute(param_name) is not None:
            logger.debug('  derive_: get_attribute '+param_name)
            continue
        elif param_name in params:  # already calculated KPV/KTI/Phase ***********************NEW
            logger.debug('  derive_parameters: re-using '+param_name)
            continue

        logger.debug('  derive_: computing '+param_name)        
        node_class = node_mgr.derived_nodes[param_name]  #NB raises KeyError if Node is "unknown"
        
        # build ordered dependencies
        deps = []
        node_deps = node_class.get_dependency_names()
        for dep_name in node_deps:
            if dep_name in params:  # already calculated KPV/KTI/Phase
                deps.append(params[dep_name])
            elif node_mgr.get_attribute(dep_name) is not None:
                deps.append(node_mgr.get_attribute(dep_name))
            elif dep_name in node_mgr.hdf_keys:  
                # LFL/Derived parameter
                # all parameters (LFL or other) need get_aligned which is
                # available on DerivedParameterNode
                try:
                    dp = derived_param_from_hdf(hdf.get_param(dep_name,
                                                              valid_only=True))
                except KeyError:
                    # Parameter is invalid.
                    dp = None
                deps.append(dp)
            else:  # dependency not available
                deps.append(None)
        if all([d is None for d in deps]):
            raise RuntimeError("No dependencies available - Nodes cannot "
                               "operate without ANY dependencies available! "
                               "Node: %s" % node_class.__name__)

        # initialise node
        node = node_class()
        logger.info("Processing parameter %s", param_name)
        # Derive the resulting value

        result = node.get_derived(deps)

        if node.node_type is KeyPointValueNode:
            #Q: track node instead of result here??
            params[param_name] = result
            for one_hz in result.get_aligned(P(frequency=1, offset=0)):
                if not (0 <= one_hz.index <= duration):
                    raise IndexError(
                        "KPV '%s' index %.2f is not between 0 and %d" %
                        (one_hz.name, one_hz.index, duration))
                kpv_list.append(one_hz)
        elif node.node_type is KeyTimeInstanceNode:
            params[param_name] = result
            for one_hz in result.get_aligned(P(frequency=1, offset=0)):
                if not (0 <= one_hz.index <= duration):
                    raise IndexError(
                        "KTI '%s' index %.2f is not between 0 and %d" %
                        (one_hz.name, one_hz.index, duration))
                kti_list.append(one_hz)
        elif node.node_type is FlightAttributeNode:
            params[param_name] = result
            try:
                flight_attrs.append(Attribute(result.name, result.value)) # only has one Attribute result
            except:
                logger.warning("Flight Attribute Node '%s' returned empty "
                               "handed.", param_name)
        elif issubclass(node.node_type, SectionNode):
            aligned_section = result.get_aligned(P(frequency=1, offset=0))
            for index, one_hz in enumerate(aligned_section):
                # SectionNodes allow slice starts and stops being None which
                # signifies the beginning and end of the data. To avoid TypeErrors
                # in subsequent derive methods which perform arithmetic on section
                # slice start and stops, replace with 0 or hdf.duration.
                fallback = lambda x, y: x if x is not None else y

                duration = fallback(duration, 0)

                start = fallback(one_hz.slice.start, 0)
                stop = fallback(one_hz.slice.stop, duration)
                start_edge = fallback(one_hz.start_edge, 0)
                stop_edge = fallback(one_hz.stop_edge, duration)

                slice_ = slice(start, stop)
                one_hz = Section(one_hz.name, slice_, start_edge, stop_edge)
                aligned_section[index] = one_hz
                
                if not (0 <= start <= duration and 0 <= stop <= duration + 1):
                    msg = "Section '%s' (%.2f, %.2f) not between 0 and %d"
                    raise IndexError(msg % (one_hz.name, start, stop, duration))
                if not 0 <= start_edge <= duration:
                    msg = "Section '%s' start_edge (%.2f) not between 0 and %d"
                    raise IndexError(msg % (one_hz.name, start_edge, duration))
                if not 0 <= stop_edge <= duration + 1:
                    msg = "Section '%s' stop_edge (%.2f) not between 0 and %d"
                    raise IndexError(msg % (one_hz.name, stop_edge, duration))
                section_list.append(one_hz)
            params[param_name] = aligned_section
        elif issubclass(node.node_type, DerivedParameterNode):
            if duration:
                # check that the right number of results were returned
                # Allow a small tolerance. For example if duration in seconds
                # is 2822, then there will be an array length of  1411 at 0.5Hz and 706
                # at 0.25Hz (rounded upwards). If we combine two 0.25Hz
                # parameters then we will have an array length of 1412.
                expected_length = duration * result.frequency
                if result.array is None:
                    logger.warning("No array set; creating a fully masked array for %s", param_name)
                    array_length = expected_length
                    # Where a parameter is wholly masked, we fill the HDF
                    # file with masked zeros to maintain structure.
                    result.array = \
                        np_ma_masked_zeros_like(np.ma.arange(expected_length))
                else:
                    array_length = len(result.array)
                length_diff = array_length - expected_length
                if length_diff == 0:
                    pass
                elif 0 < length_diff < 5:
                    logger.warning("Cutting excess data for parameter '%s'. "
                                   "Expected length was '%s' while resulting "
                                   "array length was '%s'.", param_name,
                                   expected_length, len(result.array))
                    result.array = result.array[:expected_length]
                else:
                    raise ValueError("Array length mismatch for parameter "
                                     "'%s'. Expected '%s', resulting array "
                                     "length '%s'." % (param_name,
                                                       expected_length,
                                                       array_length))
            
            hdf.set_param(result)
            # Keep hdf_keys up to date.
            node_mgr.hdf_keys.append(param_name)
        elif issubclass(node.node_type, ApproachNode):
            aligned_approach = result.get_aligned(P(frequency=1, offset=0))
            for approach in aligned_approach:
                # Does not allow slice start or stops to be None.
                valid_turnoff = (not approach.turnoff or
                                 (0 <= approach.turnoff <= duration))
                valid_slice = ((0 <= approach.slice.start <= duration) and
                               (0 <= approach.slice.stop <= duration))
                valid_gs_est = (not approach.gs_est or
                                ((0 <= approach.gs_est.start <= duration) and
                                 (0 <= approach.gs_est.stop <= duration)))
                valid_loc_est = (not approach.loc_est or
                                 ((0 <= approach.loc_est.start <= duration) and
                                  (0 <= approach.loc_est.stop <= duration)))
                if not all([valid_turnoff, valid_slice, valid_gs_est,
                            valid_loc_est]):
                    raise ValueError('ApproachItem contains index outside of '
                                     'flight data: %s' % approach)
                approach_list.append(approach)
            params[param_name] = aligned_approach
        else:
            raise NotImplementedError("Unknown Type %s" % node.__class__)
        continue
    return kti_list, kpv_list, section_list, approach_list, flight_attrs, params
Esempio n. 17
0
    def derive(
            self,
            rad=P('Altitude Radio'),
            hdot=P('Vertical Speed'),
    ):
        def seek_deck(rad, hdot, min_idx, rad_hz):
            def one_direction(rad, hdot, sence, rad_hz):
                # Stairway to Heaven is getting a bit old. Getting with the times?
                # Vertical Speed / 60 = Pressure alt V/S in feet per second
                b_diffs = hdot / 60

                # Rate of change on radalt array = Rad alt V/S in feet per second
                r_diffs = np.ma.ediff1d(rad * rad_hz, to_begin=b_diffs[0])

                # Difference between ROC greater than 6fps will mean flying over
                # the deck; use pressure alt roc when that happens and radio alt
                # roc in all other cases
                diffs = np.ma.where(
                    np.ma.abs(r_diffs - b_diffs) > 6.0 * rad_hz, b_diffs,
                    r_diffs)

                height = integrate(diffs,
                                   frequency=rad_hz,
                                   direction=sence,
                                   repair=False)
                return height

            height_from_rig = np_ma_masked_zeros_like(rad)
            if len(rad[:min_idx]) > 0:
                height_from_rig[:min_idx] = one_direction(
                    rad[:min_idx], hdot[:min_idx], "backwards", rad_hz)
            if len(rad[min_idx:]) > 0:
                height_from_rig[min_idx:] = one_direction(
                    rad[min_idx:], hdot[min_idx:], "forwards", rad_hz)
            '''
            # And we are bound to want to know the rig height somewhere, so here's how to work that out.
            rig_height = rad[0]-height_from_rig[0]
            # I checked this and it seems pretty consistent.
            # See Library\Projects\Helicopter FDM\Algorithm Development\Rig height estimates from Bond initial test data.xlsx
            #lat=hdf['Latitude'].array[app_slice][-1]
            #lon=hdf['Longitude'].array[app_slice][-1]
            #print(lat, lon, rig_height)
            '''
            return height_from_rig

        # Prepare a masked array filled with zeros for the parameter (same length as radalt array)
        self.array = np_ma_masked_zeros_like(rad.array)
        rad_peak_idxs, rad_peak_vals = cycle_finder(rad.array, min_step=150.0)

        if len(rad_peak_idxs) < 4:
            return

        slice_idxs = list(
            zip(rad_peak_idxs[:-2], rad_peak_idxs[1:-1], rad_peak_idxs[2:],
                rad_peak_vals[1:]))
        for slice_idx in slice_idxs[1:-1]:
            this_deck_slice = slice(slice_idx[0] + 1, slice_idx[2] - 1)
            if slice_idx[3] > 5.0:
                # We didn't land in this period
                continue
            else:
                self.array[this_deck_slice] = seek_deck(
                    rad.array[this_deck_slice], hdot.array[this_deck_slice],
                    slice_idx[1] - slice_idx[0], rad.frequency)
                '''