Skip to main content

ProfileData

Struct ProfileData 

Source
pub struct ProfileData {
Show 50 fields pub name: String, pub velocity: f64, pub bc: f64, pub mass: f64, pub diameter: f64, pub drag_model: String, pub twist_rate: Option<f64>, pub sight_height: Option<f64>, pub zero_distance: Option<f64>, pub units: String, pub temperature: f64, pub pressure: f64, pub humidity: f64, pub altitude: f64, pub bullet_name: Option<String>, pub created: Option<String>, pub wind_speed: Option<f64>, pub wind_direction: Option<f64>, pub shooting_angle: Option<f64>, pub auto_zero: Option<f64>, pub twist_right: Option<bool>, pub use_bc_segments: Option<bool>, pub bullet_length: Option<f64>, pub elevation_click: Option<String>, pub windage_click: Option<String>, pub bc_segments: Option<Vec<ProfileBcSegment>>, pub drag_curve: Option<Vec<ProfileDragPoint>>, pub dsf_points: Option<Vec<DsfPoint>>, pub bc_reference: Option<String>, pub pressure_reference: Option<String>, pub density_altitude: Option<f64>, pub zero_poi_up_m: Option<f64>, pub zero_poi_right_m: Option<f64>, pub sight_offset_lateral_m: Option<f64>, pub elevation_cf: Option<f64>, pub windage_cf: Option<f64>, pub zero_sets: Option<Vec<ProfileZeroSet>>, pub reticle: Option<ReticleDescription>, pub clicks_per_revolution: Option<u32>, pub zero_stop: Option<bool>, pub elevation_travel_up_mil: Option<f64>, pub elevation_travel_down_mil: Option<f64>, pub windage_travel_left_mil: Option<f64>, pub windage_travel_right_mil: Option<f64>, pub turret_elevation_dialed_mil: Option<f64>, pub turret_windage_dialed_mil: Option<f64>, pub hold_bound_up_mil: Option<f64>, pub hold_bound_down_mil: Option<f64>, pub hold_bound_left_mil: Option<f64>, pub hold_bound_right_mil: Option<f64>,
}
Expand description

Saved ballistic profile for quick recall

Fields§

§name: String§velocity: f64§bc: f64§mass: f64§diameter: f64§drag_model: String§twist_rate: Option<f64>§sight_height: Option<f64>§zero_distance: Option<f64>§units: String§temperature: f64§pressure: f64§humidity: f64§altitude: f64§bullet_name: Option<String>§created: Option<String>§wind_speed: Option<f64>§wind_direction: Option<f64>§shooting_angle: Option<f64>§auto_zero: Option<f64>§twist_right: Option<bool>§use_bc_segments: Option<bool>§bullet_length: Option<f64>§elevation_click: Option<String>

Turret elevation click graduation for --adjustment-unit clicks (MBA-1355), e.g. “0.1mil” or “0.25moa” — parsed by parse_click_value at both save-time (validation) and resolve-time (resolve_click_values). Unit-invariant (an angular graduation, not a linear measurement), so converted_to leaves it untouched — see the bc_segments/ drag_curve comment below for why.

§windage_click: Option<String>

Turret windage click graduation for --adjustment-unit clicks (MBA-1355). Falls back to the resolved elevation graduation when unset — see resolve_click_values.

§bc_segments: Option<Vec<ProfileBcSegment>>

Velocity-banded BC breakpoints (MBA-1323 Phase 2: multi-row .a7p G1/G7 import). velocity_mps in each entry is ALWAYS meters/second regardless of this profile’s units field — see ProfileBcSegment. The scalar bc field above is retained as the highest-velocity row for tools that only understand a single BC; this list is the authoritative full schedule once present. None (the pre-Phase-2 shape) means “no velocity-banded schedule was captured” and callers fall back to the scalar bc.

FORWARD-COMPAT WARNING (one-way): #[serde(default)] means this field round-trips safely through readers that predate Phase 2, but “safely” only means deserialization doesn’t error — a pre-Phase-2 (or otherwise un-updated, e.g. stale WASM/bindings) reader silently drops this key and solves with only the scalar bc above. That is a materially different, unwarned answer whenever the schedule’s non-fastest bands matter (empirically confirmed: ~639 m/s vs. ~411 m/s impact velocity for the same imported profile — see CLI_USAGE.md’s “Multi-BC and CUSTOM drag curves” section). There is no sentinel trick available here the way there is for drag_curve/CUSTOM below (a real, plausible-looking bc value is unavoidable for back-compat with single-BC tools), so this direction of version skew degrades silently by design and must stay documented rather than “fixed”.

