Exemplo n.º 1
0
    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)
Exemplo n.º 2
0
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
Exemplo n.º 3
0
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
Exemplo n.º 4
0
    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
Exemplo n.º 5
0
    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
Exemplo n.º 6
0
    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
Exemplo n.º 7
0
    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
Exemplo n.º 8
0
    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
Exemplo n.º 9
0
    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)
Exemplo n.º 10
0
    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
Exemplo n.º 11
0
    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
Exemplo n.º 12
0
    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
Exemplo n.º 13
0
    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
Exemplo n.º 14
0
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
Exemplo n.º 15
0
    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
Exemplo n.º 16
0
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
Exemplo n.º 17
0
    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)
Exemplo n.º 18
0
    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
Exemplo n.º 19
0
    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
Exemplo n.º 20
0
    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
Exemplo n.º 21
0
    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
Exemplo n.º 22
0
    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
Exemplo n.º 23
0
    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
Exemplo n.º 24
0
    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
Exemplo n.º 25
0
    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
Exemplo n.º 26
0
    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
Exemplo n.º 27
0
    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
Exemplo n.º 28
0
    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
Exemplo n.º 29
0
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
Exemplo n.º 30
0
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
Exemplo n.º 31
0
    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])
Exemplo n.º 32
0
    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
Exemplo n.º 33
0
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