def update_geovector(self): ''' Update traffic according to geovector rules ''' # Check if any geovectors are defined in the first place, return if not if not bool(geovector.geovecs): return # Retrieve vectorized limits for each aircraft in the current timestep gsmin, gsmax, trkmin, trkmax, vsmin, vsmax = get_limits() # Convert ground speed to calibrated airspeed casmin = vtas2cas(np.ones(traf.ntraf) * gsmin, traf.alt) casmax = vtas2cas(np.ones(traf.ntraf) * gsmax, traf.alt) # Limit selected airspeed where setting exceeds limit self.spd = np.fmax(self.spd, casmin) self.spd = np.fmin(self.spd, casmax) # Limit selected track where setting exceeds limit self.trk = np.where(degto180(self.trk - trkmin) < 0, trkmin, self.trk) # left of minimum self.trk = np.where(degto180(self.trk - trkmax) > 0, trkmax, self.trk) # right of maximum # Check which aircraft breach the vertical speed limits vsbreach = np.logical_or(self.vs < vsmin, self.vs > vsmax) # Limit selected vertical speed where setting exceeds limit self.vs = np.fmax(self.vs, vsmin) self.vs = np.fmin(self.vs, vsmax) # If vs is not zero but aircraft already at altitude, altitude needs to be changed changealt = np.logical_and(np.abs(self.vs) > 0., (self.alt - traf.alt) < 1e-6) # Only change altitude when vs limit will be breached self.alt = np.where(np.logical_and(vsbreach, changealt), traf.alt + 100*self.vs, self.alt)
def applygeovec(): # Apply each geovector for vec in geovecs: areaname = vec[0] if areafilter.hasArea(areaname): swinside = areafilter.checkInside(areaname, traf.lat, traf.lon, traf.alt) gsmin, gsmax, trkmin, trkmax, vsmin, vsmax = vec[1:] # -----Ground speed limiting # For now assume no wind: so use tas as gs if gsmin: casmin = vtas2cas(np.ones(traf.ntraf) * gsmin, traf.alt) usemin = traf.selspd < casmin traf.selspd[swinside & usemin] = casmin[swinside & usemin] if gsmax: casmax = vtas2cas(np.ones(traf.ntraf) * gsmax, traf.alt) usemax = traf.selspd > casmax traf.selspd[swinside & usemax] = casmax[swinside & usemax] #------ Limit Track(so hdg) # Max track interval is 180 degrees to avoid ambiguity of what is inside the interval if trkmin and trkmax: # Use degto180 to avodi problems for e.g interval [350,30] usemin = swinside & (degto180(traf.trk - trkmin) < 0 ) # Left of minimum usemax = swinside & (degto180(traf.trk - trkmax) > 0 ) # Right of maximum #print(usemin,usemax) traf.ap.trk[swinside & usemin] = trkmin traf.ap.trk[swinside & usemax] = trkmax # -----Ground speed limiting # For now assume no wind: so use tas as gs if vsmin: traf.selvs[swinside & (traf.vs < vsmin)] = vsmin # Activate V/S mode by using a slightly higher altitude than current values traf.selalt[swinside & (traf.vs < vsmin)] = traf.alt[swinside & (traf.vs < vsmin)] + \ np.sign(vsmin)*200.*ft if vsmax: traf.selvs[swinside & (traf.vs > vsmax)] = vsmax # Activate V/S mode by using a slightly higher altitude than current values traf.selalt[swinside & (traf.vs > vsmax)] = traf.alt[swinside & (traf.vs > vsmax)] + \ np.sign(vsmax)*200.*ft return
def applygeovec(): # Apply each geovector for vec in geovecs: areaname = vec[0] if areafilter.hasArea(areaname): swinside = areafilter.checkInside(areaname, traf.lat, traf.lon, traf.alt) gsmin,gsmax,trkmin,trkmax,vsmin,vsmax = vec[1:] # -----Ground speed limiting # For now assume no wind: so use tas as gs if gsmin: casmin = vtas2cas(np.ones(traf.ntraf)*gsmin,traf.alt) usemin = traf.selspd<casmin traf.selspd[swinside & usemin] = casmin[swinside & usemin] if gsmax: casmax = vtas2cas(np.ones(traf.ntraf)*gsmax,traf.alt) usemax = traf.selspd > casmax traf.selspd[swinside & usemax] = casmax[swinside & usemax] #------ Limit Track(so hdg) # Max track interval is 180 degrees to avoid ambiguity of what is inside the interval if trkmin and trkmax: # Use degto180 to avodi problems for e.g interval [350,30] usemin = swinside & (degto180(traf.trk - trkmin)<0) # Left of minimum usemax = swinside & (degto180(traf.trk - trkmax)>0) # Right of maximum #print(usemin,usemax) traf.ap.trk[swinside & usemin] = trkmin traf.ap.trk[swinside & usemax] = trkmax # -----Ground speed limiting # For now assume no wind: so use tas as gs if vsmin: traf.selvs[swinside & (traf.vs<vsmin)] = vsmin # Activate V/S mode by using a slightly higher altitude than current values traf.selalt[swinside & (traf.vs < vsmin)] = traf.alt[swinside & (traf.vs < vsmin)] + \ np.sign(vsmin)*200.*ft if vsmax: traf.selvs[swinside & (traf.vs > vsmax)] = vsmax # Activate V/S mode by using a slightly higher altitude than current values traf.selalt[swinside & (traf.vs > vsmax)] = traf.alt[swinside & (traf.vs > vsmax)] + \ np.sign(vsmax)*200.*ft return
def limits(self, intent_v_tas, intent_vs, intent_h, ax): """ apply limits on indent speed, vertical speed, and altitude (called in pilot module)""" super(PerfNAP, self).limits(intent_v_tas, intent_vs, intent_h) # if isinstance(self.vmin, np.ndarray): # pass # else: # self.update() allow_h = np.where(intent_h > self.hmax, self.hmax, intent_h) intent_v_cas = aero.vtas2cas(intent_v_tas, allow_h) allow_v_cas = np.where(intent_v_cas < self.vmin, self.vmin, intent_v_cas) allow_v_cas = np.where(intent_v_cas > self.vmax, self.vmax, allow_v_cas) allow_v_tas = aero.vcas2tas(allow_v_cas, allow_h) vs_max_with_acc = (1 - ax / self.axmax) * self.vsmax allow_vs = np.where(intent_vs > self.vsmax, vs_max_with_acc, intent_vs) allow_vs = np.where(intent_vs < self.vsmin, self.vsmin, allow_vs) # print(intent_h, allow_h, self.hmax) # print(intent_v_cas, allow_v_cas, self.vmin, self.vmax) # print(self.coeff.limits_rotor['EC35']) return allow_v_tas, allow_vs, allow_h
def update_airspeed(self): # Compute horizontal acceleration delta_spd = self.aporasas.tas - self.tas ax = self.perf.acceleration() need_ax = np.abs(delta_spd) > np.abs(bs.sim.simdt * ax) self.ax = need_ax * np.sign(delta_spd) * ax # Update velocities self.tas = np.where(need_ax, self.tas + self.ax * bs.sim.simdt, self.aporasas.tas) self.cas = vtas2cas(self.tas, self.alt) self.M = vtas2mach(self.tas, self.alt) # Turning turnrate = np.degrees(g0 * np.tan(np.where(self.aphi>self.eps,self.aphi,self.bank) \ / np.maximum(self.tas, self.eps))) delhdg = (self.aporasas.hdg - self.hdg + 180) % 360 - 180 # [deg] self.swhdgsel = np.abs(delhdg) > np.abs(bs.sim.simdt * turnrate) # Update heading self.hdg = np.where(self.swhdgsel, self.hdg + bs.sim.simdt * turnrate * np.sign(delhdg), self.aporasas.hdg) % 360.0 # Update vertical speed delta_alt = self.aporasas.alt - self.alt self.swaltsel = np.abs(delta_alt) > np.maximum( 10 * ft, np.abs(2 * np.abs(bs.sim.simdt * self.vs))) target_vs = self.swaltsel * np.sign(delta_alt) * np.abs(self.aporasas.vs) delta_vs = target_vs - self.vs # print(delta_vs / fpm) need_az = np.abs(delta_vs) > 300 * fpm # small threshold self.az = need_az * np.sign(delta_vs) * (300 * fpm) # fixed vertical acc approx 1.6 m/s^2 self.vs = np.where(need_az, self.vs+self.az*bs.sim.simdt, target_vs) self.vs = np.where(np.isfinite(self.vs), self.vs, 0) # fix vs nan issue
def UpdateAirSpeed(self, simdt, simt): # Compute horizontal acceleration delta_spd = self.pilot.tas - self.tas need_ax = np.abs(delta_spd) > kts # small threshold self.ax = need_ax * np.sign(delta_spd) * self.perf.acceleration() # Update velocities self.tas = self.tas + self.ax * simdt self.cas = vtas2cas(self.tas, self.alt) self.M = vtas2mach(self.tas, self.alt) # Turning turnrate = np.degrees(g0 * np.tan(self.bank) / np.maximum(self.tas, self.eps)) delhdg = (self.pilot.hdg - self.hdg + 180) % 360 - 180 # [deg] self.swhdgsel = np.abs(delhdg) > np.abs(1.5 * simdt * turnrate) # Update heading self.hdg = (self.hdg + np.where( self.swhdgsel, simdt * turnrate * np.sign(delhdg), delhdg)) % 360.0 # Update vertical speed delta_alt = self.pilot.alt - self.alt self.swaltsel = np.abs(delta_alt) > np.maximum( 10 * ft, np.abs(2 * simdt * np.abs(self.vs))) target_vs = self.swaltsel * np.sign(delta_alt) * np.abs(self.pilot.vs) delta_vs = target_vs - self.vs # print(delta_vs / fpm) need_az = np.abs(delta_vs) > 300 * fpm # small threshold self.az = need_az * np.sign(delta_vs) * ( 300 * fpm) # fixed vertical acc approx 1.6 m/s^2 self.vs = np.where(need_az, self.vs + self.az * simdt, target_vs) self.vs = np.where(np.isfinite(self.vs), self.vs, 0) # fix vs nan issue
def limits(self, intent_v_tas, intent_vs, intent_h, ax): """ apply limits on indent speed, vertical speed, and altitude (called in pilot module)""" super(OpenAP, self).limits(intent_v_tas, intent_vs, intent_h) # if isinstance(self.vmin, np.ndarray): # pass # else: # self.update() allow_h = np.where(intent_h > self.hmax, self.hmax, intent_h) intent_v_cas = aero.vtas2cas(intent_v_tas, allow_h) allow_v_cas = np.where(intent_v_cas < self.vmin, self.vmin, intent_v_cas) allow_v_cas = np.where(intent_v_cas > self.vmax, self.vmax, allow_v_cas) allow_v_tas = aero.vcas2tas(allow_v_cas, allow_h) vs_max_with_acc = (1 - ax / self.axmax) * self.vsmax allow_vs = np.where(intent_vs > self.vsmax, vs_max_with_acc, intent_vs) allow_vs = np.where(intent_vs < self.vsmin, self.vsmin, allow_vs) # print(intent_h, allow_h, self.hmax) # print(intent_v_cas, allow_v_cas, self.vmin, self.vmax) # print(self.coeff.limits_rotor['EC35']) return allow_v_tas, allow_vs, allow_h
def UpdateAirSpeed(self, simdt, simt): # Compute horizontal acceleration delta_spd = self.pilot.tas - self.tas need_ax = np.abs(delta_spd) > kts # small threshold self.ax = need_ax * np.sign(delta_spd) * self.perf.acceleration() # Update velocities self.tas = self.tas + self.ax * simdt self.cas = vtas2cas(self.tas, self.alt) self.M = vtas2mach(self.tas, self.alt) # Turning turnrate = np.degrees(g0 * np.tan(self.bank) / np.maximum(self.tas, self.eps)) delhdg = (self.pilot.hdg - self.hdg + 180) % 360 - 180 # [deg] self.swhdgsel = np.abs(delhdg) > np.abs(2 * simdt * turnrate) # Update heading self.hdg = (self.hdg + simdt * turnrate * self.swhdgsel * np.sign(delhdg)) % 360. # Update vertical speed delta_alt = self.pilot.alt - self.alt self.swaltsel = np.abs(delta_alt) > np.maximum(10 * ft, np.abs(2 * simdt * np.abs(self.vs))) target_vs = self.swaltsel * np.sign(delta_alt) * np.abs(self.pilot.vs) delta_vs = target_vs - self.vs # print(delta_vs / fpm) need_az = np.abs(delta_vs) > 300 * fpm # small threshold self.az = need_az * np.sign(delta_vs) * (300 * fpm) # fixed vertical acc approx 1.6 m/s^2 self.vs = np.where(need_az, self.vs+self.az*simdt, target_vs) self.vs = np.where(np.isfinite(self.vs), self.vs, 0) # fix vs nan issue
def UpdateAirSpeed(self, simdt, simt): # Acceleration self.delspd = self.pilot.tas - self.tas swspdsel = np.abs(self.delspd) > 0.4 # <1 kts = 0.514444 m/s ax = self.perf.acceleration(simdt) # Update velocities self.tas = np.where(swspdsel, \ self.tas + swspdsel * ax * np.sign(self.delspd) * simdt,\ self.pilot.tas) self.cas = vtas2cas(self.tas, self.alt) self.M = vtas2mach(self.tas, self.alt) # Turning turnrate = np.degrees(g0 * np.tan(self.bank) / np.maximum(self.tas, self.eps)) delhdg = (self.pilot.hdg - self.hdg + 180.) % 360 - 180. # [deg] self.swhdgsel = np.abs(delhdg) > np.abs(2. * simdt * turnrate) # Update heading self.hdg = (self.hdg + simdt * turnrate * self.swhdgsel * np.sign(delhdg)) % 360. # Update vertical speed delalt = self.pilot.alt - self.alt self.swaltsel = np.abs(delalt) > np.maximum(10 * ft, np.abs(2. * simdt * np.abs(self.vs))) self.vs = self.swaltsel * np.sign(delalt) * np.abs(self.pilot.vs)
def limits(self, intent_v_tas, intent_vs, intent_h, ax): """ apply limits on indent speed, vertical speed, and altitude (called in pilot module)""" super(OpenAP, self).limits(intent_v_tas, intent_vs, intent_h) allow_h = np.where(intent_h > self.hmax, self.hmax, intent_h) intent_v_cas = aero.vtas2cas(intent_v_tas, allow_h) allow_v_cas = np.where((intent_v_cas < self.vmin), self.vmin, intent_v_cas) allow_v_cas = np.where(intent_v_cas > self.vmax, self.vmax, allow_v_cas) allow_v_tas = aero.vcas2tas(allow_v_cas, allow_h) vs_max_with_acc = (1 - ax / self.axmax) * self.vsmax allow_vs = np.where((intent_vs > 0) & (intent_vs > self.vsmax), vs_max_with_acc, intent_vs) # for climb with vs larger than vsmax allow_vs = np.where( (intent_vs < 0) & (intent_vs < self.vsmin), vs_max_with_acc, allow_vs) # for descent with vs smaller than vsmin (negative) allow_vs = np.where( (self.phase == ph.GD) & (bs.traf.tas < self.vminto), 0, allow_vs) # takeoff aircraft return allow_v_tas, allow_vs, allow_h
def creconfs(self, acid, actype, targetidx, dpsi, cpa, tlosh, dH=None, tlosv=None, spd=None): latref = self.lat[targetidx] # deg lonref = self.lon[targetidx] # deg altref = self.alt[targetidx] # m trkref = radians(self.trk[targetidx]) gsref = self.gs[targetidx] # m/s vsref = self.vs[targetidx] # m/s cpa = cpa * nm pzr = settings.asas_pzr * nm pzh = settings.asas_pzh * ft trk = trkref + radians(dpsi) gs = gsref if spd is None else spd if dH is None: acalt = altref acvs = 0.0 else: acalt = altref + dH tlosv = tlosh if tlosv is None else tlosv acvs = vsref - np.sign(dH) * (abs(dH) - pzh) / tlosv # Horizontal relative velocity vector gsn, gse = gs * cos(trk), gs * sin(trk) vreln, vrele = gsref * cos(trkref) - gsn, gsref * sin(trkref) - gse # Relative velocity magnitude vrel = sqrt(vreln * vreln + vrele * vrele) # Relative travel distance to closest point of approach drelcpa = tlosh * vrel + (0 if cpa > pzr else sqrt(pzr * pzr - cpa * cpa)) # Initial intruder distance dist = sqrt(drelcpa * drelcpa + cpa * cpa) # Rotation matrix diagonal and cross elements for distance vector rd = drelcpa / dist rx = cpa / dist # Rotate relative velocity vector to obtain intruder bearing brn = degrees(atan2(-rx * vreln + rd * vrele, rd * vreln + rx * vrele)) # Calculate intruder lat/lon aclat, aclon = geo.qdrpos(latref, lonref, brn, dist / nm) # convert groundspeed to CAS, and track to heading wn, we = self.wind.getdata(aclat, aclon, acalt) tasn, tase = gsn - wn, gse - we acspd = vtas2cas(sqrt(tasn * tasn + tase * tase), acalt) achdg = degrees(atan2(tase, tasn)) # Create and, when necessary, set vertical speed self.create(1, actype, acalt, acspd, None, aclat, aclon, achdg, acid) self.ap.selaltcmd(len(self.lat) - 1, altref, acvs) self.vs[-1] = acvs
def limits(self): """Flight envelope""" # Connect this with function limits in performance.py # combine minimum speeds and flight phases. Phases initial climb, cruise # and approach use the same CLmax and thus the same function for Vmin self.vmto = self.vm_to*np.sqrt(self.mass/bs.traf.rho) self.vmic = np.sqrt(2*self.mass*g0/(bs.traf.rho*self.clmaxcr*self.Sref)) self.vmcr = self.vmic self.vmap = self.vmic self.vmld = self.vm_ld*np.sqrt(self.mass/bs.traf.rho) # summarize and convert to cas # note: aircraft on ground may be pushed back self.vmin = (self.phase==1)*vtas2cas(self.vmto, bs.traf.alt) + \ ((self.phase==2) + (self.phase==3) + (self.phase==4))*vtas2cas(self.vmcr, bs.traf.alt) + \ (self.phase==5)*vtas2cas(self.vmld, bs.traf.alt) + (self.phase==6)*-10.0 # forwarding to tools bs.traf.limspd, \ bs.traf.limspd_flag, \ bs.traf.limalt, \ bs.traf.limalt_flag, \ bs.traf.limvs, \ bs.traf.limvs_flag = calclimits(vtas2cas(bs.traf.pilot.tas, bs.traf.alt), \ bs.traf.gs, \ self.vmto, \ self.vmin, \ self.vmo, \ self.mmo, \ bs.traf.M, \ bs.traf.alt, \ self.hmaxact, \ bs.traf.pilot.alt, \ bs.traf.pilot.vs, \ self.maxthr, \ self.Thr_pilot, \ self.D, \ bs.traf.cas, \ self.mass, \ self.ESF, \ self.phase) return
def limits(self): """Flight envelope""" # Connect this with function limits in performance.py # combine minimum speeds and flight phases. Phases initial climb, cruise # and approach use the same CLmax and thus the same function for Vmin self.vmto = self.vm_to*np.sqrt(self.mass/bs.traf.rho) self.vmic = np.sqrt(2*self.mass*g0/(bs.traf.rho*self.clmaxcr*self.Sref)) self.vmcr = self.vmic self.vmap = self.vmic self.vmld = self.vm_ld*np.sqrt(self.mass/bs.traf.rho) # summarize and convert to cas # note: aircraft on ground may be pushed back self.vmin = (self.phase==1)*vtas2cas(self.vmto, bs.traf.alt) + \ ((self.phase==2) + (self.phase==3) + (self.phase==4))*vtas2cas(self.vmcr, bs.traf.alt) + \ (self.phase==5)*vtas2cas(self.vmld, bs.traf.alt) + (self.phase==6)*-10.0 # forwarding to tools bs.traf.limspd, \ bs.traf.limspd_flag, \ bs.traf.limalt, \ bs.traf.limalt_flag, \ bs.traf.limvs, \ bs.traf.limvs_flag = calclimits(vtas2cas(bs.traf.pilot.tas, bs.traf.alt), \ bs.traf.gs, \ self.vmto, \ self.vmin, \ self.vmo, \ self.mmo, \ bs.traf.M, \ bs.traf.alt, \ self.hmaxact, \ bs.traf.pilot.alt, \ bs.traf.pilot.vs, \ self.maxthr, \ self.thrust_pilot, \ self.D, \ bs.traf.tas, \ self.mass, \ self.ESF, \ self.phase) return
def inflight(v, h, vs, thr0): """Compute thrust ration for inflight""" def dfunc(mratio): d = -0.4204 * mratio + 1.0824 return d def nfunc(roc): n = 2.667e-05 * roc + 0.8633 return n def mfunc(vratio, roc): m = -1.2043e-1 * vratio - 8.8889e-9 * roc**2 + 2.4444e-5 * roc + 4.7379e-1 return m roc = np.abs(np.asarray(vs / aero.fpm)) v = np.where(v < 10, 10, v) mach = aero.vtas2mach(v, h) vcas = aero.vtas2cas(v, h) p = aero.vpressure(h) p10 = aero.vpressure(10000 * aero.ft) p35 = aero.vpressure(35000 * aero.ft) # approximate thrust at top of climb (REF 2) F35 = (200 + 0.2 * thr0 / 4.448) * 4.448 mach_ref = 0.8 vcas_ref = aero.vmach2cas(mach_ref, 35000 * aero.ft) # segment 3: alt > 35000: d = dfunc(mach / mach_ref) b = (mach / mach_ref)**(-0.11) ratio_seg3 = d * np.log(p / p35) + b # segment 2: 10000 < alt <= 35000: a = (vcas / vcas_ref)**(-0.1) n = nfunc(roc) ratio_seg2 = a * (p / p35)**(-0.355 * (vcas / vcas_ref) + n) # segment 1: alt <= 10000: F10 = F35 * a * (p10 / p35)**(-0.355 * (vcas / vcas_ref) + n) m = mfunc(vcas / vcas_ref, roc) ratio_seg1 = m * (p / p35) + (F10 / F35 - m * (p10 / p35)) ratio = np.where( h > 35000 * aero.ft, ratio_seg3, np.where(h > 10000 * aero.ft, ratio_seg2, ratio_seg1), ) # convert to maximum static thrust ratio ratio_F0 = ratio * F35 / thr0 return ratio_F0
def limits(self, intent_v, intent_vs, intent_h, ax): """Flight envelope""" # Connect this with function limits in performance.py # combine minimum speeds and flight phases. Phases initial climb, cruise # and approach use the same CLmax and thus the same function for Vmin self.vmto = self.vm_to * np.sqrt(self.mass / bs.traf.rho) self.vmic = np.sqrt(2 * self.mass * g0 / (bs.traf.rho * self.clmaxcr * self.Sref)) self.vmcr = self.vmic self.vmap = self.vmic self.vmld = self.vm_ld * np.sqrt(self.mass / bs.traf.rho) # summarize and convert to cas # note: aircraft on ground may be pushed back self.vmin = (self.phase==1)*vtas2cas(self.vmto, bs.traf.alt) + \ ((self.phase==2) + (self.phase==3) + (self.phase==4))*vtas2cas(self.vmcr, bs.traf.alt) + \ (self.phase==5)*vtas2cas(self.vmld, bs.traf.alt) + (self.phase==6)*-10.0 # forwarding to tools self.limspd, self.limspd_flag, self.limalt, \ self.limalt_flag, self.limvs, self.limvs_flag = calclimits( vtas2cas(intent_v, bs.traf.alt), bs.traf.gs, \ self.vmto, self.vmin, self.vmo, self.mmo, \ bs.traf.M, bs.traf.alt, self.hmaxact, \ intent_h, intent_vs, self.maxthr, \ self.thrust_pilot, self.D, bs.traf.tas, \ self.mass, self.ESF, self.phase) # Update desired sates with values within the flight envelope # When CAS is limited, it needs to be converted to TAS as only this TAS is used later on! allowed_tas = np.where(self.limspd_flag, vcas2tas(self.limspd, bs.traf.alt), intent_v) # Autopilot selected altitude [m] allowed_alt = np.where(self.limalt_flag, self.limalt, intent_h) # Autopilot selected vertical speed (V/S) allowed_vs = np.where(self.limvs_flag, self.limvs, intent_vs) return allowed_tas, allowed_vs, allowed_alt
def inflight(v, h, vs, thr0): """Compute thrust ration for inflight""" def dfunc(mratio): d = -0.4204 * mratio + 1.0824 return d def nfunc(roc): n = 2.667e-05 * roc + 0.8633 return n def mfunc(vratio, roc): m = -1.2043e-1 * vratio - 8.8889e-9 * roc**2 + 2.4444e-5 * roc + 4.7379e-1 return m roc = np.abs(np.asarray(vs/aero.fpm)) v = np.where(v < 10, 10, v) mach = aero.vtas2mach(v, h) vcas = aero.vtas2cas(v, h) p = aero.vpressure(h) p10 = aero.vpressure(10000*aero.ft) p35 = aero.vpressure(35000*aero.ft) # approximate thrust at top of climb (REF 2) F35 = (200 + 0.2 * thr0/4.448) * 4.448 mach_ref = 0.8 vcas_ref = aero.vmach2cas(mach_ref, 35000*aero.ft) # segment 3: alt > 35000: d = dfunc(mach/mach_ref) b = (mach / mach_ref) ** (-0.11) ratio_seg3 = d * np.log(p/p35) + b # segment 2: 10000 < alt <= 35000: a = (vcas / vcas_ref) ** (-0.1) n = nfunc(roc) ratio_seg2 = a * (p/p35) ** (-0.355 * (vcas/vcas_ref) + n) # segment 1: alt <= 10000: F10 = F35 * a * (p10/p35) ** (-0.355 * (vcas/vcas_ref) + n) m = mfunc(vcas/vcas_ref, roc) ratio_seg1 = m * (p/p35) + (F10/F35 - m * (p10/p35)) ratio = np.where(h>35000*aero.ft, ratio_seg3, np.where(h>10000*aero.ft, ratio_seg2, ratio_seg1)) # convert to maximum static thrust ratio ratio_F0 = ratio * F35 / thr0 return ratio_F0
def create(self, n=1): super(Autopilot, self).create(n) # FMS directions self.tas[-n:] = bs.traf.tas[-n:] self.trk[-n:] = bs.traf.trk[-n:] self.alt[-n:] = bs.traf.alt[-n:] self.spd[-n:] = vtas2cas(self.tas[-n:], self.alt[-n:]) # VNAV Variables self.dist2vs[-n:] = -999. # Route objects self.route.extend([Route()] * n)
def limits(self, intent_v_tas, intent_vs, intent_h, ax): """apply limits on indent speed, vertical speed, and altitude (called in pilot module) Args: intent_v_tas (float or 1D-array): intent true airspeed intent_vs (float or 1D-array): intent vertical speed intent_h (float or 1D-array): intent altitude ax (float or 1D-array): acceleration Returns: floats or 1D-arrays: Allowed TAS, Allowed vetical rate, Allowed altitude """ allow_h = np.where(intent_h > self.hmax, self.hmax, intent_h) intent_v_cas = aero.vtas2cas(intent_v_tas, allow_h) allow_v_cas = np.where((intent_v_cas < self.vmin), self.vmin, intent_v_cas) allow_v_cas = np.where(intent_v_cas > self.vmax, self.vmax, allow_v_cas) allow_v_tas = aero.vcas2tas(allow_v_cas, allow_h) allow_v_tas = np.where( aero.vtas2mach(allow_v_tas, allow_h) > self.mmo, aero.vmach2tas(self.mmo, allow_h), allow_v_tas, ) # maximum cannot exceed MMO vs_max_with_acc = (1 - ax / self.axmax) * self.vsmax allow_vs = np.where( (intent_vs > 0) & (intent_vs > self.vsmax), vs_max_with_acc, intent_vs ) # for climb with vs larger than vsmax allow_vs = np.where( (intent_vs < 0) & (intent_vs < self.vsmin), vs_max_with_acc, allow_vs ) # for descent with vs smaller than vsmin (negative) allow_vs = np.where( (self.phase == ph.GD) & (bs.traf.tas < self.vminto), 0, allow_vs ) # takeoff aircraft # corect rotercraft speed limits ir = np.where(self.lifttype == coeff.LIFT_ROTOR)[0] allow_v_tas[ir] = np.where( (intent_v_tas[ir] < self.vmin[ir]), self.vmin[ir], intent_v_tas[ir] ) allow_v_tas[ir] = np.where( (intent_v_tas[ir] > self.vmax[ir]), self.vmax[ir], allow_v_tas[ir] ) allow_vs[ir] = np.where( (intent_vs[ir] < self.vsmin[ir]), self.vsmin[ir], intent_vs[ir] ) allow_vs[ir] = np.where( (intent_vs[ir] > self.vsmax[ir]), self.vsmax[ir], allow_vs[ir] ) return allow_v_tas, allow_vs, allow_h
def creconfs(self, acid, actype, targetidx, dpsi, cpa, tlosh, dH=None, tlosv=None, spd=None): latref = self.lat[targetidx] # deg lonref = self.lon[targetidx] # deg altref = self.alt[targetidx] # m trkref = radians(self.trk[targetidx]) gsref = self.gs[targetidx] # m/s vsref = self.vs[targetidx] # m/s cpa = cpa * nm pzr = settings.asas_pzr * nm pzh = settings.asas_pzh * ft trk = trkref + radians(dpsi) gs = gsref if spd is None else spd if dH is None: acalt = altref acvs = 0.0 else: acalt = altref + dH tlosv = tlosh if tlosv is None else tlosv acvs = vsref - np.sign(dH) * (abs(dH) - pzh) / tlosv # Horizontal relative velocity vector gsn, gse = gs * cos(trk), gs * sin(trk) vreln, vrele = gsref * cos(trkref) - gsn, gsref * sin(trkref) - gse # Relative velocity magnitude vrel = sqrt(vreln * vreln + vrele * vrele) # Relative travel distance to closest point of approach drelcpa = tlosh * vrel + (0 if cpa > pzr else sqrt(pzr * pzr - cpa * cpa)) # Initial intruder distance dist = sqrt(drelcpa * drelcpa + cpa * cpa) # Rotation matrix diagonal and cross elements for distance vector rd = drelcpa / dist rx = cpa / dist # Rotate relative velocity vector to obtain intruder bearing brn = degrees(atan2(-rx * vreln + rd * vrele, rd * vreln + rx * vrele)) # Calculate intruder lat/lon aclat, aclon = geo.qdrpos(latref, lonref, brn, dist / nm) # convert groundspeed to CAS, and track to heading wn, we = self.wind.getdata(aclat, aclon, acalt) tasn, tase = gsn - wn, gse - we acspd = vtas2cas(sqrt(tasn * tasn + tase * tase), acalt) achdg = degrees(atan2(tase, tasn)) # Create and, when necessary, set vertical speed self.create(1, actype, acalt, acspd, None, aclat, aclon, achdg, acid) self.ap.selaltcmd(len(self.lat) - 1, altref, acvs) self.vs[-1] = acvs
def limits(self): """FLIGHT ENVELPOE""" # summarize minimum speeds - ac in ground mode might be pushing back self.vmin = (self.phase == 1) * self.vmto + (self.phase == 2) * self.vmic + (self.phase == 3) * self.vmcr + \ (self.phase == 4) * self.vmap + (self.phase == 5) * self.vmld + (self.phase == 6) * -10. # maximum altitude: hmax/act = MIN[hmo, hmax+gt*(dtemp-ctc1)+gw*(mmax-mact)] # or hmo if hmx ==0 () # at the moment just ISA atmosphere, dtemp = 0 c1 = self.dtemp - self.ctct1 # if c1<0: c1 = 0 # values above 0 remain, values below are replaced through 0 c1m = np.array(c1 < 0) * 0.00000000000001 c1def = np.maximum(c1, c1m) self.hact = self.hmax + self.gt * c1def + self.gw * (self.mmax - self.mass) # if hmax in OPF File ==0: hmaxact = hmo, else minimum(hmo, hmact) self.hmaxact = (self.hmax == 0) * self.hmo + ( self.hmax != 0) * np.minimum(self.hmo, self.hact) # forwarding to windtools # forwarding to windtools bs.traf.limspd, \ bs.traf.limspd_flag, \ bs.traf.limalt, \ bs.traf.limalt_flag, \ bs.traf.limvs, \ bs.traf.limvs_flag = calclimits(vtas2cas(bs.traf.pilot.tas, bs.traf.alt), \ bs.traf.gs, \ self.vmto, \ self.vmin, \ self.vmo, \ self.mmo, \ bs.traf.M, \ bs.traf.alt, \ self.hmaxact, \ bs.traf.pilot.alt, \ bs.traf.pilot.vs, \ self.maxthr, \ self.Thr, \ self.D, \ bs.traf.tas, \ self.mass, \ self.ESF, \ self.phase) return
def limits(self, indent_v, indent_vs, indent_h): """ apply limits on indent speed, vertical speed, and altitude """ super(PerfNAP, self).limits(indent_v, indent_vs, indent_h) indent_v = aero.vtas2cas(indent_v, indent_h) allow_v = np.where(indent_v < self.vmin, self.vmin, indent_v) allow_v = np.where(indent_v > self.vmax, self.vmax, indent_v) allow_v = aero.vcas2tas(indent_v, indent_h) allow_vs = np.where(indent_vs < self.vsmin, self.vsmin, indent_vs) allow_vs = np.where(indent_vs > self.vsmax, self.vsmax, indent_vs) allow_h = np.where(indent_h > self.hmaxalt, self.hmaxalt, indent_h) return allow_v, allow_vs, allow_h
def limits(self, indent_v, indent_vs, indent_h): """ apply limits on indent speed, vertical speed, and altitude """ super(PerfNAP, self).limits(indent_v, indent_vs, indent_h) indent_v = aero.vtas2cas(indent_v, indent_h) allow_v = np.where(indent_v < self.vmin, self.vmin, indent_v) allow_v = np.where(indent_v > self.vmax, self.vmax, indent_v) allow_v = aero.vcas2tas(indent_v, indent_h) allow_vs = np.where(indent_vs < self.vsmin, self.vsmin, indent_vs) allow_vs = np.where(indent_vs > self.vsmax, self.vsmax, indent_vs) allow_h = np.where(indent_h > self.hmaxalt, self.hmaxalt, indent_h) return allow_v, allow_vs, allow_h
def limits(self): """FLIGHT ENVELPOE""" # summarize minimum speeds - ac in ground mode might be pushing back self.vmin = (self.phase == 1) * self.vmto + (self.phase == 2) * self.vmic + (self.phase == 3) * self.vmcr + \ (self.phase == 4) * self.vmap + (self.phase == 5) * self.vmld + (self.phase == 6) * -10. # maximum altitude: hmax/act = MIN[hmo, hmax+gt*(dtemp-ctc1)+gw*(mmax-mact)] # or hmo if hmx ==0 () # at the moment just ISA atmosphere, dtemp = 0 c1 = self.dtemp - self.ctct1 # if c1<0: c1 = 0 # values above 0 remain, values below are replaced through 0 c1m = np.array(c1<0)*0.00000000000001 c1def = np.maximum(c1, c1m) self.hact = self.hmax+self.gt*c1def+self.gw*(self.mmax-self.mass) # if hmax in OPF File ==0: hmaxact = hmo, else minimum(hmo, hmact) self.hmaxact = (self.hmax==0)*self.hmo +(self.hmax !=0)*np.minimum(self.hmo, self.hact) # forwarding to tools # forwarding to tools bs.traf.limspd, \ bs.traf.limspd_flag, \ bs.traf.limalt, \ bs.traf.limalt_flag, \ bs.traf.limvs, \ bs.traf.limvs_flag = calclimits(vtas2cas(bs.traf.pilot.tas, bs.traf.alt), \ bs.traf.gs, \ self.vmto, \ self.vmin, \ self.vmo, \ self.mmo, \ bs.traf.M, \ bs.traf.alt, \ self.hmaxact, \ bs.traf.pilot.alt, \ bs.traf.pilot.vs, \ self.maxthr, \ self.Thr, \ self.D, \ bs.traf.cas, \ self.mass, \ self.ESF, \ self.phase) return
def limits(self, intent_v, intent_vs, intent_h, ax): """FLIGHT ENVELPOE""" # summarize minimum speeds - ac in ground mode might be pushing back self.vmin = (self.phase == 1) * self.vmto + (self.phase == 2) * self.vmic + (self.phase == 3) * self.vmcr + \ (self.phase == 4) * self.vmap + (self.phase == 5) * self.vmld + (self.phase == 6) * -10. # maximum altitude: hmax/act = MIN[hmo, hmax+gt*(dtemp-ctc1)+gw*(mmax-mact)] # or hmo if hmx ==0 () # at the moment just ISA atmosphere, dtemp = 0 c1 = self.dtemp - self.ctct1 # if c1<0: c1 = 0 # values above 0 remain, values below are replaced through 0 c1m = np.array(c1 < 0) * 0.00000000000001 c1def = np.maximum(c1, c1m) self.hact = self.hmax + self.gt * c1def + self.gw * (self.mmax - self.mass) # if hmax in OPF File ==0: hmaxact = hmo, else minimum(hmo, hmact) self.hmaxact = (self.hmax == 0) * self.hmo + ( self.hmax != 0) * np.minimum(self.hmo, self.hact) # forwarding to tools self.limspd, self.limspd_flag, self.limalt, \ self.limalt_flag, self.limvs, self.limvs_flag = calclimits( vtas2cas(intent_v, bs.traf.alt), bs.traf.gs, self.vmto, self.vmin, self.vmo, self.mmo, bs.traf.M, bs.traf.alt, self.hmaxact, intent_h, intent_vs, self.maxthr, self.thrust, self.D, bs.traf.tas, self.mass, self.ESF, self.phase) # Update desired sates with values within the flight envelope # When CAS is limited, it needs to be converted to TAS as only this TAS is used later on! allowed_tas = np.where(self.limspd_flag, vcas2tas(self.limspd, bs.traf.alt), intent_v) # Autopilot selected altitude [m] allowed_alt = np.where(self.limalt_flag, self.limalt, intent_h) # Autopilot selected vertical speed (V/S) allowed_vs = np.where(self.limvs_flag, self.limvs, intent_vs) return allowed_tas, allowed_vs, allowed_alt
def limits(self, intent_v_tas, intent_vs, intent_h, ax): """ apply limits on indent speed, vertical speed, and altitude (called in pilot module)""" super(OpenAP, self).limits(intent_v_tas, intent_vs, intent_h) allow_h = np.where(intent_h > self.hmax, self.hmax, intent_h) intent_v_cas = aero.vtas2cas(intent_v_tas, allow_h) allow_v_cas = np.where((intent_v_cas < self.vmin), self.vmin, intent_v_cas) allow_v_cas = np.where(intent_v_cas > self.vmax, self.vmax, allow_v_cas) allow_v_tas = aero.vcas2tas(allow_v_cas, allow_h) vs_max_with_acc = (1 - ax / self.axmax) * self.vsmax allow_vs = np.where((intent_vs > 0) & (intent_vs>self.vsmax), vs_max_with_acc, intent_vs) # for climb with vs larger than vsmax allow_vs = np.where((intent_vs < 0) & (intent_vs<self.vsmin), vs_max_with_acc, allow_vs) # for descent with vs smaller than vsmin (negative) allow_vs = np.where((self.phase==ph.GD) & (bs.traf.tas < self.vminto), 0, allow_vs) # takeoff aircraft return allow_v_tas, allow_vs, allow_h
def update_airspeed(self): # Compute horizontal acceleration delta_spd = self.aporasas.tas - self.tas need_ax = np.abs(delta_spd) > np.abs(bs.sim.simdt * self.perf.axmax) self.ax = need_ax * np.sign(delta_spd) * self.perf.axmax # Update velocities self.tas = np.where(need_ax, self.tas + self.ax * bs.sim.simdt, self.aporasas.tas) self.cas = vtas2cas(self.tas, self.alt) self.M = vtas2mach(self.tas, self.alt) # Turning turnrate = np.degrees(g0 * np.tan(np.where(self.ap.turnphi>self.eps,self.ap.turnphi,self.ap.bankdef) \ / np.maximum(self.tas, self.eps))) delhdg = (self.aporasas.hdg - self.hdg + 180) % 360 - 180 # [deg] self.swhdgsel = np.abs(delhdg) > np.abs(bs.sim.simdt * turnrate) # Update heading self.hdg = np.where( self.swhdgsel, self.hdg + bs.sim.simdt * turnrate * np.sign(delhdg), self.aporasas.hdg) % 360.0 # Update vertical speed (alt select, capture and hold autopilot mode) delta_alt = self.aporasas.alt - self.alt # Old dead band version: # self.swaltsel = np.abs(delta_alt) > np.maximum( # 10 * ft, np.abs(2 * bs.sim.simdt * self.vs)) # Update version: time based engage of altitude capture (to adapt for UAV vs airliner scale) self.swaltsel = np.abs(delta_alt) > 1.05*np.maximum(np.abs(bs.sim.simdt * self.aporasas.vs), \ np.abs(bs.sim.simdt * self.vs)) target_vs = self.swaltsel * np.sign(delta_alt) * np.abs( self.aporasas.vs) delta_vs = target_vs - self.vs # print(delta_vs / fpm) need_az = np.abs(delta_vs) > 300 * fpm # small threshold self.az = need_az * np.sign(delta_vs) * ( 300 * fpm) # fixed vertical acc approx 1.6 m/s^2 self.vs = np.where(need_az, self.vs + self.az * bs.sim.simdt, target_vs) self.vs = np.where(np.isfinite(self.vs), self.vs, 0) # fix vs nan issue
def limits(self, indent_v, indent_vs, indent_h): """ apply limits on indent speed, vertical speed, and altitude """ super(PerfNAP, self).limits(indent_v, indent_vs, indent_h) # if isinstance(self.vmin, np.ndarray): # pass # else: # self.update() indent_v = aero.vtas2cas(indent_v, indent_h) allow_v = np.where(indent_v < self.vmin, self.vmin, indent_v) allow_v = np.where(indent_v > self.vmax, self.vmax, indent_v) allow_v = aero.vcas2tas(indent_v, indent_h) allow_vs = np.where(indent_vs < self.vsmin, self.vsmin, indent_vs) allow_vs = np.where(indent_vs > self.vsmax, self.vsmax, indent_vs) allow_h = np.where(indent_h > self.hmaxalt, self.hmaxalt, indent_h) return allow_v, allow_vs, allow_h
def create(self, n=1): actype = bs.traf.type[-1] """Create new aircraft""" # note: coefficients are initialized in SI units if actype in coeffBS.atype: # aircraft self.coeffidx = coeffBS.atype.index(actype) # engine else: self.coeffidx = 0 if not settings.verbose: if not self.warned: print "Aircraft is using default B747-400 performance." self.warned = True else: print "Flight " + bs.traf.id[ -1] + " has an unknown aircraft type, " + actype + ", BlueSky then uses default B747-400 performance." self.coeffidxlist = np.append(self.coeffidxlist, self.coeffidx) self.mass = np.append(self.mass, coeffBS.MTOW[self.coeffidx]) # aircraft weight self.Sref = np.append( self.Sref, coeffBS.Sref[self.coeffidx]) # wing surface reference area self.etype = np.append( self.etype, coeffBS.etype[self.coeffidx]) # engine type of current aircraft self.engines.append(coeffBS.engines[ self.coeffidx]) # avaliable engine type per aircraft type # speeds self.refma = np.append( self.refma, coeffBS.cr_Ma[self.coeffidx]) # nominal cruise Mach at 35000 ft self.refcas = np.append(self.refcas, vtas2cas(coeffBS.cr_spd[self.coeffidx], 35000 * ft)) # nominal cruise CAS self.gr_acc = np.append( self.gr_acc, coeffBS.gr_acc[self.coeffidx]) # ground acceleration self.gr_dec = np.append( self.gr_dec, coeffBS.gr_dec[self.coeffidx]) # ground acceleration # calculate the crossover altitude according to the BADA 3.12 User Manual self.atrans = ((1000/6.5)*(T0*(1-((((1+gamma1*(self.refcas/a0)*(self.refcas/a0))** \ (gamma2))-1) / (((1+gamma1*self.refma*self.refma)** \ (gamma2))-1))**((-(beta)*R)/g0)))) # limits self.vm_to = np.append(self.vm_to, coeffBS.vmto[self.coeffidx]) self.vm_ld = np.append(self.vm_ld, coeffBS.vmld[self.coeffidx]) self.vmto = np.append(self.vmto, 0.0) self.vmic = np.append(self.vmic, 0.0) self.vmcr = np.append(self.vmcr, 0.0) self.vmap = np.append(self.vmap, 0.0) self.vmld = np.append(self.vmld, 0.0) self.vmin = np.append(self.vmin, 0.0) self.mmo = np.append(self.mmo, coeffBS.max_Ma[self.coeffidx]) # maximum Mach self.vmo = np.append(self.vmo, coeffBS.max_spd[self.coeffidx]) # maximum CAS self.hmaxact = np.append( self.hmaxact, coeffBS.max_alt[self.coeffidx]) # maximum altitude # aerodynamics self.CD0 = np.append( self.CD0, coeffBS.CD0[self.coeffidx]) # parasite drag coefficient self.k = np.append(self.k, coeffBS.k[self.coeffidx]) # induced drag factor self.clmaxcr = np.append( self.clmaxcr, coeffBS.clmax_cr[self.coeffidx]) # max. cruise lift coefficient self.qS = np.append(self.qS, 0.0) # performance - initialise neutrally self.D = np.append(self.D, 0.) self.ESF = np.append(self.ESF, 1.) # flight phase self.phase = np.append(self.phase, 0.) self.bank = np.append(self.bank, 0.) self.post_flight = np.append(self.post_flight, False) # for initialisation, # we assume that ac has yet to take off self.pf_flag = np.append(self.pf_flag, True) # engines # turboprops if coeffBS.etype[self.coeffidx] == 2: if coeffBS.engines[self.coeffidx][0] in coeffBS.propenlist: self.propengidx = coeffBS.propenlist.index( coeffBS.engines[self.coeffidx][0]) else: self.propengidx = 0 if not Perf.warned2: print "prop aircraft is using standard engine. Please check valid engine types per aircraft type" Perf.warned2 = True self.P = np.append( self.P, coeffBS.P[self.propengidx] * coeffBS.n_eng[self.coeffidx]) self.PSFC_TO = np.append(self.PSFC_TO, coeffBS.PSFC_TO[self.propengidx]) self.PSFC_CR = np.append(self.PSFC_CR, coeffBS.PSFC_CR[self.propengidx]) self.ff = np.append(self.ff, 0.) # neutral initialisation # jet characteristics needed for numpy calculations self.rThr = np.append(self.rThr, 1.) self.Thr = np.append(self.Thr, 1.) self.maxthr = np.append(self.maxthr, 1.) self.SFC = np.append(self.SFC, 1.) self.ffto = np.append(self.ffto, 1.) self.ffcl = np.append(self.ffcl, 1.) self.ffcr = np.append(self.ffcr, 1.) self.ffid = np.append(self.ffid, 1.) self.ffap = np.append(self.ffap, 1.) # jet (also default) else: # so coeffBS.etype[self.coeffidx] ==1: if coeffBS.engines[self.coeffidx][0] in coeffBS.jetenlist: self.jetengidx = coeffBS.jetenlist.index( coeffBS.engines[self.coeffidx][0]) else: self.jetengidx = 0 if not self.warned2: print " jet aircraft is using standard engine. Please check valid engine types per aircraft type" self.warned2 = True self.rThr = np.append( self.rThr, coeffBS.rThr[self.jetengidx] * coeffBS.n_eng[self.coeffidx]) # rated thrust (all engines) self.Thr = np.append(self.Thr, coeffBS.rThr[self.jetengidx] * coeffBS.n_eng[self.coeffidx] ) # initialize thrust with rated thrust self.maxthr = np.append( self.maxthr, coeffBS.rThr[self.jetengidx] * coeffBS.n_eng[self.coeffidx] * 1.2) # maximum thrust - initialize with 1.2*rThr self.SFC = np.append(self.SFC, coeffBS.SFC[self.jetengidx]) self.ff = np.append(self.ff, 0.) # neutral initialisation self.ffto = np.append( self.ffto, coeffBS.ffto[self.jetengidx] * coeffBS.n_eng[self.coeffidx]) self.ffcl = np.append( self.ffcl, coeffBS.ffcl[self.jetengidx] * coeffBS.n_eng[self.coeffidx]) self.ffcr = np.append( self.ffcr, coeffBS.ffcr[self.jetengidx] * coeffBS.n_eng[self.coeffidx]) self.ffid = np.append( self.ffid, coeffBS.ffid[self.jetengidx] * coeffBS.n_eng[self.coeffidx]) self.ffap = np.append( self.ffap, coeffBS.ffap[self.jetengidx] * coeffBS.n_eng[self.coeffidx]) # propeller characteristics needed for numpy calculations self.P = np.append(self.P, 1.) self.PSFC_TO = np.append(self.PSFC_TO, 1.) self.PSFC_CR = np.append(self.PSFC_CR, 1.) return
def applygeovec(): # Apply each geovector for areaname, vec in geovecs.items(): if areafilter.hasArea(areaname): swinside = areafilter.checkInside(areaname, traf.lat, traf.lon, traf.alt) insids = set(np.array(traf.id)[swinside]) newids = insids - vec.previnside delids = vec.previnside - insids # Store LNAV/VNAV status of new aircraft for acid in newids: idx = traf.id2idx(acid) vec.prevstatus[acid] = [traf.swlnav[idx], traf.swvnav[idx]] # Revert aircraft who have exited the geovectored area to their original status for acid in delids: idx = traf.id2idx(acid) if idx >= 0: traf.swlnav[idx], traf.swvnav[idx] = vec.prevstatus.pop( acid) vec.previnside = insids # -----Ground speed limiting # For now assume no wind: so use tas as gs if vec.gsmin is not None: casmin = vtas2cas(np.ones(traf.ntraf) * vec.gsmin, traf.alt) usemin = traf.selspd < casmin traf.selspd[swinside & usemin] = casmin[swinside & usemin] traf.swvnav[swinside & usemin] = False if vec.gsmax is not None: casmax = vtas2cas(np.ones(traf.ntraf) * vec.gsmax, traf.alt) usemax = traf.selspd > casmax traf.selspd[swinside & usemax] = casmax[swinside & usemax] traf.swvnav[swinside & usemax] = False #------ Limit Track(so hdg) # Max track interval is 180 degrees to avoid ambiguity of what is inside the interval if None not in [vec.trkmin, vec.trkmax]: # Use degto180 to avodi problems for e.g interval [350,30] usemin = swinside & (degto180(traf.trk - vec.trkmin) < 0 ) # Left of minimum usemax = swinside & (degto180(traf.trk - vec.trkmax) > 0 ) # Right of maximum traf.swlnav[swinside & (usemin | usemax)] = False traf.ap.trk[swinside & usemin] = vec.trkmin traf.ap.trk[swinside & usemax] = vec.trkmax # -----Ground speed limiting # For now assume no wind: so use tas as gs if vec.vsmin is not None: traf.selvs[swinside & (traf.vs < vec.vsmin)] = vec.vsmin traf.swvnav[swinside & (traf.vs < vec.vsmin)] = False # Activate V/S mode by using a slightly higher altitude than current values traf.selalt[swinside & (traf.vs < vec.vsmin)] = traf.alt[swinside & (traf.vs < vec.vsmin)] + \ np.sign(vec.vsmin)*200.*ft if vec.vsmax is not None: traf.selvs[swinside & (traf.vs > vec.vsmax)] = vec.vsmax traf.swvnav[swinside & (traf.vs < vec.vsmax)] = False # Activate V/S mode by using a slightly higher altitude than current values traf.selalt[swinside & (traf.vs > vec.vsmax)] = traf.alt[swinside & (traf.vs > vec.vsmax)] + \ np.sign(vec.vsmax)*200.*ft return
def inflight(v, h, vs, thr0): """Compute thrust ration for inflight""" def dfunc(mratio): d = np.where( mratio<0.85, 0.73, np.where( mratio<0.92, 0.73+(0.69-0.73)/(0.92-0.85)*(mratio-0.85), np.where( mratio<1.08, 0.66+(0.63-0.66)/(1.08-1.00)*(mratio-1.00), np.where( mratio<1.15, 0.63+(0.60-0.63)/(1.15-1.08)*(mratio-1.08), 0.60 ) ) ) ) return d def nfunc(roc): n = np.where(roc<1500, 0.89, np.where(roc<2500, 0.93, 0.97)) return n def mfunc(vratio, roc): m = np.where( vratio<0.67, 0.4, np.where( vratio<0.75, 0.39, np.where( vratio<0.83, 0.38, np.where( vratio<0.92, 0.37, 0.36 ) ) ) ) m = np.where( roc<1500, m-0.06, np.where( roc<2500, m-0.01, m ) ) return m roc = np.abs(np.asarray(vs/aero.fpm)) v = np.where(v < 10, 10, v) mach = aero.vtas2mach(v, h) vcas = aero.vtas2cas(v, h) p = aero.vpressure(h) p10 = aero.vpressure(10000*aero.ft) p35 = aero.vpressure(35000*aero.ft) # approximate thrust at top of climb (REF 2) F35 = (200 + 0.2 * thr0/4.448) * 4.448 mach_ref = 0.8 vcas_ref = aero.vmach2cas(mach_ref, 35000*aero.ft) # segment 3: alt > 35000: d = dfunc(mach/mach_ref) b = (mach / mach_ref) ** (-0.11) ratio_seg3 = d * np.log(p/p35) + b # segment 2: 10000 < alt <= 35000: a = (vcas / vcas_ref) ** (-0.1) n = nfunc(roc) ratio_seg2 = a * (p/p35) ** (-0.355 * (vcas/vcas_ref) + n) # segment 1: alt <= 10000: F10 = F35 * a * (p10/p35) ** (-0.355 * (vcas/vcas_ref) + n) m = mfunc(vcas/vcas_ref, roc) ratio_seg1 = m * (p/p35) + (F10/F35 - m * (p10/p35)) ratio = np.where(h>35000*aero.ft, ratio_seg3, np.where(h>10000*aero.ft, ratio_seg2, ratio_seg1)) # convert to maximum static thrust ratio ratio_F0 = ratio * F35 / thr0 return ratio_F0
def create(self, n=1): super(PerfBS,self).create(n) """CREATE NEW AIRCRAFT""" actypes = bs.traf.type[-n:] coeffidx = [] for actype in actypes: if actype in coeffBS.atype: coeffidx.append(coeffBS.atype.index(actype)) else: coeffidx.append(0) if not settings.verbose: if not self.warned: print("Aircraft is using default B747-400 performance.") self.warned = True else: print("Flight " + bs.traf.id[-1] + " has an unknown aircraft type, " + actype + ", BlueSky then uses default B747-400 performance.") coeffidx = np.array(coeffidx) # note: coefficients are initialized in SI units self.coeffidxlist[-n:] = coeffidx self.mass[-n:] = coeffBS.MTOW[coeffidx] # aircraft weight self.Sref[-n:] = coeffBS.Sref[coeffidx] # wing surface reference area self.etype[-n:] = coeffBS.etype[coeffidx] # engine type of current aircraft self.engines[-n:] = [coeffBS.engines[c] for c in coeffidx] # speeds self.refma[-n:] = coeffBS.cr_Ma[coeffidx] # nominal cruise Mach at 35000 ft self.refcas[-n:] = vtas2cas(coeffBS.cr_spd[coeffidx], 35000*ft) # nominal cruise CAS self.gr_acc[-n:] = coeffBS.gr_acc[coeffidx] # ground acceleration self.gr_dec[-n:] = coeffBS.gr_dec[coeffidx] # ground acceleration # calculate the crossover altitude according to the BADA 3.12 User Manual self.atrans[-n:] = ((1000/6.5)*(T0*(1-((((1+gamma1*(self.refcas[-n:]/a0)*(self.refcas[-n:]/a0))** \ (gamma2))-1) / (((1+gamma1*self.refma[-n:]*self.refma[-n:])** \ (gamma2))-1))**((-(beta)*R)/g0)))) # limits self.vm_to[-n:] = coeffBS.vmto[coeffidx] self.vm_ld[-n:] = coeffBS.vmld[coeffidx] self.mmo[-n:] = coeffBS.max_Ma[coeffidx] # maximum Mach self.vmo[-n:] = coeffBS.max_spd[coeffidx] # maximum CAS self.hmaxact[-n:] = coeffBS.max_alt[coeffidx] # maximum altitude # self.vmto/vmic/vmcr/vmap/vmld/vmin are initialised as 0 by super.create # aerodynamics self.CD0[-n:] = coeffBS.CD0[coeffidx] # parasite drag coefficient self.k[-n:] = coeffBS.k[coeffidx] # induced drag factor self.clmaxcr[-n:] = coeffBS.clmax_cr[coeffidx] # max. cruise lift coefficient self.ESF[-n:] = 1. # self.D/qS are initialised as 0 by super.create # flight phase self.pf_flag[-n:] = 1 # self.phase/bank/post_flight are initialised as 0 by super.create # engines self.n_eng[-n:] = coeffBS.n_eng[coeffidx] # Number of engines turboprops = self.etype[-n:] == 2 propidx = [] jetidx = [] for engine in self.engines[-n:]: # engine[0]: default to first engine in engine list for this aircraft if engine[0] in coeffBS.propenlist: propidx.append(coeffBS.propenlist.index(engine[0])) else: propidx.append(0) if engine[0] in coeffBS.jetenlist: jetidx.append(coeffBS.jetenlist.index(engine[0])) else: jetidx.append(0) propidx=np.array(propidx) jetidx =np.array(jetidx) # Make two index lists of the engine type, assuming jet and prop. In the end, choose which one to use self.P[-n:] = np.where(turboprops, coeffBS.P[propidx]*self.n_eng[-n:] , 1.) self.PSFC_TO[-n:] = np.where(turboprops, coeffBS.PSFC_TO[propidx]*self.n_eng[-n:], 1.) self.PSFC_CR[-n:] = np.where(turboprops, coeffBS.PSFC_CR[propidx]*self.n_eng[-n:], 1.) self.rThr[-n:] = np.where(turboprops, 1. , coeffBS.rThr[jetidx]*coeffBS.n_eng[coeffidx]) # rated thrust (all engines) self.Thr[-n:] = np.where(turboprops, 1. , coeffBS.rThr[jetidx]*coeffBS.n_eng[coeffidx]) # initialize thrust with rated thrust self.Thr_pilot[-n:] = np.where(turboprops, 1. , coeffBS.rThr[jetidx]*coeffBS.n_eng[coeffidx]) # initialize thrust with rated thrust self.maxthr[-n:] = np.where(turboprops, 1. , coeffBS.rThr[jetidx]*coeffBS.n_eng[coeffidx]*1.2) # maximum thrust - initialize with 1.2*rThr self.SFC[-n:] = np.where(turboprops, 1. , coeffBS.SFC[jetidx] ) self.ffto[-n:] = np.where(turboprops, 1. , coeffBS.ffto[jetidx]*coeffBS.n_eng[coeffidx]) self.ffcl[-n:] = np.where(turboprops, 1. , coeffBS.ffcl[jetidx]*coeffBS.n_eng[coeffidx]) self.ffcr[-n:] = np.where(turboprops, 1. , coeffBS.ffcr[jetidx]*coeffBS.n_eng[coeffidx]) self.ffid[-n:] = np.where(turboprops, 1. , coeffBS.ffid[jetidx]*coeffBS.n_eng[coeffidx]) self.ffap[-n:] = np.where(turboprops, 1. , coeffBS.ffap[jetidx]*coeffBS.n_eng[coeffidx])
def create(self, n=1): super(PerfBS, self).create(n) """CREATE NEW AIRCRAFT""" actypes = bs.traf.type[-n:] coeffidx = [] for actype in actypes: if actype in coeffBS.atype: coeffidx.append(coeffBS.atype.index(actype)) else: coeffidx.append(0) if not settings.verbose: if not self.warned: print( "Aircraft is using default B747-400 performance.") self.warned = True else: print("Flight " + bs.traf.id[-1] + " has an unknown aircraft type, " + actype + ", BlueSky then uses default B747-400 performance.") coeffidx = np.array(coeffidx) # note: coefficients are initialized in SI units self.coeffidxlist[-n:] = coeffidx self.mass[-n:] = coeffBS.MTOW[coeffidx] # aircraft weight self.Sref[-n:] = coeffBS.Sref[coeffidx] # wing surface reference area self.etype[-n:] = coeffBS.etype[ coeffidx] # engine type of current aircraft self.engines[-n:] = [coeffBS.engines[c] for c in coeffidx] # speeds self.refma[-n:] = coeffBS.cr_Ma[ coeffidx] # nominal cruise Mach at 35000 ft self.refcas[-n:] = vtas2cas(coeffBS.cr_spd[coeffidx], 35000 * ft) # nominal cruise CAS self.gr_acc[-n:] = coeffBS.gr_acc[coeffidx] # ground acceleration self.gr_dec[-n:] = coeffBS.gr_dec[coeffidx] # ground acceleration # calculate the crossover altitude according to the BADA 3.12 User Manual self.atrans[-n:] = ((1000/6.5)*(T0*(1-((((1+gamma1*(self.refcas[-n:]/a0)*(self.refcas[-n:]/a0))** \ (gamma2))-1) / (((1+gamma1*self.refma[-n:]*self.refma[-n:])** \ (gamma2))-1))**((-(beta)*R)/g0)))) # limits self.vm_to[-n:] = coeffBS.vmto[coeffidx] self.vm_ld[-n:] = coeffBS.vmld[coeffidx] self.mmo[-n:] = coeffBS.max_Ma[coeffidx] # maximum Mach self.vmo[-n:] = coeffBS.max_spd[coeffidx] # maximum CAS self.hmaxact[-n:] = coeffBS.max_alt[coeffidx] # maximum altitude # self.vmto/vmic/vmcr/vmap/vmld/vmin are initialised as 0 by super.create # aerodynamics self.CD0[-n:] = coeffBS.CD0[coeffidx] # parasite drag coefficient self.k[-n:] = coeffBS.k[coeffidx] # induced drag factor self.clmaxcr[-n:] = coeffBS.clmax_cr[ coeffidx] # max. cruise lift coefficient self.ESF[-n:] = 1. # self.D/qS are initialised as 0 by super.create # flight phase self.pf_flag[-n:] = 1 # self.phase/bank/post_flight are initialised as 0 by super.create # engines self.n_eng[-n:] = coeffBS.n_eng[coeffidx] # Number of engines turboprops = self.etype[-n:] == 2 propidx = [] jetidx = [] for engine in self.engines[-n:]: if engine in coeffBS.propenlist: propidx.append(coeffBS.propenlist.index(engine)) else: propidx.append(0) if engine in coeffBS.jetenlist: jetidx.append(coeffBS.jetenlist.index(engine)) else: jetidx.append(0) propidx = np.array(propidx) jetidx = np.array(jetidx) # Make two index lists of the engine type, assuming jet and prop. In the end, choose which one to use self.P[-n:] = np.where(turboprops, coeffBS.P[propidx] * self.n_eng[-n:], 1.) self.PSFC_TO[-n:] = np.where( turboprops, coeffBS.PSFC_TO[propidx] * self.n_eng[-n:], 1.) self.PSFC_CR[-n:] = np.where( turboprops, coeffBS.PSFC_CR[propidx] * self.n_eng[-n:], 1.) self.rThr[-n:] = np.where( turboprops, 1., coeffBS.rThr[jetidx] * coeffBS.n_eng[coeffidx]) # rated thrust (all engines) self.Thr[-n:] = np.where( turboprops, 1., coeffBS.rThr[jetidx] * coeffBS.n_eng[coeffidx]) # initialize thrust with rated thrust self.Thr_pilot[-n:] = np.where( turboprops, 1., coeffBS.rThr[jetidx] * coeffBS.n_eng[coeffidx]) # initialize thrust with rated thrust self.maxthr[-n:] = np.where( turboprops, 1., coeffBS.rThr[jetidx] * coeffBS.n_eng[coeffidx] * 1.2) # maximum thrust - initialize with 1.2*rThr self.SFC[-n:] = np.where(turboprops, 1., coeffBS.SFC[jetidx]) self.ffto[-n:] = np.where( turboprops, 1., coeffBS.ffto[jetidx] * coeffBS.n_eng[coeffidx]) self.ffcl[-n:] = np.where( turboprops, 1., coeffBS.ffcl[jetidx] * coeffBS.n_eng[coeffidx]) self.ffcr[-n:] = np.where( turboprops, 1., coeffBS.ffcr[jetidx] * coeffBS.n_eng[coeffidx]) self.ffid[-n:] = np.where( turboprops, 1., coeffBS.ffid[jetidx] * coeffBS.n_eng[coeffidx]) self.ffap[-n:] = np.where( turboprops, 1., coeffBS.ffap[jetidx] * coeffBS.n_eng[coeffidx]) return
def inflight(v, h, vs, thr0): """Compute thrust ration for inflight""" def dfunc(mratio): d = np.where( mratio < 0.85, 0.73, np.where( mratio < 0.92, 0.73 + (0.69 - 0.73) / (0.92 - 0.85) * (mratio - 0.85), np.where( mratio < 1.08, 0.66 + (0.63 - 0.66) / (1.08 - 1.00) * (mratio - 1.00), np.where( mratio < 1.15, 0.63 + (0.60 - 0.63) / (1.15 - 1.08) * (mratio - 1.08), 0.60)))) return d def nfunc(roc): n = np.where(roc < 1500, 0.89, np.where(roc < 2500, 0.93, 0.97)) return n def mfunc(vratio, roc): m = np.where( vratio < 0.67, 0.4, np.where( vratio < 0.75, 0.39, np.where(vratio < 0.83, 0.38, np.where(vratio < 0.92, 0.37, 0.36)))) m = np.where(roc < 1500, m - 0.06, np.where(roc < 2500, m - 0.01, m)) return m roc = np.abs(np.asarray(vs / aero.fpm)) v = np.where(v < 10, 10, v) mach = aero.vtas2mach(v, h) vcas = aero.vtas2cas(v, h) p = aero.vpressure(h) p10 = aero.vpressure(10000 * aero.ft) p35 = aero.vpressure(35000 * aero.ft) # approximate thrust at top of climb (REF 2) F35 = (200 + 0.2 * thr0 / 4.448) * 4.448 mach_ref = 0.8 vcas_ref = aero.vmach2cas(mach_ref, 35000 * aero.ft) # segment 3: alt > 35000: d = dfunc(mach / mach_ref) b = (mach / mach_ref)**(-0.11) ratio_seg3 = d * np.log(p / p35) + b # segment 2: 10000 < alt <= 35000: a = (vcas / vcas_ref)**(-0.1) n = nfunc(roc) ratio_seg2 = a * (p / p35)**(-0.355 * (vcas / vcas_ref) + n) # segment 1: alt <= 10000: F10 = F35 * a * (p10 / p35)**(-0.355 * (vcas / vcas_ref) + n) m = mfunc(vcas / vcas_ref, roc) ratio_seg1 = m * (p / p35) + (F10 / F35 - m * (p10 / p35)) ratio = np.where(h > 35000 * aero.ft, ratio_seg3, np.where(h > 10000 * aero.ft, ratio_seg2, ratio_seg1)) # convert to maximum static thrust ratio ratio_F0 = ratio * F35 / thr0 return ratio_F0