§drag_curve: Option<Vec<ProfileDragPoint>>

Full Mach/Cd drag curve (MBA-1323 Phase 2: .a7p bc_type == CUSTOM import). When present, the scalar bc/drag_model fields are not physically meaningful for the solve (drag_model reads “CUSTOM”; see map_a7p_to_profile’s CUSTOM handling for why bc is an inert 0.0 sentinel rather than a real coefficient).

FORWARD-COMPAT NOTE: unlike bc_segments above, a reader that predates Phase 2 (or otherwise doesn’t consume this field) is safe by construction, not just by omission: it still sees bc == 0.0 and drag_model == "CUSTOM", so BallisticInputs::validate_for_solve rejects the solve loudly (“bc_value must be finite and greater than zero”) instead of silently running physics under a fabricated coefficient.

§dsf_points: Option<Vec<DsfPoint>>

Mach-keyed drop-scale-factor table (MBA-1357), accumulated one point at a time by the dsf verb. None for every profile with no DSF calibration yet — including every profile saved before this field existed, which loads clean and solves untrued (same #[serde(default)] forward-compat pattern as bc_segments/ drag_curve above: an old reader that predates this field silently drops it on re-save, degrading to untrued drop with no error).

§bc_reference: Option<String>

Which standard atmosphere bc/bc_segments are referenced to (MBA-1365): None (the omitted-field default, and every profile saved before this field existed) or "icao" mean ICAO; "army-standard-metro" declares the older Army Standard Metro reference some vendor-published BCs use instead. Parsed by parse_bc_reference_profile_field, written by bc_reference_profile_field (which never writes "icao" — it stays the omitted default so an untouched profile round-trips with no new key). Unit-invariant, like bc_segments/drag_curve above, so converted_to leaves it untouched.

§pressure_reference: Option<String>

Whether pressure is absolute station pressure or a sea-level-corrected altimeter setting (QNH), mirroring bc_reference (MBA-1397): None (the omitted-field default, and every profile saved before this field existed) or "absolute" mean absolute; "qnh" declares a QNH pressure that must be reduced to station pressure before use. Parsed by parse_pressure_reference_profile_field, written by pressure_reference_profile_field (which never writes "absolute" – it stays the omitted default so an untouched profile round-trips with no new key).

§density_altitude: Option<f64>

Density altitude (MBA-1366), feet imperial / meters metric per units (same convention as altitude). None (the omitted-field default, and every profile saved before this field existed) means no density-altitude override is stored; a saved value supersedes altitude/pressure when the profile is loaded (see trajectory’s --density-altitude for the full precedence rule). converted_to rescales it exactly like altitude since it shares the same feet/meters convention.

§zero_poi_up_m: Option<f64>

Deliberate vertical POI offset AT the zero range (MBA-1359, Kestrel “zero height”): positive = the rifle is deliberately zeroed to impact HIGH by this much at the zero distance. ALWAYS meters regardless of this profile’s units field (same unit-fixed convention as ProfileBcSegment::velocity_mps), so converted_to leaves it untouched. None (the omitted-field default, and every profile saved before this field existed) means no offset; an old reader silently drops it on re-save (the bc_segments forward-compat pattern).

§zero_poi_right_m: Option<f64>

Deliberate horizontal POI offset AT the zero range (MBA-1359, Kestrel “zero offset”): positive = impacts RIGHT. ALWAYS meters, like zero_poi_up_m above.

§sight_offset_lateral_m: Option<f64>

Lateral sight-to-bore mount offset (MBA-1396, offset-mounted optics): positive = sight RIGHT of bore. ALWAYS meters (unit-fixed like the zero POI fields above), so converted_to leaves it untouched; same #[serde(default)] forward-compat pattern (an old reader silently drops it on re-save).

§elevation_cf: Option<f64>

Elevation-axis scope tracking correction factor from a tall-target test (MBA-1358, Litz), stored as the published ratio actual measured travel / dialed travel (0.95 = the scope under-tracks by 5%). Elevation dial-unit outputs (mil/MOA/SMOA/IPHY/clicks) are DIVIDED by this factor — an under-tracking scope needs more dial — and dialed truing observations are MULTIPLIED by it (scope-dial -> true angular); raw drop inches never scale. NOTE on conventions: Kestrel’s “Scope Cal” MULTIPLIES its factor into the solution because it stores the reciprocal (dialed/actual); we divide because we store the published actual/dialed — same physics, opposite bookkeeping. Dimensionless, so converted_to leaves it untouched. Validated on load: must be strictly between 0.5 and 1.5 (a factor outside that band means the tall-target test went wrong, not that the scope does). None = 1.0 = no correction. Derive with ballistics tall-target; overridden by --elevation-cf.

§windage_cf: Option<f64>

Windage-axis scope tracking correction factor (MBA-1358), same contract and direction as elevation_cf: windage-axis dial-unit outputs (including mover lead/ring) are divided by it; overridden by --windage-cf.

§zero_sets: Option<Vec<ProfileZeroSet>>

Named zero sets (MBA-1360): alternate zero distances and per-load dial corrections (Lapua Sight-In POI / ATrag zero zones / Strelok multi-zero class). Managed by profile zero-set add|remove|list; selected at solve time with --zero-set NAME. Nothing here applies unless a set is explicitly selected — the profile’s own zero_distance/auto_zero remain the master zero.

FORWARD-COMPAT (the bc_segments pattern, deliberately): #[serde(default)] means a reader that predates this field loads the profile cleanly and solves with the master zero — which is exactly what a CURRENT reader does when no --zero-set is selected, so an old reader can never silently produce a different default answer. Requesting an alternate set on an old binary fails loudly at the flag (--zero-set is an unknown argument there). The one-way skew is re-SAVING: an old reader that rewrites the profile silently drops this key (documented, like bc_segments; there is no sentinel trick available that wouldn’t corrupt the master-zero fields old readers rely on).

§reticle: Option<ReticleDescription>

The optic’s reticle (MBA-1361), so reticle hold --profile NAME can place a firing solution without being handed a description every time. Set with profile save --reticle-json <file>; carried forward untouched by a re-save.

Angular data only (milliradians from the optical center), so ProfileData::converted_to (in main.rs) leaves it alone for the same reason it leaves elevation_click alone — a subtension is not a linear measurement.

FORWARD-COMPAT (the bc_segments pattern): #[serde(default)] means a reader that predates this field loads the profile cleanly. Nothing about a trajectory depends on it — it is a display/hold aid consumed only by the reticle command — so an old reader cannot produce a different ballistic answer because of it; it simply has no reticle verb. The one-way skew is re-SAVING, which drops the key, exactly as documented for bc_segments and zero_sets.

§clicks_per_revolution: Option<u32>

Turret mechanics and reticle hold bounds (MBA-1348): twelve fields (this one through hold_bound_right_mil below) assembled by ProfileData::optic_profile into a ballistics_engine::optic::OpticProfile for the dial/hold/hybrid correction planner. Every one is independently Option and, like elevation_click/windage_click above, ALWAYS stored in mil (or its own natural type for clicks_per_revolution/zero_stop) regardless of this profile’s units field — angular turret/reticle geometry, not a linear measurement — so converted_to leaves all twelve untouched. Set with profile save --clicks-per-rev/--zero-stop/--travel-up/--travel-down/--windage-travel-left/ --windage-travel-right/--turret-elev/--turret-wind/--hold-up/--hold-down/ --hold-left/--hold-right; validated at save time via optic_profile() + OpticProfile::validate() (a profile can never be saved with, say, a dialed turret state outside its own declared travel, or a non-positive click size).

FORWARD-COMPAT (the bc_segments pattern, deliberately): #[serde(default)] means a reader that predates these fields loads the profile cleanly with every one of them absent — identical to what a CURRENT reader does for a profile that never set them, so an old reader can never silently produce a different ballistic answer because of them (nothing about a trajectory solve reads them; only a later dial/hold planner does). The one-way skew is re-SAVING on an old binary, which silently drops all twelve keys, exactly as documented for bc_segments/ zero_sets/reticle above.

This field specifically: click detents per full turret revolution, for turrets whose cap marks revolutions at all (many hunting turrets do not). None means unknown/not applicable, never a specific count.

§zero_stop: Option<bool>

Whether the elevation turret hard-stops at its lowest travel so it cannot be dialed below zero (MBA-1348) — purely descriptive metadata, never read by plan_corrections (see OpticProfile::zero_stop’s own doc comment for why). None (the omitted-field default) means not recorded; optic_profile() treats that the same as Some(false).

§elevation_travel_up_mil: Option<f64>

Elevation travel remaining UP from the current zero (not the turret’s mechanical bottom), DIAL-space mil (MBA-1348). Required together with elevation_travel_down_miloptic_profile() returns a named-field Err if only one of the pair is set, rather than silently treating the unset half as zero travel (a specific, likely-false physical claim, not an honest “unknown”).

§elevation_travel_down_mil: Option<f64>

Elevation travel remaining DOWN from the current zero, DIAL-space mil (MBA-1348). See elevation_travel_up_mil.

§windage_travel_left_mil: Option<f64>

Windage travel remaining LEFT from the current zero, DIAL-space mil (MBA-1348) — TravelLimits::down_mil on the windage axis (see that type’s doc comment for the left/down convention). Required together with windage_travel_right_mil, like elevation_travel_up_mil/_down_mil above.

§windage_travel_right_mil: Option<f64>

Windage travel remaining RIGHT from the current zero, DIAL-space mil (MBA-1348) — TravelLimits::up_mil on the windage axis. See windage_travel_left_mil.

§turret_elevation_dialed_mil: Option<f64>

The elevation turret’s current dialed offset from zero, DIAL-space mil, signed: positive is dialed UP (MBA-1348). Required together with turret_windage_dialed_miloptic_profile() returns a named-field Err if only one axis of the pair is set, rather than silently assuming the other reads zero.

§turret_windage_dialed_mil: Option<f64>

The windage turret’s current dialed offset from zero, DIAL-space mil, signed: positive is dialed RIGHT (MBA-1348). See turret_elevation_dialed_mil.

§hold_bound_up_mil: Option<f64>

The reticle’s usable hold extent ABOVE center, TRUE angular mil (MBA-1348) — see HoldBounds, an explicit spec input (manufacturer spec sheet or bench measurement), never derived from this profile’s own reticle field. Required together with hold_bound_down_mil/hold_bound_left_mil/hold_bound_right_mil — all four or none.

§hold_bound_down_mil: Option<f64>

The reticle’s usable hold extent BELOW center, TRUE angular mil (MBA-1348). See hold_bound_up_mil.

§hold_bound_left_mil: Option<f64>

The reticle’s usable hold extent LEFT of center, TRUE angular mil (MBA-1348). See hold_bound_up_mil.

§hold_bound_right_mil: Option<f64>

The reticle’s usable hold extent RIGHT of center, TRUE angular mil (MBA-1348). See hold_bound_up_mil.

Implementations§

Source§

impl ProfileData

Source

pub fn optic_profile(&self) -> Result<Option<OpticProfile>, String>

Assembles this profile’s OpticProfile (MBA-1348) from elevation_click/ windage_click (parsed via parse_click_value, windage falling back to the resolved elevation graduation — the same precedence resolve_click_values uses) plus the twelve turret/hold fields declared alongside reticle above.

Returns Ok(None) only when NONE of the twelve fields are set: the profile has never been given any turret model at all. Every other combination either succeeds (Ok(Some(..))) or is a named-field Err — including elevation_click itself being unset while ANY of the other eleven fields IS set, since those eleven are meaningless without a click graduation (OpticProfile cannot be constructed without one, structurally) and silently ignoring them would let a save persist turret/hold data no downstream code could ever use.

Does NOT call OpticProfile::validate() itself — callers that need a pre-validated profile (profile save) call it explicitly, so a validation failure is attributed to the operation asking for it rather than to assembly.

Source

pub fn unit_system(&self) -> Result<UnitSystem, String>

Parse this profile’s units field into the CLI unit system it names.

Factored out of the CLI’s converted_to (which now calls this) so the bridge’s profile.validate applies the identical check with the identical message.

Source

pub fn validation_warnings(&self) -> Vec<String>

The cheap invariants the CLI already applies to a saved profile, collected instead of short-circuited: the units string (converted_to’s gate), the MBA-1358 tracking-CF band (load_profile’s gate), and the MBA-1348 turret/optic assembly + validation (profile save’s gate, including parse_click_value on the stored click graduations). No new physics checks — this is exactly the existing load/save surface, aggregated for the bridge’s profile.validate. Empty means the profile passes every one of those gates.

Trait Implementations§

Source§

impl Clone for ProfileData

Source§

fn clone(&self) -> ProfileData

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 ProfileData

Source§

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

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

impl<'de> Deserialize<'de> for ProfileData

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 Serialize for ProfileData

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

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> 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> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<SS, SP> SupersetOf<SS> for SP
where SS: SubsetOf<SP>,

Source§

fn to_subset(&self) -> Option<SS>

The inverse inclusion map: attempts to construct self from the equivalent element of its superset. Read more
Source§

fn is_in_subset(&self) -> bool

Checks if self is actually part of its subset T (and can be converted to it).
Source§

fn to_subset_unchecked(&self) -> SS

Use with care! Same as self.to_subset but without any property checks. Always succeeds.
Source§

fn from_subset(element: &SS) -> SP

The inclusion map: converts self to the equivalent element of its superset.
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.