ballistics-engine
High-performance ballistics trajectory engine with professional physics modeling.
Features
- Professional-grade trajectory calculations with multiple drag models (G1, G7, G8)
- Advanced physics including wind effects and atmospheric modeling
- Fast Rust implementation with Python bindings via PyO3
- Imperial units API (grains, fps, yards, inches) with automatic metric conversion
Installation
pip install ballistics-engine
Quick Start
from ballistics_engine import BallisticInputs, TrajectorySolver, WindConditions, AtmosphericConditions, DragModel
# Create ballistic inputs (all imperial units)
inputs = BallisticInputs(
bc=0.505, # G7 BC
bullet_weight_grains=168, # grains
muzzle_velocity_fps=2650, # feet per second
bullet_diameter_inches=0.308, # inches
bullet_length_inches=1.24, # inches
sight_height_inches=1.5, # inches above bore
zero_distance_yards=100, # yards
twist_rate_inches=11.25, # inches per turn
)
inputs.drag_model = DragModel.g7() # Use G7 drag model
# Create wind conditions (optional)
wind = WindConditions(
speed_mph=10, # mph
direction_degrees=90, # degrees (0=headwind, 90=from right)
)
# Create atmospheric conditions (optional). Pass the readings you have and leave
# the rest out: a value you pass flies exactly as given.
atmosphere = AtmosphericConditions(
temperature_f=72, # Fahrenheit, a reading
pressure_inhg=29.31, # inHg, absolute station pressure (a reading)
humidity_percent=50, # percent
altitude_feet=600, # feet
)
# Solve trajectory
solver = TrajectorySolver(inputs, wind=wind, atmosphere=atmosphere)
result = solver.solve()
# Print results
print(f"Max range: {result.max_range_yards:.1f} yards")
print(f"Time of flight: {result.time_of_flight:.2f} seconds")
print(f"Impact velocity: {result.impact_velocity_fps:.1f} fps")
print(f"Impact energy: {result.impact_energy_ftlbs:.1f} ft-lbs")
# Iterate through trajectory points
for point in result.points:
print(f"Time: {point.time:.2f}s, X: {point.x:.1f}yd, Y: {point.y:.3f}yd, V: {point.velocity_fps:.1f}fps")
Units
The Python API uses imperial units for convenience:
- Mass: grains (gr)
- Velocity: feet per second (fps)
- Distance: yards (yd) and inches (in)
- Pressure: inches of mercury (inHg)
- Temperature: Fahrenheit (°F)
- Wind speed: miles per hour (mph)
All conversions to metric (used internally by the Rust engine) are handled automatically.
API Reference
BallisticInputs
Main input parameters for trajectory calculation.
Parameters:
bc(float): Ballistic coefficientbullet_weight_grains(float): Bullet mass in grainsmuzzle_velocity_fps(float): Muzzle velocity in fpsbullet_diameter_inches(float): Bullet diameter in inchesbullet_length_inches(float): Bullet length in inchessight_height_inches(float): Sight height above bore in incheszero_distance_yards(float): Zero distance in yardsshooting_angle_degrees(float): Uphill/downhill angle in degreestwist_rate_inches(float): Barrel twist rate (inches per turn)is_right_twist(bool): True for right-hand twist (default: True)
WindConditions
Wind parameters.
Parameters:
speed_mph(float): Wind speed in mph (default: 0)direction_degrees(float): Wind direction in degrees (0=headwind, 90=from right, default: 0)
AtmosphericConditions
Atmospheric parameters at the shooter.
Parameters:
temperature_f(float or None): Temperature in Fahrenheit. Omit it, or passNone, when you have no reading.pressure_inhg(float or None): Absolute station pressure in inHg, not a sea-level-corrected altimeter setting. Omit it, or passNone, when you have no reading.humidity_percent(float): Relative humidity percentage (default: 50)altitude_feet(float): Altitude in feet (default: 0)
Readings and omitted values. A temperature or pressure you pass is a reading and
flies exactly as given, whatever its value and whatever the altitude. A real 59 °F or
29.92 inHg at 5000 ft flies as 59 °F or 29.92 inHg. A value you leave out reads back as
59 / 29.92 but is not a reading: above 1 m of altitude it flies the ICAO standard air
at altitude_feet (about 41 °F and 24.9 inHg at 5000 ft).
Before engine 0.49.0 the binding could not tell the two apart. At any altitude above 1 m, a temperature within 0.1 °C of 15 °C (58.82 to 59.18 °F) or a pressure within 0.5 hPa of 1013.25 hPa (about 29.906 to 29.936 inHg) was treated as omitted and replaced with standard air, even when it was a real reading.
temperature_supplied and pressure_supplied (bool) record which values are readings.
The constructor sets each one when you pass its value, and assigning temperature_f or
pressure_inhg sets it too. You can also set them directly. For example, a caller that
always fills in a default can pass it and then clear the flag:
atmo = AtmosphericConditions(temperature_f=59.0, pressure_inhg=form_pressure, altitude_feet=5000)
atmo.temperature_supplied = False # 59 was our placeholder, not a reading
Inputs dicts (auto_zero_inputs, run_monte_carlo, monte_carlo_parallel)
When these functions take the atmosphere from the inputs dict, temperature (°C) and
pressure (hPa, absolute) follow the same rule. A key that is present and not None is a
reading. For monte_carlo_parallel, a value sampled per run through param_names is also a
reading. A missing key keeps the legacy rule. The optional bool keys temperature_supplied
and pressure_supplied override this, for a caller that always sends every key:
{"pressure": 1013.25, "pressure_supplied": False} flies the omitted rule, not 1013.25 hPa.
A Monte Carlo sample keeps the base dict's presence.
calculate_zero_angle(inputs, yards, ..., temperature_f=None, pressure_inhg=None, ...)
follows the same rule for its keyword arguments.
TrajectorySolver
Trajectory calculation engine.
Methods:
__init__(inputs, wind=None, atmosphere=None): Create solver with inputssolve(): Calculate trajectory, returnsTrajectoryResult
TrajectoryResult
Trajectory calculation results.
Properties:
max_range_yards(float): Maximum range in yardsmax_height_yards(float): Maximum height in yardstime_of_flight(float): Total flight time in secondsimpact_velocity_fps(float): Impact velocity in fpsimpact_energy_ftlbs(float): Impact energy in ft-lbspoints(list[TrajectoryPoint]): List of trajectory points
TrajectoryPoint
Individual point along trajectory.
Properties:
time(float): Time in secondsx(float): Downrange distance in yardsy(float): Vertical position in yards (relative to line of sight)z(float): Lateral position in yardsvelocity_fps(float): Velocity in fpsenergy_ftlbs(float): Kinetic energy in ft-lbs
DragModel
Drag model selection.
Static methods:
DragModel.g1(): G1 drag model (flat base)DragModel.g7(): G7 drag model (boat tail)DragModel.g8(): G8 drag model (boat tail with meplat)
bridge_call(request_json: str) -> str
The engine's versioned JSON command bridge: one request envelope in, one response
envelope out, both as strings. It is a transport onto the engine's own service layer,
so it reaches corrections.bc5d_table_path and atmosphere.pressure_reference,
which the typed classes above cannot carry at all. It is also the safer route to
effects.wind_shear_model: the inputs dict has accepted wind_shear_model for some
time, but it takes any string and silently resolves an unrecognised one (to the power
law in TrajectorySolver, to no shear in fast_integrate), where the bridge validates
it and names the offending field.
In the inputs dict, enable_wind_shear without a wind_shear_model means the power
law ("power_law", the CLI's default model); with shear off the model defaults to
"none". A model you name, "none" included, is used exactly as given.
BallisticInputs.wind_shear_model reads back the resolved value.
import json
from ballistics_engine import bridge_call
response = json.loads(bridge_call(json.dumps({
"api_version": 1,
"command": "solve",
"request": {
"schema_version": 1,
"projectile": {"mass_kg": 0.01134, "diameter_m": 0.00782, "length_m": 0.0338,
"drag_model": "G7", "ballistic_coefficient": 0.243},
"rifle": {"muzzle_velocity_mps": 823.0, "sight_height_m": 0.0381},
"shot": {"max_range_m": 1000.0, "zero_distance_m": 100.0},
"atmosphere": {}, "wind": {"speed_mps": 4.47, "direction_from_rad": 1.5708},
"solver": {}, "effects": {"wind_shear_model": "logarithmic"},
"sampling": {"interval_m": 100.0},
},
})))
if response["ok"]:
print(response["result"]["samples"][-1])
else:
print(response["error"]["code"], response["error"]["message"])
Unlike the rest of this API, failures come back inside the returned JSON rather than
as a Python exception — a bad envelope, an unknown command or an invalid field all
return a well-formed {"ok": false, "api_version": 1, "engine_version": "...", "error": {"code": ..., "message": ...}} document. The wrapper returns the string
verbatim; parsing and error handling are the caller's.
Unlike the classes above, the bridge speaks SI units throughout (kg, m, m/s, K, Pa, radians), because it is the engine's own wire contract.
Ask meta.capabilities for the command list this wheel can actually run — it is
build-dependent, and this wheel links the engine with default features off, so the
PDF- and profile-import-gated commands are not compiled in.
License
Dual licensed under MIT or Apache-2.0.
Links
- Homepage: https://ballistics.rs/
- Repository: https://github.com/ajokela/ballistics-engine
- Documentation: https://docs.rs/ballistics-engine
Metadata
Release files for ballistics-engine 0.49.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| ballistics_engine-0.49.0.tar.gz | 71.0 kB | Without provenance | Details for ballistics_engine-0.49.0.tar.gz |
Built distributions (wheels)
| File | Actions Reset | |||
|---|---|---|---|---|
| ballistics_engine-0.49.0-cp38-abi3-win_amd64.whl | CPython 3.8 | abi3 | Windows x86-64 | Details for ballistics_engine-0.49.0-cp38-abi3-win_amd64.whl |
| ballistics_engine-0.49.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl | CPython 3.8 | abi3 | Linux glibc 2.17+ x86-64 | Details for ballistics_engine-0.49.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
| ballistics_engine-0.49.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl | CPython 3.8 | abi3 | Linux glibc 2.17+ ARM64 | Details for ballistics_engine-0.49.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl |
| ballistics_engine-0.49.0-cp38-abi3-macosx_11_0_arm64.whl | CPython 3.8 | abi3 | macOS 11.0+ ARM64 | Details for ballistics_engine-0.49.0-cp38-abi3-macosx_11_0_arm64.whl |
| ballistics_engine-0.49.0-cp38-abi3-macosx_10_12_x86_64.whl | CPython 3.8 | abi3 | macOS 10.12+ x86-64 | Details for ballistics_engine-0.49.0-cp38-abi3-macosx_10_12_x86_64.whl |
Total release size: 6.1 MB
Release files / ballistics_engine-0.49.0.tar.gz
| Download URL | ballistics_engine-0.49.0.tar.gz |
|---|---|
| Size | 71.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
dc4453b0138579f5e67c31278637d11df46fdf603d856b8c42fbce29fc4186fd
|
|
BLAKE2b-256 checksum How to use checksums |
b1309c3b6833586468d20913bceadbff2929b18b3effb662df954cc3f0461d82
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
maturin/1.15.0
|
Release files / ballistics_engine-0.49.0-cp38-abi3-win_amd64.whl
| Download URL | ballistics_engine-0.49.0-cp38-abi3-win_amd64.whl |
|---|---|
| Size | 1.2 MB |
| Tags | CPython 3.8 Windows x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
a03a674803d346e4cb89f54a708012504402ae33e08851c2d27211ca664a9cd9
|
|
BLAKE2b-256 checksum How to use checksums |
f01cde4def3da63217be7c231c8e3849ab2995c63357142f4df8ad3e21fa6e5f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
maturin/1.15.0
|
Release files / ballistics_engine-0.49.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | ballistics_engine-0.49.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 1.3 MB |
| Tags | CPython 3.8 Linux glibc 2.17+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
93f8bc4ea2875beb423eb3b1e44508c843c6d4fc57159c4b0859e9e9cfb54f2b
|
|
BLAKE2b-256 checksum How to use checksums |
25cd3be567c36b007af73992c7fc618e4e92bd950aa85c6e303c303a3b0e4ae6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
maturin/1.15.0
|
Release files / ballistics_engine-0.49.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
| Download URL | ballistics_engine-0.49.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl |
|---|---|
| Size | 1.2 MB |
| Tags | CPython 3.8 Linux glibc 2.17+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
36c0da2c07ff81c02623d06b5933e4a2daa686737d13860c2ab88bd0d98d2af6
|
|
BLAKE2b-256 checksum How to use checksums |
501d30e5feac6594d26e8cf67e9664381904d9e88f8da379441de180a4f49ad2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
maturin/1.15.0
|
Release files / ballistics_engine-0.49.0-cp38-abi3-macosx_11_0_arm64.whl
| Download URL | ballistics_engine-0.49.0-cp38-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 1.1 MB |
| Tags | CPython 3.8 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
62a30da1aaa744bbbceaed552f6dd8c58f835e03dcbef3be6028d973b54dbd07
|
|
BLAKE2b-256 checksum How to use checksums |
eeee7e625459226ab7d97b9e146b003b18e987d16911da98e60ec880530f516c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
maturin/1.15.0
|
Release files / ballistics_engine-0.49.0-cp38-abi3-macosx_10_12_x86_64.whl
| Download URL | ballistics_engine-0.49.0-cp38-abi3-macosx_10_12_x86_64.whl |
|---|---|
| Size | 1.2 MB |
| Tags | CPython 3.8 abi3 macOS 10.12+ x86-64 |
|
SHA-256 checksum How to use checksums |
535bea80dbee0f8d8c5bd119b5cdee954ec113f357a1fcdc356ed9a24dc4f793
|
|
BLAKE2b-256 checksum How to use checksums |
4e7e69adee03b54f9fe8d64da46e2418e92e7e3f3a960d322545e1a8b02587e3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
maturin/1.15.0
|
Security for ballistics-engine 0.49.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.