Skip to main content

Numerical launch-angle solver for moving targets under gravity and quadratic drag.

Project description

Official repository notice

This is the only official repository for ballistic-solver.

Official repository: https://github.com/ujinf74/ballistic-solver

Do not download ZIP files, binaries, or installers from third-party copies, mirrors, or reuploads. Use only the Releases page of this repository.

banner

CI Release Native + PyPI PyPI License

ballistic-solver is a native C/C++ numerical solver that computes launch angles to intercept moving targets under gravity and quadratic air drag, with optional wind.

Unlike vacuum / closed-form solvers, this project simulates the projectile and solves the intercept numerically, aiming for robust real-time use even when trajectories are strongly curved.


Quick start

Python (PyPI)

pip install ballistic-solver

Requires Python >= 3.10.

import ballistic_solver as bs

result = bs.solve(
    relPos0=(120, 30, 5),
    relVel=(2, -1, 0),
    v0=90,
    kDrag=0.002,
)

print(result["theta"], result["phi"], result["miss"])
print(result["success"], result["status"], result["message"])

For tighter convergence without manually tuning every knob:

params = bs.params_preset("precise")
result = bs.solve((120, 30, 5), (2, -1, 0), 90, 0.002, params=params)

For repeated solves, Python also provides a thin convenience wrapper:

solver = bs.Solver.preset("precise")
result = solver.solve((120, 30, 5), (2, -1, 0), 90, 0.002)

Demo (Unity)

Highly curved trajectories under strong air drag, still converging to a hit against moving targets.

https://github.com/user-attachments/assets/c0c69cdd-0dd4-4606-9c7d-f21dd002d7f7


Why this solver

Many launch-angle solvers depend on vacuum assumptions or partially linearized models. This project instead simulates the projectile and solves the intercept numerically, targeting robustness in real-time simulations and integration scenarios.


Key properties

  • Moving targets supported
  • Constant-acceleration targets supported via the extended API
  • Strong air resistance (quadratic drag) supported
  • Low / High arc selection (since v0.2)
  • Wind vector supported (since v0.3)
  • Extended C ABI utilities (since v0.4)
  • Quartic vacuum-lead initialization for moving targets (since v0.6)
  • High-arc moving-target auxiliary multi-start fallback (since v0.6.1)
  • Fast / balanced / precise solver presets
  • Physical drag helper: kDrag = 0.5 * rho * Cd * area / mass
  • Robust in strongly nonlinear regimes (no analytic assumptions)
  • Best-effort result returned even without perfect convergence
  • Explicit success / failure reporting (+ diagnostic message)
  • Stable C ABI for multi-language use
  • Header-only C++ core
  • Easy install via PyPI: pip install ballistic-solver

Python API

solve(...)

solve(relPos0, relVel, v0, kDrag, arcMode=None, params=None, relAcc=None) -> dict
  • relPos0: target relative position at t=0 (x,y,z)
  • relVel: target relative velocity (x,y,z)
  • v0: muzzle speed (scalar)
  • kDrag: quadratic drag coefficient
  • arcMode: 0/1 or "low"/"high" (case-insensitive); None keeps the params value
  • params: optional BallisticParams for advanced tuning (gravity, wind, integrator and solver knobs)
  • relAcc: optional constant target relative acceleration (x,y,z); same as solve_accel

Returned dict keys include:

  • success (bool)
  • theta, phi (radians)
  • miss (closest-approach distance)
  • tStar (time of closest approach)
  • relMissAtStar (3-vector miss at tStar)
  • status (SolveStatus integer)
  • message (short diagnostic string)
  • plus convergence diagnostics (iterations, acceptedSteps, lastLambda, lastAlpha)

Utilities

params = bs.params_preset("fast")  # or "balanced", "precise"
kDrag = bs.k_drag_from_physical(
    airDensity=1.225,
    dragCoefficient=0.30,
    area=0.00426,
    mass=0.145,
)

For constant-acceleration targets:

