# Flight to each range

Whether each range's come-up is read off one flight with your zero, or off a fresh flight dialed for that range.

## Generated table

**Setting:** Flight to each range

| Choice | Used by |
|---|---|
| From the zeroed path (default) | Standard, Physics, Classic, Spin |
| Dialed for each range | Custom only |

## What it does

A come-up is the angle you dial above your zero so that the bullet crosses the line of sight at a given range. This setting decides how the solver finds it.

- **From the zeroed path (default):** the solver flies the bullet once, with your zero dialed, and reads how far below the line of sight it is at each range. The come-up for that range is that drop divided by the range. Every range of a card is read off the same flight.
- **Dialed for each range:** the solver flies the bullet again with the elevation dialed for that range, so it travels the arc it would really take, and the come-up is the elevation at which that flight crosses the line of sight at that range. Gravity, drag, the air's height and the spin effects then act along the path the bullet really flies, and the time of flight, the arrival velocity and the Mach number are those of that flight.

## When to choose it

Leave it on the model's own choice (all four models use the zeroed path) when you compare numbers with another ballistics app: the baseline and classic references both report drop divided by range. Choose the dialed flight when you want the come-up, velocity and time of flight of the arc the bullet actually flies. It is offered under the Custom model. The difference is small to about 1,500 yd and grows beyond it, most for slow bullets with a steep arc and on steep shots. A card can take several times longer to compute, less so when the air already follows the bullet's real height (see The algorithm).

## How to picture it

Draw the line of sight from the rifle to a far target, and the bullet's arc above it.

**The zeroed path.** Draw one arc: it leaves the rifle pointed a few mils above the line of sight (your zero), crosses the line near the zero range and then falls away below it. At each range along the line, mark how far below the line the arc is. That gap, divided by the range, is the come-up. One arc serves every range.

**The dialed flights.** Now draw a fan of arcs, one for each range you care about, each leaving at a higher angle than the zeroed arc, because far targets need more elevation. Each arc is drawn only up to its own range, where it comes down onto the line of sight. A far target's arc rises well above the line and spends longer in flight than the zeroed one. The come-up for a range is how much higher than the zero its own arc leaves; the arrival speed, time and sideways drift are read at the end of that arc.

The two pictures differ where the arcs differ: close in they almost coincide, and far out the fan's arcs leave higher than the single zeroed one. On a slope, gravity pulls straight down while the line of sight is tilted, so arcs that leave at different angles meet gravity at different angles to their paths.

## The math

Both values start from the same thing: a flight launched at a bore angle, integrated step by step (fourth-order Runge-Kutta, 0.001 s steps unless changed), and sampled where it crosses each range along the line of sight. This page owns only how the bore angle is chosen and how the answer is read.

| Symbol | Meaning | Unit |
|---|---|---|
| $R$ | range along the line of sight | m |
| $\theta$ | inclination of the line of sight, positive uphill | rad |
| $\beta_0$ | zero bore angle: the angle above the line of sight at which the zeroed flight crosses the line at the zero range | rad |
| $d$ | dial: the angle added to the zero bore angle | rad |
| $y(R;d)$ | height of the flight launched at bore angle $\beta_0 + d$ above the line of sight, measured at right angles to it, at range $R$ (negative below) | m |
| $E$ | the come-up angle, before the aerodynamic jump, the drop scale factor and the scope's elevation tracking factor | rad |
| $\beta$ | bore angle of a flight, $\beta_0 + d$ | rad |
| $v_0$ | muzzle velocity | m/s |
| $\varphi$ | angle of the bullet's path above the line of sight at some instant | rad |
| $g$ | gravity | m/s² |
| $g_\perp$ | gravity's component across the bullet's path | m/s² |
| $E_i$ | the zeroed come-up of the $i$-th range of the card, $-y(R_i;0)/R_i$ | rad |
| $L$, $H$ | the least and the greatest of 0 and all the $E_i$ | rad |
| $p$ | padding added at both ends of the dial grid | rad |
| $d_j$ | the seven dials flown, $j = 0 \dots 6$ | rad |
| $k$, $a$ | the grid interval the dial lies in, and the first of the four grid dials used | none |
| $Y_i(d)$ | the cubic through four neighbouring $y(R_i;d_j)$, as a function of the dial | m |
| $Q_i(d)$ | the same cubic through the values of another flight quantity (time, speed, Mach, sideways offset) at range $R_i$ | varies |
| $h$ | step of the slope estimate | rad |

