# Calibration goal

What guided calibration aims at for this profile: a shot (distance, angle, air) or a Mach number, and how sure the shooter wants to be there.

## Generated table

**Record:** profile · **Covers:** `calibration_goal`

| Field | Type | Required | Limits in the schema | Solver reads | In a shared log |
|---|---|---|---|---|---|
| `calibration_goal.azimuth_true_deg` | number | no | - | no | rounded to 10° when location is hidden |
| `calibration_goal.env` | object | no | - | no | as stored |
| `calibration_goal.env.altitude_ft` | number | no | min -1500; max 20000 | no | removed when location is hidden |
| `calibration_goal.env.lat_deg` | number | yes, within | - | no | rounded to a whole degree when location is hidden |
| `calibration_goal.env.rh_pct` | number | yes, within | - | no | as stored |
| `calibration_goal.env.station_pressure_inHg` | number | yes, within | - | no | rounded to 0.1 inHg when location is hidden |
| `calibration_goal.env.temp_F` | number | yes, within | - | no | as stored |
| `calibration_goal.error_target_mil` | number | yes, within | - | no | as stored |
| `calibration_goal.inclination_deg` | number | no | - | no | as stored |
| `calibration_goal.kind` | `'distance'` or `'mach'` | yes, within | - | no | as stored |
| `calibration_goal.mach` | number | no | - | no | as stored |
| `calibration_goal.range_yd` | number | no | - | no | as stored |

## What it holds

This optional block of a profile version says where the shooter wants the fitted drag to be trustworthy. It has two arms. A distance goal is a shot: a range, the slope and direction of the line of fire, the air, and an error target. A Mach goal is a speed and an error target. Guided calibration uses it to rank the next target to shoot and to say when the goal is reached. The block is advice for the guide only: no come-up, no fit and no connector answer reads it.

## What to know first

**Absent means walking outward.** With no goal the guide aims at the Mach 0.1 below the slowest group so far (the muzzle Mach with no groups), never lower than 0.6. It then never says "Goal reached".

**Only the app writes it, and only part of it.** The Guide card's goal box takes a distance, an error target and, when the model reads one, a site altitude. The other air values, slope and direction are copied; no screen types a Mach goal or connector tool sets one.

**The two arms treat air differently.** A distance goal is flown in its own stored air. A Mach goal has none: it becomes a range in the air of the latest logged group, so it moves as the weather does.

**"Goal reached" needs data, not only a small number.** The predicted uncertainty at the goal must be at or under the error target, and at least two logged groups of this profile must have a Mach at or below the goal's plus 0.1.

## The fields

**`calibration_goal.kind`** Which arm: `'distance'` or `'mach'`. Required when the block is present. In the distance arm `range_yd`, `inclination_deg`, `azimuth_true_deg`, `env` and `error_target_mil` are all required; in the Mach arm `mach` and `error_target_mil` are. The record schema allows no other value, and an arm may carry only its own members.

**`calibration_goal.range_yd`** Distance arm only: the goal distance, in yards. Example: 3000.

**`calibration_goal.inclination_deg`** Distance arm only: the slope of the line of sight at the goal, in degrees, uphill plus, downhill minus. 0 is level. It is part of the shot the guide solves for the goal's Mach.

**`calibration_goal.azimuth_true_deg`** Distance arm only: the direction of fire at the goal, in degrees clockwise from true north (90 is east). It matters only to effects that depend on direction.

**`calibration_goal.env`** Distance arm only: the air of the goal, one object with four numbers, all required when the object is present, and an optional fifth. They are `calibration_goal.env.temp_F` (degrees Fahrenheit), `calibration_goal.env.rh_pct` (relative humidity, percent), `calibration_goal.env.station_pressure_inHg` (station pressure in inches of mercury, the actual pressure at the goal) and `calibration_goal.env.lat_deg` (latitude, degrees, north plus). Example: 50 °F, 40 %, 27.0 inHg, 38°.

