r"""Static longitudinal stability: tail volume, downwash, neutral point and static margin.
An aircraft is statically stable in pitch when a nose-up disturbance creates
a nose-down moment, :math:`C_{M,\alpha} < 0`. With the wing-body lift acting
at its aerodynamic centre :math:`h_{ac,wb}` and the tail lift a distance
:math:`l_t` behind the centre of gravity, the moment slope is linear in the
centre-of-gravity position :math:`h = x_{cg}/\bar c` and vanishes at the
*neutral point* :math:`h_n`. The static margin :math:`h_n - h` measures how
far forward of it the centre of gravity sits.
"""
from __future__ import annotations
import numpy as np
from engineeringkit._validation import finite as _finite
from engineeringkit._validation import interval as _interval
from engineeringkit._validation import positive as _positive
__all__ = [
"tail_volume_ratio",
"downwash_gradient",
"neutral_point",
"pitching_moment_slope",
"static_margin",
]
[docs]
def tail_volume_ratio(S_t, l_t, S, c_bar):
r"""Horizontal-tail volume ratio :math:`V_H = l_t S_t / (\bar c S)`.
Parameters
----------
S_t : float or array_like
Horizontal-tail area [m^2], positive.
l_t : float or array_like
Distance from the centre of gravity to the tail aerodynamic centre [m], positive.
S : float or array_like
Wing area [m^2], positive.
c_bar : float or array_like
Mean aerodynamic chord [m], positive.
Returns
-------
float or ndarray
Tail volume ratio [-]; typically 0.3 to 1.
Notes
-----
Inputs are raw SI numbers with no dimensional metadata; see
:doc:`/guides/aerodynamics`.
Examples
--------
>>> round(float(tail_volume_ratio(3.0, 5.0, 16.0, 1.5)), 4)
0.625
"""
_positive(S_t=S_t, l_t=l_t, S=S, c_bar=c_bar)
return np.asarray(l_t) * S_t / (c_bar * S)
[docs]
def downwash_gradient(a_w, AR):
r"""Downwash gradient at the tail, :math:`d\varepsilon/d\alpha = 2a_w/(\pi AR)`.
Far behind an elliptically loaded wing the downwash angle is
:math:`\varepsilon = 2C_L/(\pi AR)`, twice its value at the wing.
Parameters
----------
a_w : float or array_like
Wing lift-curve slope [1/rad], positive.
AR : float or array_like
Wing aspect ratio [-], positive.
Returns
-------
float or ndarray
:math:`d\varepsilon/d\alpha` [-].
Notes
-----
A far-wake estimate; a tail close behind the wing sees less. Inputs are
raw SI numbers with no dimensional metadata; see
:doc:`/guides/aerodynamics`.
Examples
--------
>>> round(float(downwash_gradient(4.8, 8.0)), 4)
0.382
"""
_positive(a_w=a_w, AR=AR)
return 2 * np.asarray(a_w) / (np.pi * AR)
def _check_tail(a_wb, a_t, V_H, deps_dalpha, eta):
_positive(a_wb=a_wb, a_t=a_t, V_H=V_H)
_interval("deps_dalpha", deps_dalpha, 0, 1, closed="left")
_interval("eta", eta, 0, 1, closed="right")
[docs]
def neutral_point(h_ac_wb, a_wb, a_t, V_H, deps_dalpha=0.0, eta=1.0):
r"""Stick-fixed neutral point :math:`h_n = h_{ac,wb} + \eta V_H \frac{a_t}{a_{wb}}\left(1 - \frac{d\varepsilon}{d\alpha}\right)`.
Parameters
----------
h_ac_wb : float or array_like
Wing-body aerodynamic centre as a fraction of the mean chord from
its leading edge [-].
a_wb : float or array_like
Wing-body lift-curve slope [1/rad], positive.
a_t : float or array_like
Tail lift-curve slope [1/rad], positive.
V_H : float or array_like
Tail volume ratio [-], positive (:func:`tail_volume_ratio`).
deps_dalpha : float or array_like, optional
Downwash gradient at the tail [-] in ``[0, 1)`` (:func:`downwash_gradient`).
eta : float or array_like, optional
Tail efficiency, the tail-to-free-stream dynamic-pressure ratio [-], in ``(0, 1]``.
Returns
-------
float or ndarray
Neutral point as a fraction of the mean chord [-].
Notes
-----
Inputs are raw SI numbers with no dimensional metadata; see
:doc:`/guides/aerodynamics`.
References
----------
J. D. Anderson, *Introduction to Flight*, 8th ed. (McGraw-Hill, 2016),
secs. 7.5-7.10.
Examples
--------
>>> round(float(neutral_point(0.24, 4.8, 4.0, 0.6, 0.38)), 4)
0.55
"""
_finite(h_ac_wb=h_ac_wb)
_check_tail(a_wb, a_t, V_H, deps_dalpha, eta)
return np.asarray(h_ac_wb) + eta * np.asarray(V_H) * a_t / a_wb * (1 - np.asarray(deps_dalpha))
[docs]
def pitching_moment_slope(h, h_ac_wb, a_wb, a_t, V_H, deps_dalpha=0.0, eta=1.0):
r"""Moment-curve slope about the centre of gravity, :math:`C_{M,\alpha} = a_{wb}(h - h_n)`.
Parameters
----------
h : float or array_like
Centre of gravity as a fraction of the mean chord [-].
h_ac_wb, a_wb, a_t, V_H, deps_dalpha, eta
As in :func:`neutral_point`.
Returns
-------
float or ndarray
:math:`dC_{M,cg}/d\alpha` [1/rad]; negative for a statically stable aircraft.
Notes
-----
Inputs are raw SI numbers with no dimensional metadata; see
:doc:`/guides/aerodynamics`.
Examples
--------
>>> round(float(pitching_moment_slope(0.3, 0.24, 4.8, 4.0, 0.6, 0.38)), 4)
-1.2
"""
_finite(h=h)
return np.asarray(a_wb) * (np.asarray(h) - neutral_point(h_ac_wb, a_wb, a_t, V_H, deps_dalpha, eta))
[docs]
def static_margin(h_n, h):
r"""Static margin :math:`h_n - h`, the neutral point's distance behind the centre of gravity.
Parameters
----------
h_n : float or array_like
Neutral point as a fraction of the mean chord [-].
h : float or array_like
Centre of gravity as a fraction of the mean chord [-].
Returns
-------
float or ndarray
Static margin [-]; positive for a statically stable aircraft
(5-15% of the chord for most transport aircraft).
Notes
-----
Inputs are raw SI numbers with no dimensional metadata; see
:doc:`/guides/aerodynamics`.
Examples
--------
>>> round(float(static_margin(0.55, 0.3)), 4)
0.25
"""
_finite(h_n=h_n, h=h)
return np.asarray(h_n) - h