Constants: 7 dials; padding $p = 0.15\,(H - L)$; a span $H - L$ of at most $10^{-9}$ rad counts as "every range needs no dial"; slope step $h = 10^{-6}$ rad; Newton stops when a step is under $10^{-10}$ rad, or after 20 steps; 785 mil (45°) is the largest elevation the solver accepts.

**`'zeroed'`.** One flight at the zero bore angle:

$$E = -\frac{y(R;\,0)}{R}$$

With `angle` set to `'true'` the same drop is turned into an angle with the arctangent, $E = -\arctan\!\big(y(R;0)/R\big)$ (the `angle` option owns that choice; see [angle](angle.md)). Time, velocity, Mach, sideways offset and the Spin model's outputs are those of this one flight at range $R$.

**`'dialed'`.** The come-up is the dial at which the flight comes down onto the line of sight at the range:

$$y(R;\,d) = 0 \qquad E = d$$

so the bore angle of the flight is $\beta = \beta_0 + d$ and every term of the equations of motion acts on that flight. What differs between a zeroed and a dialed flight is only the launch angle: the bullet leaves with velocity $v_0(\cos\beta,\ \sin\beta)$, from a point the sight height below the line of sight. Gravity is fixed in the earth, so in the frame of the line of sight it is $(-g\sin\theta,\ -g\cos\theta)$. A path rising at angle $\varphi$ above the line of sight feels gravity across it as

$$g_\perp = g\cos(\theta + \varphi)$$

and drag acts along the path. A zeroed flight leaves at the zero bore angle, a few mils; a dialed flight to a far range leaves at the whole come-up angle. For small $\varphi$, $g_\perp \approx g\cos\theta - g\,\varphi\sin\theta$, and the derivative with respect to $\varphi$, $-g\sin(\theta+\varphi)$, is negative on an uphill shot, positive on a downhill one while $|\varphi| < |\theta|$, and vanishes on a level shot (where only the second-order term $-\tfrac12 g\varphi^2$ remains).

**Finding $d$.** Flying one flight per range from the muzzle would cost one flight per row, so the solver flies the card at seven fixed dials and interpolates. With $E_i$ the zeroed come-up of range $R_i$:

$$L = \min(0,\ E_1,\dots,E_n) \qquad H = \max(0,\ E_1,\dots,E_n) \qquad p = 0.15\,(H - L)$$

$$d_j = L - p + \frac{j}{6}\,(H - L + 2p) \qquad j = 0,\dots,6$$

If $H - L \le 10^{-9}$ the zeroed flight is returned as it is and $E = E_i$. Otherwise each $d_j$ is flown over all the ranges. For range $R_i$ the heights $y(R_i;d_j)$ are interpolated by a cubic through four neighbouring dials. Let $k$ be the index with $d_k < d \le d_{k+1}$ (at most 5; 0 for a dial below the grid; 5 for one beyond it), and let $a = \max\!\big(0,\ \min(3,\ k-1)\big)$ be the first of the four dials used:

$$Y_i(d) = \sum_{m=a}^{a+3} y(R_i;d_m)\prod_{\substack{q=a\\ q\ne m}}^{a+3}\frac{d - d_q}{d_m - d_q}$$

The dial is the root of $Y_i$, found by Newton's method from $d = E_i$ with a one-sided slope:

$$d \leftarrow d - \frac{Y_i(d)}{\big(Y_i(d+h) - Y_i(d)\big)/h}$$

Every other quantity is read off the same grid with the same cubic at the dial found: $Q_i(d)$ for time of flight, speed, Mach number, sideways offset and, for the Spin model, stability, spin rate and yaw of repose. The row's height above the line of sight is $0$ by construction, so the drop reported as `drop_in` is still the zeroed flight's, under either value.

**What the come-up becomes.** Under `'zeroed'` the row's come-up is $1000\,E$ mil with $E$ as above. Under `'dialed'` it is $1000\,d$ mil. From there on the two are treated alike: the aerodynamic jump is subtracted and the drop scale factor at the row's Mach number multiplies it; an error is raised if the result exceeds 785 mil; the scope's elevation tracking factor then divides it. An error is also raised when a result is not a finite number.

## The algorithm

Where it runs: inside the function that builds a range card (the one `solve` uses with a single range), after the zero has been found and the zeroed flight has been flown, and before spin drift, jump and the drop scale factor are applied. The branches, in the order the code tests them:

