Exemple #1
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
Exemple #2
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)
Exemple #3
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
Exemple #4
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
Exemple #5
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
Exemple #6
0
def compute_thrust_ratio(phase, bpr, v, h, unit='SI'):
    """Computer the dynamic thrust based on engine bypass-ratio, static maximum
    thrust, aircraft true airspeed, and aircraft altitude

    Args:
        phase (int or 1D-array): phase of flight, option: phase.[NA, TO, IC, CL,
            CR, DE, FA, LD, GD]
        bpr (int or 1D-array): engine bypass ratio
        v (int or 1D-array): aircraft true airspeed
        h (int or 1D-array): aircraft altitude

    Returns:
        int or 1D-array: thust in N
    """

    n = len(phase)

    if unit == 'EP':
        v = v * aero.kts
        h = h * aero.ft

    G0 = 0.0606 * bpr + 0.6337
    Mach = aero.vtas2mach(v, h)
    P0 = aero.p0
    P = aero.vpressure(h)
    PP = P / P0

    # thrust ratio at take off
    ratio_takeoff = 1 - 0.377 * (1+bpr) / np.sqrt((1+0.82*bpr)*G0) * Mach \
               + (0.23 + 0.19 * np.sqrt(bpr)) * Mach**2

    # thrust ratio for climb and cruise
    A = -0.4327 * PP**2 + 1.3855 * PP + 0.0472
    Z = 0.9106 * PP**3 - 1.7736 * PP**2 + 1.8697 * PP
    X = 0.1377 * PP**3 - 0.4374 * PP**2 + 1.3003 * PP

    ratio_inflight = A - 0.377 * (1+bpr) / np.sqrt((1+0.82*bpr)*G0) * Z * Mach \
          + (0.23 + 0.19 * np.sqrt(bpr)) * X * Mach**2

    # thrust ratio for descent, considering 15% of inflight model thrust
    ratio_idle = 0.15 * ratio_inflight

    # thrust ratio array
    #   LD and GN assume ZERO thrust
    tr = np.zeros(n)
    tr = np.where(phase == ph.TO, ratio_takeoff, tr)
    tr = np.where((phase == ph.IC) | (phase == ph.CL) | (phase == ph.CR),
                  ratio_inflight, tr)
    tr = np.where(phase == ph.DE, ratio_idle, tr)

    return tr
Exemple #7
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
Exemple #8
0
def compute_thrust_ratio(phase, bpr, v, h, unit='SI'):
    """Computer the dynamic thrust based on engine bypass-ratio, static maximum
    thrust, aircraft true airspeed, and aircraft altitude

    Args:
        phase (int or 1D-array): phase of flight, option: phase.[NA, TO, IC, CL,
            CR, DE, FA, LD, GD]
        bpr (int or 1D-array): engine bypass ratio
        v (int or 1D-array): aircraft true airspeed
        h (int or 1D-array): aircraft altitude

    Returns:
        int or 1D-array: thust in N
    """

    n = len(phase)

    if unit == 'EP':
        v = v * aero.kts
        h = h * aero.ft

    G0 = 0.0606 * bpr + 0.6337
    Mach = aero.vtas2mach(v, h)
    P0 = aero.p0
    P = aero.vpressure(h)
    PP = P / P0

    # thrust ratio at take off
    ratio_takeoff = 1 - 0.377 * (1+bpr) / np.sqrt((1+0.82*bpr)*G0) * Mach \
               + (0.23 + 0.19 * np.sqrt(bpr)) * Mach**2

    # thrust ratio for climb and cruise
    A = -0.4327 * PP**2 + 1.3855 * PP + 0.0472
    Z = 0.9106 * PP**3 - 1.7736 * PP**2 + 1.8697 * PP
    X = 0.1377 * PP**3 - 0.4374 * PP**2 + 1.3003 * PP

    ratio_inflight = A - 0.377 * (1+bpr) / np.sqrt((1+0.82*bpr)*G0) * Z * Mach \
          + (0.23 + 0.19 * np.sqrt(bpr)) * X * Mach**2

    # thrust ratio for descent, considering 15% of inflight model thrust
    ratio_idle = 0.15 * ratio_inflight

    # thrust ratio array
    #   LD and GN assume ZERO thrust
    tr = np.zeros(n)
    tr = np.where(phase==ph.TO, ratio_takeoff, tr)
    tr = np.where((phase==ph.IC) | (phase==ph.CL) | (phase==ph.CR), ratio_inflight, tr)
    tr = np.where(phase==ph.DE, ratio_idle, tr)

    return tr
Exemple #9
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
Exemple #10
0
def tr_takepff(bpr, v, h):
    """Compute thrust ration at take-off"""
    G0 = 0.0606 * bpr + 0.6337
    Mach = aero.vtas2mach(v, h)
    P0 = aero.p0
    P = aero.vpressure(h)
    PP = P / P0

    A = -0.4327 * PP**2 + 1.3855 * PP + 0.0472
    Z = 0.9106 * PP**3 - 1.7736 * PP**2 + 1.8697 * PP
    X = 0.1377 * PP**3 - 0.4374 * PP**2 + 1.3003 * PP

    ratio = A - 0.377 * (1+bpr) / np.sqrt((1+0.82*bpr)*G0) * Z * Mach \
          + (0.23 + 0.19 * np.sqrt(bpr)) * X * Mach**2

    return ratio
Exemple #11
0
def tr_takepff(bpr, v, h):
    """Compute thrust ration at take-off"""
    G0 = 0.0606 * bpr + 0.6337
    Mach = aero.vtas2mach(v, h)
    P0 = aero.p0
    P = aero.vpressure(h)
    PP = P / P0

    A = -0.4327 * PP**2 + 1.3855 * PP + 0.0472
    Z = 0.9106 * PP**3 - 1.7736 * PP**2 + 1.8697 * PP
    X = 0.1377 * PP**3 - 0.4374 * PP**2 + 1.3003 * PP

    ratio = A - 0.377 * (1+bpr) / np.sqrt((1+0.82*bpr)*G0) * Z * Mach \
          + (0.23 + 0.19 * np.sqrt(bpr)) * X * Mach**2

    return ratio
Exemple #12
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
Exemple #13
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
Exemple #14
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