result = bs.solve_accel(
    relPos0=(120, 30, 5),
    relVel=(2, -1, 0),
    relAcc=(0, 0.2, 0),
    v0=90,
    kDrag=0.002,
    params=bs.params_preset("precise"),
)

Solver validity note

The solver internally integrates projectile motion using:

  • 4th-order Runge–Kutta (RK4)
  • Fixed timestep dt
  • Quadratic drag: a = (0, 0, -g) - kDrag * |v - wind| * (v - wind)
  • Wind as air velocity

To match in-game ballistics, your runtime simulation must use the same physical model and integrator configuration.

If your game uses a different integrator (e.g., Euler) or a different timestep, the computed launch angles may not hit even if the solver reports success.


C ABI (stable interface)

Primary intercept API:

void ballistic_inputs_init(BallisticInputs* in);
int32_t ballistic_solve(const BallisticInputs* in, BallisticOutputs* out);

Return value policy:

  • 0: API call completed and out was filled.
  • <0: API-level failure, such as null pointers or an internal exception.
  • Numerical solve success is reported separately by out->success and out->status.
  • ABI v3 adds convergence diagnostics to BallisticOutputs: iterations, acceptedSteps, lastLambda, and lastAlpha.
  • ABI v4 adds a preset field to BallisticInputs (0=Fast, 1=Balanced, 2=Precise). ballistic_inputs_apply_preset records it so that the full preset tuning (line-search, lambda tries, finite-difference step, golden-section) is carried into ballistic_solve, not only dt/tMax/tolMiss/maxIter.

Callers should check both:

int32_t rc = ballistic_solve(&in, &out);
if (rc != 0) {
    /* API call failed */
}
if (!out.success) {
    /* Solver ran but did not satisfy the requested tolerance. */
}

Since v0.4.0, additional utility functions are available:

void ballistic_rk4_step(...);

int32_t ballistic_simulate_trajectory(...);
int32_t ballistic_simulate_trajectory_from_angles(...);

int32_t ballistic_find_closest_approach(...);

int32_t ballistic_vacuum_arc_angles_to_point(...);
void ballistic_initial_guess_vacuum_lead(...);

Additional extended APIs include:

void ballistic_accel_inputs_init(BallisticAccelInputs* in);
int32_t ballistic_solve_accel(const BallisticAccelInputs* in, BallisticOutputs* out);
int32_t ballistic_inputs_apply_preset(BallisticInputs* in, int32_t preset);
int32_t ballistic_k_drag_from_physical(...);
int32_t ballistic_make_relative_motion(...);

See ballistic_solver_c_api.h for full signatures and parameter definitions.

This enables usage from:

  • C / C++
  • Python (ctypes via the C ABI)
  • C# / .NET / Unity (P/Invoke)
  • Unity (UnityEngine.Vector3 wrapper example)
  • Godot 4 (GDExtension addon-style example)
  • Others via FFI

Prebuilt native binaries are provided via GitHub Releases.


Arc mode (since v0.2)

C ABI convention:

  • arcMode = 0 → Low
  • arcMode = 1 → High

High arc example:

https://github.com/user-attachments/assets/4334ed87-597e-4ad4-b21e-c1a1a17e8cd8


Wind (since v0.3)

C ABI convention:

  • wind[3] = air velocity vector (same frame as relPos0/relVel)
  • Drag uses relative airspeed: v_rel = v_projectile - wind

Wind demo:

https://github.com/user-attachments/assets/1cd998cf-34db-4a74-8817-c6393227ef4e


Using prebuilt binaries (C ABI)

Download the archive for your platform from Releases.

Each release contains:

  • Shared library

    • Windows: ballistic_solver.dll
    • Linux: libballistic_solver.so
    • macOS: libballistic_solver.dylib
  • C ABI header: ballistic_solver_c_api.h


C# / Unity usage

A C# P/Invoke example is available in:

examples/dotnet/

On Windows, place ballistic_solver.dll next to the executable (or ensure it is discoverable via PATH), then call ballistic_solve via DllImport.