```text
fitCard(profile, env, target, ranges, options) -> one row per range
  1. if options.curvature == 'on':
         solve each range separately with its own line-of-sight dip, curvature off, and return        // the dip differs per range
  2. bore0 = zeroAngle(flight input, zero range, zero slope, zero height)         // search, at most 50 flights to the zero range
  3. sorted = ranges, ascending
  4. flightsFor(wind):  return fly(input with this wind, bore0, sorted)            // the zeroed flight over every range
     s1 = flightsFor(wind 1)
     s2 = s1 if wind 2 equals wind 1 or the target has wind segments, else flightsFor(wind 2)
  5. if options.lapse is 'path' or 'spinref' and options.flight != 'dialed':
         refine s1 and s2 by the air-height fixed point                              // owned by lapse, see lapse.md
  6. if options.flight == 'dialed':
         (s1, dial1) = dialRows(s1, wind 1)
         (s2, dial2) = s1 if no second wind, else dialRows(s2, wind 2)
  7. per row: come-up = dial1[i]*1000 if dialed, else drop/range*1000 (or atan, by angle);
             subtract the jump, multiply by the drop scale factor at the row's Mach; fail above 785 mil; divide by the elevation factor

dialRows(zeroedRows, wind) -> (rows, dials)
  E[i] = -zeroedRows[i].y / range[i]                                  // the zeroed come-up of each range
  lo = min(0, E...); hi = max(0, E...)
  if hi - lo <= 1e-9:  return (zeroedRows, E)                         // nothing to interpolate
  pad = 0.15 * (hi - lo)
  knots[j] = lo - pad + (hi - lo + 2*pad) * j / 6, j = 0..6
  passes[j] = fly(input with this wind, bore0 + knots[j], sorted)     // seven full-card flights
  for each row i:
      ys = [ passes[j][i].y for j in 0..6 ]
      d = E[i]
      repeat at most 20 times:
          y    = cubic4(knots, ys, d)
          dy   = (cubic4(knots, ys, d + 1e-6) - y) / 1e-6
          step = y / dy;  d = d - step
          stop if |step| < 1e-10
      dials[i] = d
      row = zeroedRows[i] with z, time, speed, Mach (and, if present, Sg, spin rate, yaw of repose)
            replaced by cubic4(knots, that column of passes, d);  y and range are kept (the drop stays the zeroed one)
  return (rows, dials)

cubic4(xs, ys, x):                                                    // four-point Lagrange
  k = 0; while k < len(xs) - 2 and x > xs[k+1]: k += 1
  a = max(0, min(len(xs) - 4, k - 1))
  return sum over m in a..a+3 of ys[m] * product over q in a..a+3, q != m of (x - xs[q]) / (xs[m] - xs[q])
```

Each flight the pseudocode calls is the same integrator for both values, and the zero is found the same way:

```text
fly(input, bore, ranges) -> one sample per range
  start at x = 0 on the line of sight, y = -(sight height), velocity = muzzle velocity at angle bore above the line of sight
  step with fourth-order Runge-Kutta; each stage evaluates drag, gravity (-g sin(theta), -g cos(theta)), Coriolis and,
      for the Spin model, spin, yaw of repose, lift and Magnus
  when x passes a range, interpolate linearly between the two steps and record height above the line, sideways offset,
      time, speed and Mach

zeroAngle(input, zero range, zero slope, zero height) -> bore0
  bore = 0
  repeat at most 50 times: miss = height of the flight at the zero range minus the zero height; bore -= miss / range; stop when |miss| < 1e-9 m
  (no wind, no Coriolis, in the zero's own air and on the zero's own slope)
```

**What a card costs.** Per wind, `'zeroed'` flies the card once, or seven times when `lapse` is `'path'` or `'spinref'` (the zeroed flight and six fixed aims). `'dialed'` flies it eight times: the zeroed flight and the seven dials. When the two winds differ that doubles (an entered set of wind segments counts as one wind). The zero search and the zero's calm-air drift are the same under both. The seven dial flights run to the farthest range only. The app's help text says a dialed card "takes a little longer to work out".

## On a real shot

