Skip to main content

NormalForceTable

Struct NormalForceTable 

Source
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. So C_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 is sin² α 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, with s = sin α / sin α_n, the force at α_n grows as s at the last column’s center of pressure, as hpr’s own fins follow sin α (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 rest R, grows faster, as s², 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 is C_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 α_n with the linear share at the 0° center of pressure). That is exactly the linear share growing as s and the rest as s², 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 while s ≥ 1 (from α_n to π − α_n), and everything is continuous at α_n and 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

Source

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.

Source

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.

Source

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.

Source

pub fn columns(&self) -> &[NormalForceColumn]

The columns, in increasing angle of attack.

Source

pub fn reference(&self) -> TableReference

The area the coefficients are on.

Source

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

As NormalForceTable::lookup_within.

Source

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.

Source

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 is CN/α and its center of pressure is CP.
  • At α = 0 the export’s CN is 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.
  • CP is 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, which crate::AeroModel::normal_force rescales 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::Csv naming 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 α = 0 whose 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

Source§

fn clone(&self) -> NormalForceTable

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for NormalForceTable

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'de> Deserialize<'de> for NormalForceTable

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl PartialEq for NormalForceTable

Source§

fn eq(&self, other: &NormalForceTable) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl Serialize for NormalForceTable

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl StructuralPartialEq for NormalForceTable

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> DynClone for T
where T: Clone,

Source§

fn __clone_box(&self, _: Private) -> *mut ()

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.