This works directly inside Unity.


How it works (high level)

  1. Build a vacuum-lead initial guess from the moving-target quartic.
  2. Simulate projectile motion using RK4 integration with drag (+ wind).
  3. Track the closest approach between projectile and target.
  4. Express the miss at closest approach as an angular residual.
  5. Apply a one-shot fixed-point correction before the main iteration.
  6. Solve the nonlinear system using damped least squares (Levenberg–Marquardt).
  7. Accelerate Jacobian updates with Broyden-style refinement.
  8. If the main solve stalls, retry the same auxiliary residual from additional time-based seeds.
  9. Return the best solution found.

Failure cases are explicitly detected and reported.


When solving can fail

The solver returns a best-effort result even when it cannot satisfy tolMiss. Common causes include:

  • invalid inputs (v0 <= 0, non-positive g, dt, tMax, or maxIter)
  • geometrically unreachable vacuum targets
  • targets that require an intercept beyond tMax
  • strong drag or high-arc cases where the selected arc branch cannot be maintained
  • iteration limits that are too tight for the requested tolerance

Use success, status, message, and miss together when deciding whether to accept a solution.


Status codes (SolveStatus)

BallisticOutputs.status / Python result["status"] corresponds to:

  • 0 = Ok
  • 1 = InvalidInput
  • 2 = InitialResidualFailed
  • 3 = JacobianFailed
  • 4 = LMStepSingular
  • 5 = ResidualFailedDuringSearch
  • 6 = LineSearchRejected
  • 7 = LambdaTriesExhausted
  • 8 = MaxIterReached

message contains a short diagnostic string.


Build from source

cmake -S . -B build
cmake --build build -j
ctest --test-dir build

The shared library target is ballistic_solver.

Regression and benchmark

Distributed regression and benchmark scripts are available:

python tests/random_regression.py
python benchmarks/linear_cases.py

The CI workflow runs CTest smoke coverage plus the Python regression script. The regression script includes analytic vacuum checks, constructed moving-target vacuum cases, unreachable-target checks, randomized linear cases, and constant-acceleration API smoke coverage.

Benchmark numbers depend on CPU, OS, compiler, build type, Python version, and whether native or Python entrypoints are measured. The local reference below was measured on Windows NT 10.0.26200.0, AMD Ryzen 9 6900HS, Python 3.12.8, MSVC 14.44.35207, x64 Release build, through the Python native extension.

Reference preset result from the same local Release build:

fast:     500/500, median 0.097 ms, p95 0.252 ms, p95 miss 3.425e-02 m
balanced: 500/500, median 0.191 ms, p95 0.501 ms, p95 miss 4.803e-03 m
precise:  500/500, median 0.200 ms, p95 0.582 ms, p95 miss 5.086e-06 m

Current default solver results from 10,000 generated cases with seed 20260503. The last row is the high-arc moving-target stress case:

Case set Success Median runtime P95 runtime P95 miss
Low arc, moving target 10000/10000 (100.00%) 0.434 ms 0.871 ms 5.915e-03 m
High arc, stationary target 10000/10000 (100.00%) 1.234 ms 1.939 ms 7.961e-03 m
High arc, moving target 10000/10000 (100.00%) 1.319 ms 2.503 ms 8.308e-03 m

ABI notes

  • Plain C layout across the ABI boundary
  • Fixed-size arrays only
  • No dynamic allocation across the boundary

License

