def __init__(self, icao_code=None, rwyno=0, elevation_ft=0, heading=0, surf='ASP', length_ft=10000, width_ft=100): if icao_code: # Read relevant runway data from the ourairports.com database # le_... / he_... - numbers referring to the low/high end respectively rwy_file = os.path.join(os.path.dirname(__file__), "data", "runways.csv") with open(rwy_file, newline='') as rwyfile: runwaydata = csv.reader(rwyfile, delimiter=',') runwaylist = [] for row in runwaydata: runwaylist.append(row) rindlst = [i for i, rwy in enumerate(runwaylist) if rwy[2] == icao_code] self.nrways = len(rindlst) if rwyno > self.nrways - 1: print('Requested rwy. nr. exceeds the nr. of runways at ', icao_code, '(', self.nrways, ')') raise ValueError('Incorrect runway number.') rind = rindlst[0] self.ident = runwaylist[rind + rwyno][0] self.airport_ref = runwaylist[rind + rwyno][1] self.airport_ident = runwaylist[rind + rwyno][2] self.length_ft = float(runwaylist[rind + rwyno][3]) self.width_ft = float(runwaylist[rind + rwyno][4]) self.surface = runwaylist[rind + rwyno][5] self.lighted = runwaylist[rind + rwyno][6] self.closed = runwaylist[rind + rwyno][7] self.le_ident = runwaylist[rind + rwyno][8] self.le_latitude_deg = runwaylist[rind + rwyno][9] self.le_longitude_deg = runwaylist[rind + rwyno][10] self.le_elevation_ft = float(runwaylist[rind + rwyno][11]) self.le_heading_degt = float(runwaylist[rind + rwyno][12]) try: self.le_displaced_threshold_ft = float(runwaylist[rind + rwyno][13]) except ValueError: self.le_displaced_threshold_ft = float('nan') self.he_ident = runwaylist[rind + rwyno][14] self.he_latitude_deg = runwaylist[rind + rwyno][15] self.he_longitude_deg = runwaylist[rind + rwyno][16] self.he_elevation_ft = float(runwaylist[rind + rwyno][17]) self.he_heading_degt = float(runwaylist[rind + rwyno][18]) try: self.he_displaced_threshold_ft = float(runwaylist[rind + rwyno][19]) except ValueError: self.he_displaced_threshold_ft = float('nan') else: # Define a custom runway based on the data provided self.ident = '0000000' self.airport_ref = '0000' self.airport_ident = 'CUST' self.length_ft = length_ft self.width_ft = width_ft self.surface = surf self.lighted = 1 self.closed = 0 self.le_ident = str(int(heading/10)) self.le_latitude_deg = 0 self.le_longitude_deg = 0 self.le_elevation_ft = elevation_ft self.le_heading_degt = heading self.le_displaced_threshold_ft = 0 self.he_ident = str(int(reciprocalhdg(heading)/10)) self.he_latitude_deg = 0 self.he_longitude_deg = 0 self.he_elevation_ft = elevation_ft self.he_heading_degt = reciprocalhdg(heading) self.he_displaced_threshold_ft = 0 # Metric versions of the imperial fields listed above self.length_m = co.feet2m(self.length_ft) self.width_m = co.feet2m(self.width_ft) self.le_elevation_m = co.feet2m(self.le_elevation_ft) self.le_displaced_threshold_m = co.feet2m(self.le_displaced_threshold_ft) self.he_elevation_m = co.feet2m(self.he_elevation_ft) self.he_displaced_threshold_m = co.feet2m(self.he_displaced_threshold_ft)
def _paragraph333(self): """Flight envelope, as defined by CS-23. For normal, utility, commuter, and aerobatic categories of aircraft, returns the maximum and minimum limit loads from symmetrical manoeuvres, as well as gust types each category must be designed to withstand. **Outputs:** manoeuvre_dict dictionary, with aircraft categories :code:`'norm'`,:code:`'util'`, :code:`'comm'`, and :code:`'aero'`. Embedded within each category is a dictionary of minimum and maximum manoeuvre limit loads the aircraft should be designed to withstand, in different flight conditions. Limit loads in each category are :code:`'npos_D'`, :code:`'nneg_C'`, and :code:`'nneg_D'`, relating to cruise and dive speed limits. gustmps_dict dictionary, with aircraft categories :code:`'norm'`,:code:`'util'`, :code:`'comm'`, and :code:`'aero'`. Embedded within each category is a dictionary of derived gust velocities U_de, that each category is expected to encounter and must be designed to withstand. As per CS-23.333, only :code:`'Ub_mps'` is pertinent to commuter category aircraft (rough gusts), whereas :code:`'Uc_mps'` and :code:`'Ud_mps'` apply to all. """ altitude_m = self.altitude_m # Create a dictionary of empty dictionaries for each aircraft category cs23categories_list = ['norm', 'util', 'comm', 'aero'] manoeuvre_dict = dict( zip(cs23categories_list, [{} for _ in range(len(cs23categories_list))])) gustmps_dict = dict( zip(cs23categories_list, [{} for _ in range(len(cs23categories_list))])) # (b) Manoeuvring Envelope # (b)(1, 2) manoeuvrelimitloads = self._paragraph337() for category in cs23categories_list: # The aeroplane is to be subjected to sym. manoeuvres that result in +ve limit load for speeds up to V_D manoeuvre_dict[category].update( {'npos_D': manoeuvrelimitloads[category]['npos_min']}) # The aeroplane is to be subjected to sym. manoeuvres that result in -ve limit load for speeds up to V_C manoeuvre_dict[category].update( {'nneg_C': manoeuvrelimitloads[category]['nneg_max']}) # (b)(3) manoeuvre_dict['norm'].update({'nneg_D': 0}) manoeuvre_dict['util'].update({'nneg_D': -1}) manoeuvre_dict['comm'].update({'nneg_D': 0}) manoeuvre_dict['aero'].update({'nneg_D': -1}) # (c) Gust Envelope # (c)(1) gustb_mps = np.interp( altitude_m, [co.feet2m(15000), co.feet2m(60000)], [co.feet2m(44), co.feet2m(20.86)]), gustc_mps = np.interp( altitude_m, [co.feet2m(15000), co.feet2m(60000)], [co.feet2m(44), co.feet2m(20.86)]), gustd_mps = np.interp( altitude_m, [co.feet2m(15000), co.feet2m(60000)], [co.feet2m(22), co.feet2m(20.86 / 2)]) gustmps_dict['norm'].update({ 'Uc_mps': gustc_mps[0], 'Ud_mps': gustd_mps }) gustmps_dict['util'].update({ 'Uc_mps': gustc_mps[0], 'Ud_mps': gustd_mps }) gustmps_dict['comm'].update({ 'Ub_mps': gustb_mps[0], 'Uc_mps': gustc_mps[0], 'Ud_mps': gustd_mps }) gustmps_dict['aero'].update({ 'Uc_mps': gustc_mps[0], 'Ud_mps': gustd_mps }) # (c)(2) Gust loading assumptions and load factor variation with speed return manoeuvre_dict, gustmps_dict