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_mil — optic_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_mil — optic_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
impl ProfileData
Sourcepub fn optic_profile(&self) -> Result<Option<OpticProfile>, String>
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.
Sourcepub fn unit_system(&self) -> Result<UnitSystem, String>
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.
Sourcepub fn validation_warnings(&self) -> Vec<String>
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
impl Clone for ProfileData
Source§fn clone(&self) -> ProfileData
fn clone(&self) -> ProfileData
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 ProfileData
impl Debug for ProfileData
Source§impl<'de> Deserialize<'de> for ProfileData
impl<'de> Deserialize<'de> for ProfileData
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>,
Auto Trait Implementations§
impl Freeze for ProfileData
impl RefUnwindSafe for ProfileData
impl Send for ProfileData
impl Sync for ProfileData
impl Unpin for ProfileData
impl UnsafeUnpin for ProfileData
impl UnwindSafe for ProfileData
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> DeserializeOwned for Twhere
T: for<'de> Deserialize<'de>,
Source§impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
Source§fn to_subset(&self) -> Option<SS>
fn to_subset(&self) -> Option<SS>
self from the equivalent element of its
superset. Read moreSource§fn is_in_subset(&self) -> bool
fn is_in_subset(&self) -> bool
self is actually part of its subset T (and can be converted to it).Source§fn to_subset_unchecked(&self) -> SS
fn to_subset_unchecked(&self) -> SS
self.to_subset but without any property checks. Always succeeds.Source§fn from_subset(element: &SS) -> SP
fn from_subset(element: &SS) -> SP
self to the equivalent element of its superset.