Read from the three tables below; every number in these sentences is copied from them. Each cell is the dialed flight's change from the zeroed one. On the two level shots of the first table (Standard model) the dialed flight asks for +0.094 and +0.091 mil more elevation at 3520 yd, and the bullet arrives slower, -4.6 and -4.5 fps. The slope columns have opposite signs: -0.400 mil uphill and +0.581 mil downhill, with arrival velocities of -4.7 and -4.2 fps. Gravity across the path, $g\cos(\theta+\varphi)$ in The math, is the one term that ties the slope to the path angle, and its change with $\varphi$ has opposite signs on the two slopes, as the two columns do; the table does not say how much of each column it accounts for, and does not break down the level columns either. Windage changes by -0.002 to -0.003 mil in this table. The Physics and Spin tables are close to each other: +0.142 mil (Physics, north level) and +0.141 mil (Spin, monolithic tipped bullet), -0.346 and -0.345 mil uphill, +0.619 and +0.616 mil downhill. The Physics model differs from the Standard one in several settings, so the table does not separate which of them makes +0.142 mil against +0.094 mil. On a level shot the dialed arc is longer than the zeroed one, so the bullet arrives later and slower and needs more elevation; the negative velocity cells in all three tables (-4.1 to -4.7 fps) are that slower arrival, and the same test file asserts it (a longer dialed path arrives later and slower).

**Changing only this setting on the Standard model**

The shot: a 350 gr 0.375 in bullet at 3048 fps muzzle velocity, fired at 3520 yd in air of 60 °F, 40 % humidity, 24.43 inHg at the firing line, latitude 42.1°, wind 10 mph from the right (90° to the line of fire), the same wind in every column; site altitude 5,500 ft. In the app, type it as "Site altitude" (shown when the profile's model reads one); left blank, the classic values count from sea level and can differ from this table. All other settings are the Standard model's. In the first column (North, level) the reference arc reaches 223 ft above the line of sight at 2160 yd.

| Value | Shown in the app as | North, level | East, level | North, 10° uphill | North, 10° downhill |
|---|---|---|---|---|---|
| `'zeroed'` | From the zeroed path (default) | reference | reference | reference | reference |
| `'dialed'` | Dialed for each range | +0.094 / -0.003 / -4.6 | +0.091 / -0.003 / -4.5 | -0.400 / -0.002 / -4.7 | +0.581 / -0.003 / -4.2 |

**Each value under the Physics model's other settings**

The shot: a 350 gr 0.375 in bullet at 3048 fps muzzle velocity, fired at 3520 yd in air of 60 °F, 40 % humidity, 24.43 inHg at the firing line, latitude 42.1°, wind 10 mph from the right (90° to the line of fire), the same wind in every column; site altitude 5,500 ft. In the app, type it as "Site altitude" (shown when the profile's model reads one); left blank, the classic values count from sea level and can differ from this table. All other settings are the Physics model's. In the first column (North, level) the reference arc reaches 221 ft above the line of sight at 2150 yd.

| Value | Shown in the app as | North, level | East, level | North, 10° uphill | North, 10° downhill |
|---|---|---|---|---|---|
| `'zeroed'` | From the zeroed path (default) | reference | reference | reference | reference |
| `'dialed'` | Dialed for each range | +0.142 / +0.013 / -4.5 | +0.142 / -0.003 / -4.5 | -0.346 / +0.016 / -4.6 | +0.619 / +0.010 / -4.2 |

**Each value under the Spin model's other settings**

The shot: a 350 gr 0.375 in bullet at 3048 fps muzzle velocity, fired at 3520 yd in air of 60 °F, 40 % humidity, 24.43 inHg at the firing line, latitude 42.1°, wind 10 mph from the right (90° to the line of fire), the same wind in every column; site altitude 5,500 ft. In the app, type it as "Site altitude" (shown when the profile's model reads one); left blank, the classic values count from sea level and can differ from this table. All other settings are the Spin model's. In the first column (North, level, monolithic tipped bullet) the reference arc reaches 220 ft above the line of sight at 2150 yd. Compared with the first column, the bullet differs in "North, 10° uphill" and "North, 10° downhill".

| Value | Shown in the app as | North, level, monolithic tipped bullet | North, 10° uphill | North, 10° downhill |
|---|---|---|---|---|
| `'zeroed'` | From the zeroed path (default) | reference | reference | reference |
| `'dialed'` | Dialed for each range | +0.141 / +0.007 / -4.5 | -0.345 / +0.021 / -4.6 | +0.616 / -0.004 / -4.1 |

Each cell is the change from the reference row (`'zeroed'`) in the same shot, as elevation (mil) / windage (mil) / velocity at the target (fps). Elevation: plus means more elevation must be dialed. Windage: plus means the windage correction moves to the right (dial further right); this is a correction, not where the bullet lands. Velocity: plus means the bullet arrives faster. A zero means no difference at the shown precision.

---

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