**`calibration_goal.env.altitude_ft`** Distance arm only, optional: the site altitude at the goal, in feet above sea level, -1500 to 20000 (the record schema holds it there). Only a model that reads a site altitude uses it (the Classic model; the [air page](../log/air.md) of the log says which settings read one); absent, those models count from sea level, as before. Example: 5100.

**`calibration_goal.mach`** Mach arm only: the speed the shooter wants the fit to cover. Example: 1.0. The guide turns it into the range at which a level shot reaches that Mach in the latest logged air; that range is 100 yd for a Mach at or above the Mach at 100 yd, and 6000 yd when the bullet never slows to it within 6000 yd.

**`calibration_goal.error_target_mil`** Both arms: how sure the shooter wants to be at the goal, as one standard deviation of the predicted come-up, in mil. Example: 0.10. A target of 0 or less can never be met, so the guide keeps suggesting targets.

**Limits.** The record schema asks only that each value is a number. The goal box accepts a distance above 0, any error target, and a site altitude from -1,500 to 20,000 ft (`5,100` reads as 5100; a blank box removes it; anything else is refused in words); a blank or unreadable target is saved as 0.10. The connector has no input for any member. The conversion to the solver does not look at the block. The guide refuses a distance the load cannot reach, with the message "The goal (N yd) can't be reached with this load.", and a distance of 0 or less gets the same message. The refusal is the solver's: a shot needing more than 45° of elevation. In runs for this page, with a made-up bullet, 9500 yd was accepted and 9900 yd refused.

## Who writes it

**The app.** The Guide card on the Calibrate screen has a "Change goal" button with a "No goal (walk outward)" tick, a "Goal distance (yd)" box and an "Error target (mil)" box ("Error target (MOA)" on an MOA scope since 2026-10-16; the target is still stored in mil). When the profile's model reads a site altitude, a "Site altitude at the goal (ft)" box sits between them (not for a Mach goal kept with a blank distance); otherwise any stored altitude is kept as it was. The card's goal line then names it ("… air at 5,100 ft"). "Save goal (new profile version)" stores a distance goal, or removes the block when the tick is on. The box opens with "No goal" ticked on a profile without a goal; the two boxes appear when it is unticked. A new distance goal takes the air, slope and direction of the goal the card is showing, if it shows a stored distance goal; otherwise it gets a level slope, a direction of 0 and standard air (59 °F, 50 %, 29.92 inHg) at latitude 40°. A profile that already has a Mach goal keeps it when the distance box is left blank (only the target changes); typing a distance turns it into a distance goal. The Session screen shows the guide's one-line result and has no goal control. Nothing on the profile screen types the goal; its sections carry it unchanged into every version they save.

**The connector.** None sets it. `edit_profile` and `create_profile` have no input for it. An edit keeps the block because the tool starts from a copy of the stored version, and `calibrate_profile` keeps it the same way.

**The fitter.** Never. A calibration or a reset keeps the block, as does restoring a deleted profile. Copying a shared profile (in the app or with `copy_shared_profile`) copies it as the reader saw it (the latitude rounded if the share hid location); the [origin](origin.md) and [versions](versions.md) pages describe the two.

## Who reads it

**The guide.** It runs off the main thread in the app. It takes the goal as a shot, finds its Mach, and scores each candidate target by the uncertainty at the goal after five or ten rounds there. What it does with the goal:

| Goal | The goal shot | Goal reached when |
|---|---|---|
| none | the range where a level shot slows to the walking Mach, in the latest logged air | never |
| distance | the stored range, slope, direction and air, no wind | uncertainty at or under the target, with two or more logged groups at or below its Mach plus 0.1 |
| Mach | the range where a level shot reaches that Mach, in the latest logged air | the same rule |

The latest logged air includes that group's site altitude when it has one.

Candidates come from the session's [targets](../log/session-targets.md) or its target window in steps of 100 yd; with neither there are no candidates and the card says to add today's targets. A candidate is left out when it cannot be reached or its come-up needs more elevation than `scope.elev_travel_mil`. For the second case the card prints the dialed come-up, the true come-up divided by the elevation tracking factor, which is what the check compares (since 2026-10-16; before, it printed the true come-up). The guide says "Nothing here gets you further" when the best candidate would cut the uncertainty by less than a tenth, or when no candidate reaches a new slower Mach and the target is not yet met.

