# JBM cross-reference sidecars

Independent reference trajectories from <https://www.jbmballistics.com/> used to
cross-check `BallisticsEngine.kt`. JBM has no public API, so values are entered by
hand. Driven by `JbmCrossReferenceTest` (in `:ballistics` jvmTest).

## Why
- The engine and JBM are both McCoy point-mass G1/G7 solvers, so at identical
  inputs they should agree to ~0.003 mil. JBM is therefore an external oracle that
  the engine's regenerated references are not.
- For fired cases it separates causes: if engine ≈ JBM but both miss the fired
  DOPE, the error is in the load/atmosphere data; if JBM matches fired and the
  engine does not, it's an engine defect.

## Workflow
1. Run the harness once to (re)generate input sheets:
   `./gradlew :shared:ballistics:jvmTest` (from `scopedope-android/`) or
   `./gradlew :ballistics:jvmTest` (standalone in `scopedope-shared/`).
   See `generated/jbm-input-sheets-<stamp>.md`.
2. For each case you want to cross-check, create `jbm-refs/<case_id>.json` from the
   schema below (or copy `real_139_65creedmoor_140amax.json`). Fill `inputs` from
   the input sheet.
3. Enter the load at jbmballistics.com using the **atmosphere convention** noted in
   the input sheet (altitude + 29.92 inHg sea-level-corrected for altitude-only
   cases; absolute station pressure with altitude 0 when a pressure is known),
   no wind. Read the **elevation** column in **mil**.
4. Paste each distance's value into `trajectory[].elevation_mil` (leave `null` for
   any you haven't entered — they are skipped).
5. Re-run the harness. See `generated/jbm-crossref-<stamp>.md`. Rows where
   |engine − JBM| > 0.01 mil are flagged `DIVERGENCE` and warned on the console.

## Schema
```json
{
  "case_id": "<matches a TestCase id in test_cases/>",
  "calculator": "https://www.jbmballistics.com/",
  "captured_date": "YYYY-MM-DD",
  "inputs": {
    "drag_model": "G1 | G7",
    "bc": 0.294,
    "mv_fps": 2740,
    "zero_yards": 200,
    "sight_height_in": 1.5,
    "altitude_ft": 750,
    "temp_f": 59.0,
    "pressure_hpa": 0,
    "humidity_pct": 0,
    "notes": "atmosphere convention used"
  },
  "trajectory": [
    { "yards": 300, "elevation_mil": null }
  ]
}
```

`generated/` holds harness output (input sheets + cross-ref reports) and can be
regenerated at any time.
