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.

Applications. The solver only sees relative kinematics, so the same code fits many domains — robotics (interception / capture), perimeter security and counter-UAS, agricultural bird/pest deterrence, games and simulation, and defense. An optional position-only tracker (bs.TargetTracker) adds lead-fire from a noisy track. See benchmarks/intercept_eval.py and examples/viz/ for cross-domain examples and accuracy comparisons.


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)
  • Coordinate-residual Gauss–Newton core with vacuum-seeded Jacobian (default since v1.0; ~15–25% faster, auxiliary-residual method kept as solve_aux)
  • 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)

solve uses the coordinate-residual core (Gauss–Newton on the 3D closest-approach miss, vacuum-seeded Jacobian, multistart fallback). It is the same signature as before and a drop-in replacement, typically ~15–25% faster at equal robustness.

solve_aux(...)

solve_aux(relPos0, relVel, v0, kDrag, arcMode=None, params=None, relAcc=None) -> dict

The previous default: the auxiliary-residual method (re-aim through the vacuum inverse, solved by damped least squares with a multistart fallback). Same return dict as solve. Kept for compatibility and for reproducing the auxiliary-residual results.

solve_predicted(...)

solve_predicted(predictor, v0, kDrag, arcMode=None, params=None) -> dict

Lead-fire against a position-only trajectory predictor instead of explicit relVel/relAcc. predictor(t) returns the predicted relative position (x, y, z) at time t >= 0 (e.g. from a Kalman/IMM track). Internally a local constant-acceleration model is fitted at the intercept time and iterated to a fixed point — exact for constant-velocity / constant-acceleration predictors and second-order accurate at the intercept for smoothly curving tracks. Returns the same dict as solve.

TargetTracker

tracker = bs.TargetTracker(processNoise=1.0, measNoise=0.25)
for t, pos in measurements:        # (timestamp, relative position)
    tracker.update(t, pos)
lead = tracker.predict(1.5)        # predicted relative position 1.5 s ahead
result = tracker.solve(v0=95.0, kDrag=0.0015)   # lead-fire via the predictor seam

A position-only constant-acceleration Kalman tracker (per-axis, white-noise-jerk model). It denoises the track and recovers velocity/acceleration without finite-difference noise, then feeds the predictor seam — so callers never supply relVel/relAcc. See benchmarks/predictor_eval.py for a lead-prediction accuracy comparison against a constant-velocity baseline.

A real-time 3D showcase — the tracker leading a noisy, diving moving target with range rings, tracer trails, and a live hit-rate HUD — is in examples/godot/ (default scene demo/intercept_demo.tscn).

https://github.com/user-attachments/assets/7de04137-61d9-4cf0-be5e-9804d6a9c67b

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. Seed the inverse Jacobian analytically from the vacuum-arc map (no trajectory integration), as a cheap preconditioner.
  3. Simulate projectile motion using RK4 integration with drag (+ wind) and track the closest approach between projectile and target.
  4. Use the 3D closest-approach miss vector itself as the residual (it is well-conditioned), and solve the launch angles by Gauss-Newton on it.
  5. Refine the Jacobian with Broyden-style rank-1 updates; take full steps (no Levenberg–Marquardt damping, no line search).
  6. If the warm start does not reach tolerance, retry from a small arc-appropriate theta-grid multistart.
  7. Return the best solution found.

solve uses this coordinate-residual core. The earlier auxiliary-residual method (re-aim through the vacuum inverse, with damped least squares) remains available as solve_aux for compatibility and reproducibility.

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

The default coordinate-residual solve emits Ok, InvalidInput, JacobianFailed, or MaxIterReached. The Levenberg–Marquardt codes (47) are specific to the auxiliary-residual solve_aux path.

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.054 ms, p95 0.237 ms, p95 miss 2.810e-02 m
balanced: 500/500, median 0.115 ms, p95 0.492 ms, p95 miss 7.053e-03 m
precise:  500/500, median 0.168 ms, p95 0.669 ms, p95 miss 5.596e-06 m