MIT License

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ballistic_solver-0.7.0.tar.gz (45.1 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

ballistic_solver-0.7.0-cp312-cp312-win_amd64.whl (136.4 kB view details)

Uploaded CPython 3.12Windows x86-64

ballistic_solver-0.7.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (164.4 kB view details)

Uploaded CPython 3.12manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

ballistic_solver-0.7.0-cp312-cp312-macosx_10_13_universal2.whl (268.9 kB view details)

Uploaded CPython 3.12macOS 10.13+ universal2 (ARM64, x86-64)

ballistic_solver-0.7.0-cp311-cp311-win_amd64.whl (135.1 kB view details)

Uploaded CPython 3.11Windows x86-64

ballistic_solver-0.7.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (163.0 kB view details)

Uploaded CPython 3.11manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

ballistic_solver-0.7.0-cp311-cp311-macosx_10_9_universal2.whl (268.6 kB view details)

Uploaded CPython 3.11macOS 10.9+ universal2 (ARM64, x86-64)

ballistic_solver-0.7.0-cp310-cp310-win_amd64.whl (134.4 kB view details)

Uploaded CPython 3.10Windows x86-64

ballistic_solver-0.7.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (161.4 kB view details)

Uploaded CPython 3.10manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

ballistic_solver-0.7.0-cp310-cp310-macosx_10_9_universal2.whl (266.2 kB view details)

Uploaded CPython 3.10macOS 10.9+ universal2 (ARM64, x86-64)

File details

Details for the file ballistic_solver-0.7.0.tar.gz.

File metadata

  • Download URL: ballistic_solver-0.7.0.tar.gz
  • Upload date:
  • Size: 45.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for ballistic_solver-0.7.0.tar.gz
Algorithm Hash digest
SHA256 cd8855654bdcabd8af828414723ac78a1bdd2d34a00c550828fca4691db67548
MD5 e6921c39bd1bace660624f837390ddf7
BLAKE2b-256 81de80d9d435b913db90970fd6aafd1d7c7cc6b5f8d3abd789dc04ea6de42534

See more details on using hashes here.

Provenance

The following attestation bundles were made for ballistic_solver-0.7.0.tar.gz:

Publisher: release.yml on ujinf74/ballistic-solver

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ballistic_solver-0.7.0-cp312-cp312-win_amd64.whl.

File metadata

File hashes

Hashes for ballistic_solver-0.7.0-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 d3d45c8a3c5f2496d896a2b3ccd3fc7bdf7b5b4fa8aeca3b3e790e8d7d731f9a
MD5 a2b488deaf419fda7fa01433e3aefde0
BLAKE2b-256 8bc97dac6751accdb6cd4757ac559527e9ff512467315448bc488a7b7076e3d3

See more details on using hashes here.

Provenance

The following attestation bundles were made for ballistic_solver-0.7.0-cp312-cp312-win_amd64.whl:

Publisher: release.yml on ujinf74/ballistic-solver

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ballistic_solver-0.7.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for ballistic_solver-0.7.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 576b8709465df0763cff0c13bb22ca7e3c53cb39342dd0cbd482777e69dd5a05
MD5 9ce43094ef498ec6481ec73ee4f16e6d
BLAKE2b-256 5e7dc3552fc371322077492d3d090e9d4cc92ff9c7f8ca4e7132d5d4424e74b9

See more details on using hashes here.

Provenance

The following attestation bundles were made for ballistic_solver-0.7.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl:

Publisher: release.yml on ujinf74/ballistic-solver

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ballistic_solver-0.7.0-cp312-cp312-macosx_10_13_universal2.whl.

File metadata

File hashes

Hashes for ballistic_solver-0.7.0-cp312-cp312-macosx_10_13_universal2.whl
Algorithm Hash digest
SHA256 a3317fc44fc0b170835beb0e069e9ac8d3fe522e71d5a42a4c6b09028392a4d5
MD5 3aac3a124fb8f1d50f7af952c1551dcc
BLAKE2b-256 8ffaf2a6550fcbabb3db6f6eb4ecd81cbcccd92b486ef391c20d44f688ae37a2

See more details on using hashes here.

Provenance

The following attestation bundles were made for ballistic_solver-0.7.0-cp312-cp312-macosx_10_13_universal2.whl:

Publisher: release.yml on ujinf74/ballistic-solver

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ballistic_solver-0.7.0-cp311-cp311-win_amd64.whl.

File metadata

File hashes

Hashes for ballistic_solver-0.7.0-cp311-cp311-win_amd64.whl
Algorithm Hash digest
SHA256 043bf93a89322806541439ff1f9f00933a12f066c6ff00870fb00fbef4487036
MD5 dd893c7823edebdeaad77d42a2043d8e
BLAKE2b-256 d8b667b75062d70dedffd534686d8f83096110f6b9b9d245ae918bd35dbde0bd

See more details on using hashes here.

Provenance

The following attestation bundles were made for ballistic_solver-0.7.0-cp311-cp311-win_amd64.whl:

Publisher: release.yml on ujinf74/ballistic-solver

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ballistic_solver-0.7.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for ballistic_solver-0.7.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 0e7b86d614eb2405c89d6ad65695897c13eb06f9d8975d31424b7b9e6243a841
MD5 839acd6c6468032e1b01183921060d39
BLAKE2b-256 ec5c2febb1643b6510a3ca59f7ea09a31e3caa2f673bbc32626f2fe63810347a

See more details on using hashes here.

Provenance

The following attestation bundles were made for ballistic_solver-0.7.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl:

Publisher: release.yml on ujinf74/ballistic-solver

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ballistic_solver-0.7.0-cp311-cp311-macosx_10_9_universal2.whl.

File metadata

File hashes

Hashes for ballistic_solver-0.7.0-cp311-cp311-macosx_10_9_universal2.whl
Algorithm Hash digest
SHA256 6f44d334876722e0e8ec6159b6087817ea09b638b57a5411962ff2e50d18b56f
MD5 3c53911a37ba99f906b699b53246f34a
BLAKE2b-256 383544065b515e2bc74047ffcb4fccdf44ea2907f907396f9f7758ae65b4babc

See more details on using hashes here.

Provenance

The following attestation bundles were made for ballistic_solver-0.7.0-cp311-cp311-macosx_10_9_universal2.whl:

Publisher: release.yml on ujinf74/ballistic-solver

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ballistic_solver-0.7.0-cp310-cp310-win_amd64.whl.

File metadata

File hashes

Hashes for ballistic_solver-0.7.0-cp310-cp310-win_amd64.whl
Algorithm Hash digest
SHA256 93ba412b43f2dd200e7ea85da05fb8d1e52fa23aa6e661ca15d42a68e3d8df10
MD5 0008ec6180043754783d843340845d7f
BLAKE2b-256 135134f3e21c80ed828074d8b7ecac22a95b00aa4df54b049d21b69286b030cc

See more details on using hashes here.

Provenance

The following attestation bundles were made for ballistic_solver-0.7.0-cp310-cp310-win_amd64.whl:

Publisher: release.yml on ujinf74/ballistic-solver

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ballistic_solver-0.7.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for ballistic_solver-0.7.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 e1de4bd5c39c55bcc9e4762a23ea2e22a64165d0ea1aba381234e0807c1d6989
MD5 b455389d583fd8af6f9a7bc59f3d6d0c
BLAKE2b-256 6c18d18c0fde943e15ea925f69dff6a7517f6140af13f4f51672a16c6c226744

See more details on using hashes here.

Provenance

The following attestation bundles were made for ballistic_solver-0.7.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl:

Publisher: release.yml on ujinf74/ballistic-solver

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ballistic_solver-0.7.0-cp310-cp310-macosx_10_9_universal2.whl.

File metadata

File hashes

Hashes for ballistic_solver-0.7.0-cp310-cp310-macosx_10_9_universal2.whl
Algorithm Hash digest
SHA256 564337af7502beac982448478ac2987f66fb91a4fe23a951b39d26e9a74c0b66
MD5 8ac0e70c2923869e2e8200c3359a125f
BLAKE2b-256 fcad8b554642d0b462ede51dee7f5efa2a4d95c2a804609b406ed93f6239278a

See more details on using hashes here.

Provenance

The following attestation bundles were made for ballistic_solver-0.7.0-cp310-cp310-macosx_10_9_universal2.whl:

Publisher: release.yml on ujinf74/ballistic-solver

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page