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 · 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 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<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
§fn to_subset(&self) -> Option<SS>
fn to_subset(&self) -> Option<SS>
self from the equivalent element of its
superset. Read more§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).§fn to_subset_unchecked(&self) -> SS
fn to_subset_unchecked(&self) -> SS
self.to_subset but without any property checks. Always succeeds.§fn from_subset(element: &SS) -> SP
fn from_subset(element: &SS) -> SP
self to the equivalent element of its superset.