Current default solver (coordinate-residual core) 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.168 ms 0.510 ms 6.450e-03 m
High arc, stationary target 10000/10000 (100.00%) 0.785 ms 1.604 ms 8.018e-03 m
High arc, moving target 10000/10000 (100.00%) 1.126 ms 2.350 ms 8.341e-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-1.0.0.tar.gz (65.6 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-1.0.0-cp312-cp312-win_amd64.whl (170.4 kB view details)

Uploaded CPython 3.12Windows x86-64

ballistic_solver-1.0.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (184.7 kB view details)

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

ballistic_solver-1.0.0-cp312-cp312-macosx_10_13_universal2.whl (296.8 kB view details)

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

ballistic_solver-1.0.0-cp311-cp311-win_amd64.whl (167.2 kB view details)

Uploaded CPython 3.11Windows x86-64

ballistic_solver-1.0.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (183.1 kB view details)

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

ballistic_solver-1.0.0-cp311-cp311-macosx_10_9_universal2.whl (295.9 kB view details)

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

ballistic_solver-1.0.0-cp310-cp310-win_amd64.whl (165.1 kB view details)

Uploaded CPython 3.10Windows x86-64

ballistic_solver-1.0.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (181.4 kB view details)

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

ballistic_solver-1.0.0-cp310-cp310-macosx_10_9_universal2.whl (293.5 kB view details)

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

File details

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

File metadata

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

File hashes

Hashes for ballistic_solver-1.0.0.tar.gz
Algorithm Hash digest
SHA256 73a08bfbff30f7c61997d2675c91760502511fdc0fc6b3c37f6c1b24c1ba5efe
MD5 75b0bec3b476e0980162895f476379d9
BLAKE2b-256 87b9e7f45c02b8d1fdb16a7aa90f06484ab9f4893d248763acee6dc4a15186bc

See more details on using hashes here.

Provenance

The following attestation bundles were made for ballistic_solver-1.0.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-1.0.0-cp312-cp312-win_amd64.whl.

File metadata

File hashes

Hashes for ballistic_solver-1.0.0-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 8b215dac0c8dd8b849823c5402250c8e5f42dfafad2f9e8a8b38c31525050369
MD5 11428e765d960b12fc1bc3105deb7d61
BLAKE2b-256 ef9a95aa47a0729db246fc5f651e8a66e9d39897fdb29741f1c0802a64f52566

See more details on using hashes here.

Provenance

The following attestation bundles were made for ballistic_solver-1.0.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-1.0.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for ballistic_solver-1.0.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 3cc11f20be6255512a181d56ea5b67ab3a72b524f7ef43466c1e221aee6ac355
MD5 1336b37e107be92dbcb6fdb41798c89d
BLAKE2b-256 6de9e5fc8695d3062333e71fb07cb28e0ef5352cdc3a882d27466f6b9fd4b041

See more details on using hashes here.

Provenance

The following attestation bundles were made for ballistic_solver-1.0.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-1.0.0-cp312-cp312-macosx_10_13_universal2.whl.

File metadata

File hashes

Hashes for ballistic_solver-1.0.0-cp312-cp312-macosx_10_13_universal2.whl
Algorithm Hash digest
SHA256 5fe19d5ed8691d79120660ec2bdf3218b29a013ce3105e02c8ea1050e86581a8
MD5 c231eff1bc44356e0ae568b51e0ecdb1
BLAKE2b-256 afa9f5bc16cd9e24bacaa71cf19b4b27d0d399d53da5c895c978730063477edc

See more details on using hashes here.

Provenance

The following attestation bundles were made for ballistic_solver-1.0.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-1.0.0-cp311-cp311-win_amd64.whl.

File metadata

File hashes

Hashes for ballistic_solver-1.0.0-cp311-cp311-win_amd64.whl
Algorithm Hash digest
SHA256 ed98a54d77549905baf8be7f13227e777ba617c94a4689d27fd2a47eb2d9aca6
MD5 054444726206c637330913e8c7a2b2f8
BLAKE2b-256 ae4a7dc06cdf31ba733b0762d866b16dc1b243d75317154664535e9082b242d5

See more details on using hashes here.

Provenance

The following attestation bundles were made for ballistic_solver-1.0.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-1.0.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for ballistic_solver-1.0.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 e25c40bd4bc9779cb93b53a5f79fee319b34095cfbe7dc3e105632ff44f38c36
MD5 a823df1207f3bf4f33a925d5443d0548
BLAKE2b-256 2ec90b84e93b8b54182dd4490e94b85c73465fdcfb91850e6d56c0780721f51f

See more details on using hashes here.

Provenance

The following attestation bundles were made for ballistic_solver-1.0.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-1.0.0-cp311-cp311-macosx_10_9_universal2.whl.

File metadata

File hashes

Hashes for ballistic_solver-1.0.0-cp311-cp311-macosx_10_9_universal2.whl
Algorithm Hash digest
SHA256 133d2963719b72a5c4a4ad2810087222a739885b821e0ad72b1294c1e1934d54
MD5 86de01358d176f8c08e873ad5e357c01
BLAKE2b-256 18012cc832b7ba11bd05fd9e3522d7f61664d84db8ddf7f20852430578b28753

See more details on using hashes here.

Provenance

The following attestation bundles were made for ballistic_solver-1.0.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-1.0.0-cp310-cp310-win_amd64.whl.

File metadata

File hashes

Hashes for ballistic_solver-1.0.0-cp310-cp310-win_amd64.whl
Algorithm Hash digest
SHA256 cdb3bec6e92d59f8d29fd8a512e37df7f83652038831aefaba6d672a37758ab0
MD5 e1ae488a296353e195472aa7608c1f2c
BLAKE2b-256 fb048ae659f5bf962e1f1ef1bb4427ab20392492d6cd9c82f64148436c67154a

See more details on using hashes here.

Provenance

The following attestation bundles were made for ballistic_solver-1.0.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-1.0.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for ballistic_solver-1.0.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 96b1d5c547ac3928e0cd48b536217918c815d60da97d0e4ce675c1d59cf53408
MD5 d77fcba3b13f1027bf534cc5b1b9a554
BLAKE2b-256 76d3a2bf415a0e29df6f7f8d773b10156f083a35aa22abf58d1e4ebf6fefcdb0

See more details on using hashes here.

Provenance

The following attestation bundles were made for ballistic_solver-1.0.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-1.0.0-cp310-cp310-macosx_10_9_universal2.whl.

File metadata

File hashes

Hashes for ballistic_solver-1.0.0-cp310-cp310-macosx_10_9_universal2.whl
Algorithm Hash digest
SHA256 662464f568eeae47263f3b57c7fc3dcca269e73e001bc47b276899761ce11fde
MD5 302d1bdd9e42b4cca8d826f824293a33
BLAKE2b-256 130b0a48d02b49e9dfc8c3f670a7f461a4dd7731b9c929cf9c09cd952219824e

See more details on using hashes here.

Provenance

The following attestation bundles were made for ballistic_solver-1.0.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