pub struct NormalForceTable { /* private fields */ }Expand description
The normal force and its center of pressure against Mach number and angle of attack, from
another tool, in place of hpr’s own (crate::AeroModel::with_normal_force_table).
A table is a set of columns, one per angle of attack, each holding C_N/α and the center of
pressure against Mach number. A lookup at Mach M and angle α:
- reads each column at
M(linear, holding the end values outside its Mach range, as the column’s own tables say), then interpolates linearly inαbetween the two columns around it. Below the first column’s angle it holds that column. SoC_N = (C_N/α)·αgives the columns’ normal force back at their own angles, and between them a quadratic inα: a potential-flow term linear inαplus a viscous cross-flow term inα². RASAero II’s viscous part issin² αfrom Mach 0.91 to 1.3 in the Calisto export, whichα²matches within 0.2% to 4°; faster, it grows more slowly, and the quadratic between the columns is an assumption. - Past the last column’s angle
α_n, withs = sin α / sin α_n, the force atα_ngrows assat the last column’s center of pressure, as hpr’s own fins followsin α(the decision record on flight, ADR-011). When the table starts at 0°, the part of that force beyond the 0° slope’s linear share ((C_N/α)(0) · α_n), the restR, grows faster, ass², the cross flow’s form (Galejs; Niskanen 2009 eq. 3.26), which RASAero II’s viscous part takes from Jorgensen (RASAero II Users Manual, 2019, p. 55): the force isC_N(α_n) s + R (s² − s), with the extra term at the station where the rest acts in the split (the one that gives the moment atα_nwith the linear share at the 0° center of pressure). That is exactly the linear share growing assand the rest ass², each at its own center of pressure; the station is held within the rocket (or the table’s own range of centers of pressure,NormalForceTable::lookup), and the linear share within the force. So the force never turns round and is zero tail first, the center of pressure lies between the last column’s and that station whiles ≥ 1(fromα_ntoπ − α_n), and everything is continuous atα_nand in the table’s values. This part is an assumption, not the other tool’s result; the lookup reports it.
The table serializes as its columns and its TableReference, and re-checks them when
read. The decisions are in the record on normal-force overrides, ADR-032.
Implementations§
Source§impl NormalForceTable
impl NormalForceTable
Sourcepub fn new(columns: Vec<NormalForceColumn>) -> Result<Self, AeroError>
pub fn new(columns: Vec<NormalForceColumn>) -> Result<Self, AeroError>
A table from its columns, in increasing angle of attack, on the rocket’s reference area.
§Errors
AeroError::Domain for no columns, an angle outside [0, π/2), or angles that don’t
strictly increase.
Sourcepub fn with_reference(
self,
reference: TableReference,
) -> Result<Self, AeroError>
pub fn with_reference( self, reference: TableReference, ) -> Result<Self, AeroError>
This table with its coefficients on reference. When it differs from the rocket’s,
crate::AeroModel::normal_force rescales by the ratio of the areas.
§Errors
AeroError::Domain for a diameter that isn’t finite and positive.
Sourcepub fn with_reference_diameter_m(
self,
diameter_m: f64,
) -> Result<Self, AeroError>
pub fn with_reference_diameter_m( self, diameter_m: f64, ) -> Result<Self, AeroError>
This table with its coefficients on a circle of diameter diameter_m
(NormalForceTable::with_reference).
§Errors
AeroError::Domain unless diameter_m is finite and positive.
Sourcepub fn columns(&self) -> &[NormalForceColumn]
pub fn columns(&self) -> &[NormalForceColumn]
The columns, in increasing angle of attack.
Sourcepub fn reference(&self) -> TableReference
pub fn reference(&self) -> TableReference
The area the coefficients are on.
Sourcepub fn lookup(
&self,
mach: f64,
alpha_rad: f64,
) -> Result<NormalForceLookup, AeroError>
pub fn lookup( &self, mach: f64, alpha_rad: f64, ) -> Result<NormalForceLookup, AeroError>
The normal force at mach and angle of attack alpha_rad, on the table’s reference area,
as the type’s documentation describes, with the growing share’s center of pressure past
the last column held from the nose tip to twice the table’s largest center of pressure: a
stand-in for the rocket, which the table doesn’t know (NormalForceTable::lookup_within;
a flight passes the rocket’s own length).
§Errors
Sourcepub fn lookup_within(
&self,
mach: f64,
alpha_rad: f64,
stations_m: (f64, f64),
) -> Result<NormalForceLookup, AeroError>
pub fn lookup_within( &self, mach: f64, alpha_rad: f64, stations_m: (f64, f64), ) -> Result<NormalForceLookup, AeroError>
As NormalForceTable::lookup, with the growing share’s center of pressure past the last
column held within stations_m, m aft of the nose tip: crate::AeroModel passes the
rocket, from its nose tip to its aft end.
§Errors
AeroError::Domain for a negative or non-finite Mach number, an angle outside [0, π],
or stations that aren’t finite and in order; and table errors from a column whose Mach
range refuses to extrapolate.
Sourcepub fn from_rasaero_csv(text: &str) -> Result<Self, AeroError>
pub fn from_rasaero_csv(text: &str) -> Result<Self, AeroError>
Reads RASAero II’s aerodynamic export (its Aero Plots screen, File, Export, To CSV File: RASAero II Users Manual, 2019, p. 76), one column per angle of attack in it.
The first non-blank row is the header. The columns used are named Mach, Alpha (degrees),
CN, CN Potential and CP (compared without case or surrounding space); the export’s
others, such as CNalpha (0 to 4 deg) (per rad), a secant slope to 4°, are not read.
- At an angle
α > 0, the column’s slope isCN/αand its center of pressure isCP. - At
α = 0the export’sCNis zero, so the slope is its potential-flow normal force at the smallest positive angleα₁and the same Mach number over that angle,CN Potential(α₁)/α₁: the potential part is linear inα, and the viscous cross-flow part,sin² αthrough Mach 1.3 in the Calisto export, has no slope at zero (faster, how it starts from 0° isn’t in the export, and leaving it out is an assumption). The center of pressure is the export’s atα = 0. CPis in inches (the manual, p. 13) from the nose tip (“distance measured from the nose”, p. 114), converted at 0.0254 m to the inch. hpr’s stations are also aft of the nose tip, so the design must start at the same nose tip as RASAero II’s.- The coefficients are on RASAero II’s reference area, the largest cross-section of the
body (p. 72): the table’s reference is
TableReference::LargestBody, whichcrate::AeroModel::normal_forcerescales to the rocket’s reference area.
The five columns read must hold a number in every row; the others are not read. Within one angle of attack an identical repeated row is skipped, and otherwise the Mach numbers must strictly increase: a Mach number repeated with other values, or out of order, is refused with its line, not sorted. The angles need not all have the same Mach numbers (the export behind RocketPy’s Calisto ends its 4° rows a row early).
§Errors
AeroError::Csvnaming the 1-based line for a missing header column, a field read that isn’t a number, a non-finite value, an angle outside[0°, 90°), a Mach number that doesn’t increase within its angle, a row atα = 0whose Mach number has no row atα₁, an angle with one Mach number (its row’s line), or no rows at a positive angle (line 0).
§Examples
Two Mach numbers at 0° and 2°, with only the columns the reader uses (an export has more):
use hpr_aero::NormalForceTable;
let export = "Mach,Alpha,CN,CN Potential,CP\n\
0.3,0,0,0,40\n\
0.5,0,0,0,40\n\
0.3,2,0.35,0.35,40\n\
0.5,2,0.35,0.35,40\n";
let table = NormalForceTable::from_rasaero_csv(export)?;
// At 1°, halfway between the columns at 0° and 2°, which here agree.
let at = table.lookup(0.4, 1_f64.to_radians())?;
assert!((at.slope_per_rad - 0.35 / 2_f64.to_radians()).abs() < 1e-12);
// 40 inches aft of the nose tip, in meters.
assert!((at.cp_station_m - 1.016).abs() < 1e-12);Trait Implementations§
Source§impl Clone for NormalForceTable
impl Clone for NormalForceTable
Source§fn clone(&self) -> NormalForceTable
fn clone(&self) -> NormalForceTable
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for NormalForceTable
impl Debug for NormalForceTable
Source§impl<'de> Deserialize<'de> for NormalForceTable
impl<'de> Deserialize<'de> for NormalForceTable
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Source§impl PartialEq for NormalForceTable
impl PartialEq for NormalForceTable
Source§fn eq(&self, other: &NormalForceTable) -> bool
fn eq(&self, other: &NormalForceTable) -> bool
self and other values to be equal, and is used by ==.