A run for this page, by `suggestNext` on made-up groups (made-up 350 gr bullet, Standard model, three groups at 600, 1200 and 2000 yd in 55 °F air at 29 inHg, targets every few hundred yards from 600 to 3000 yd, error target 0.10 mil unless stated). These are my own numbers, not a measurement (on an MOA scope the guide's sentences give the same figures in MOA, since 2026-10-16):

| Goal | Result |
|---|---|
| none | walks to Mach 0.97 (about 2,180 yd); now ±0.14 mil; shoot 2400 yd, 5 rounds, to ±0.08 |
| distance 4400 yd, in a made-up mild, thin-air day | Mach 0.72; now ±1.10 mil; shoot 3000 yd, to ±0.61 |
| distance 2500 yd, same air | now ±0.16 mil; shoot 2400 yd, to ±0.10 |
| distance 1500 yd, same air | "Goal reached: 1500 yd ±0.05 mil (target 0.10)" |
| distance 1500 yd, target 0 | not reached; keeps suggesting 1500 yd, ±0.05 to ±0.04 |
| distance 8000 yd | "Nothing here gets you further"; now ±16 mil |
| distance 10000 yd | refused: "can't be reached with this load" |
| Mach 1.0 | 2,118 yd in the latest logged air; shoot 2400 yd, ±0.12 to ±0.08 |

The goal's own solve also gives the card its line about what the solver answers at the goal: the come-up in use, what the fit alone says and what the book curve says, and a note when they differ by more than the uncertainty.

**The Workbench.** It reads the stored goal of the profile it shows: the goal's range is one of the comparison ranges when it is from 100 to 6000 yd, the goal's air, with its site altitude, is the default air for them (standard air, with none, when there is no goal), and the goal's Mach is marked on the chart of logged shots by Mach. Its two panels that run the guide read the same stored goal (since 2026-10-24, when the placeholder goal was removed).

**The solver and the fitter.** Neither reads it. Of the connector's tools, `next_target` runs the guide with it, as the card does; the tools that return a whole profile show the block as data. `check_profile`, `explain_dope`, `sensitivity` and `calibrate_profile` do not mention it.

## Why it is stored

A trip has a target in mind, such as a long distance in thin air, and the cheapest next group depends on it. Keeping the goal on the profile version means the aim survives a restart, goes with the profile to other devices, and shows in the version history. A distance goal stores its air because the Mach at a distance depends on the air; the goal's place is not the range's. A Mach goal stores none, for shooters who think in speed. The error target is stored so that "reached" means what the shooter chose.

## Changing it

Every version carries the goal in force when it was saved, so the history of versions shows what each calibration was aiming at; the calibration record itself has no goal member.

**No edit category.** The goal is not in the fields `editEffect` compares, so saving a goal never asks keep or reset and never touches the calibration. The Save button says it makes a new profile version, and it does: an edit is one version, and each logged group keeps the version it was shot with (see [versions](versions.md)). A fit does not use the goal, so older groups stay eligible: `ineligibleReason` compares the zero and the like, not the goal.

## Sharing and privacy

Every member reads "as stored" for a reader of a shared log except four, each when the share hides location: `calibration_goal.env.lat_deg` is rounded to a whole degree, `calibration_goal.env.station_pressure_inHg` to 0.1 inHg, `calibration_goal.azimuth_true_deg` to the nearest 10 degrees, and `calibration_goal.env.altitude_ft` is removed. With location shown they are exact. The guide does not run on a shared log; a reader's Workbench does read the goal. What a reader can work out: the goal range and the air show how far and how high the owner plans to shoot (a station pressure of 27 inHg is about 3,000 ft above sea level), and the latitude, even rounded, puts the place in a band about 70 miles wide. The temperature and humidity are not hidden by any switch. A copy of the shared profile carries the block as the reader saw it.

---

[All reference pages](../index.md)
