The project is in a healthy, maintained state
Ruby bindings for ballistics-engine - A high-performance Rust-based ballistics calculations library with 4-DOF trajectory modeling, wind deflection, atmospheric effects, and more.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Development

~> 13.0

Runtime

~> 0.9
 Project Readme

Ballistics Engine - Ruby Bindings

High-performance ballistics calculations library for Ruby, powered by Rust.

Features

  • 4-DOF Trajectory Modeling - Complete trajectory calculations with realistic physics
  • Multiple Drag Models - G1, G7, and G8 ballistic coefficients
  • Wind Deflection - Accurate wind drift calculations
  • Atmospheric Effects - Temperature, pressure, humidity, and altitude compensation
  • Unit Conversion - Automatic handling of imperial/metric conversions
  • High Performance - Rust-based calculations for maximum speed

Installation

Add this line to your application's Gemfile:

gem 'ballistics-engine'

And then execute:

$ bundle install

Or install it yourself as:

$ gem install ballistics-engine

Quick Start

The API is hash-in / hash-out — pass a hash of imperial-unit parameters, get a hash back.

require 'ballistics_engine'

# 168gr .308 with a 10 mph crosswind
result = BallisticsEngine.solve(
  "bc"                     => 0.223,   # G7 BC
  "bullet_weight_grains"   => 168.0,
  "muzzle_velocity_fps"    => 2650.0,
  "bullet_diameter_inches" => 0.308,
  "bullet_length_inches"   => 1.2,
  "sight_height_inches"    => 1.5,
  "zero_distance_yards"    => 100.0,
  "drag_model"             => "G7",    # G1 | G7 | G8
  "wind"       => { "speed_mph" => 10.0, "direction_degrees" => 90.0 },
  "atmosphere" => { "temperature_f" => 59.0, "pressure_inhg" => 29.92,
                    "humidity_percent" => 50.0, "altitude_feet" => 0.0 }
)

puts "Max range:       #{result['max_range_yards'].round(1)} yd"
puts "Time of flight:  #{result['time_of_flight'].round(3)} s"
puts "Impact velocity: #{result['impact_velocity_fps'].round(1)} fps"
result["points"].each do |p|
  puts "  range #{p['x'].round(1)}yd  drop #{p['y'].round(2)}yd  vel #{p['velocity_fps'].round(1)}fps"
end

Velocity-dependent BC (bc_segments)

Supply your own velocity:BC ladder (velocities in fps). The BC applies while the bullet's current speed is in [velocity_min_fps, velocity_max_fps):

BallisticsEngine.solve(inputs.merge(
  "use_bc_segments"  => true,
  "bc_segments_data" => [
    { "velocity_min_fps" => 1800.0, "velocity_max_fps" => 4000.0, "bc" => 0.243 },
    { "velocity_min_fps" => 1500.0, "velocity_max_fps" => 1800.0, "bc" => 0.228 },
    { "velocity_min_fps" => 1200.0, "velocity_max_fps" => 1500.0, "bc" => 0.205 }
  ]
))
# Also accepts [vmin_fps, vmax_fps, bc] triples.

Segmented wind (wind_segments)

Distance-keyed wind (independent of the velocity-keyed BC above) — e.g. downrange sensors:

BallisticsEngine.solve(inputs.merge(
  # [speed_mph, angle_degrees, until_yards]  (angle 90 = from the right)
  "wind_segments" => [[5.0, 90.0, 150.0], [7.0, 90.0, 300.0], [9.0, 90.0, 1000.0]]
))

Zero angle and Monte Carlo

z = BallisticsEngine.calculate_zero_angle(inputs.merge("target_distance_yards" => 1000.0))
puts "zero: #{z['zero_angle_moa'].round(2)} MOA"

mc = BallisticsEngine.monte_carlo(inputs.merge(
  "num_simulations"      => 1000,
  "velocity_std_dev_fps" => 10.0,
  "bc_std_dev"           => 0.01,
  "target_distance_yards"=> 1000.0
))
puts "hit probability: #{(mc['hit_probability'] * 100).round(1)}%"

API Reference

All functions live on the BallisticsEngine module and take a single hash with string keys in imperial units. Unknown keys are ignored; only the seven marked required are needed.

BallisticsEngine.solve(hash) => hash

Required: bc, bullet_weight_grains, muzzle_velocity_fps, bullet_diameter_inches, bullet_length_inches, sight_height_inches, zero_distance_yards.

Optional (selected):

  • Aim/geometry: drag_model("G7"), shooting_angle_degrees, muzzle_angle_degrees, twist_rate_inches(10), is_right_twist(true), muzzle_height_inches, target_height_inches.
  • Wind: wind = {speed_mph, direction_degrees}; wind_segments = [[speed_mph, angle_deg, until_yards], ...].
  • Atmosphere: atmosphere = {temperature_f, pressure_inhg, humidity_percent, altitude_feet}.
  • BC: use_bc_segments, bc_segments_data (see above), bc_segments = [[mach, bc], ...].
  • Coriolis: enable_coriolis, latitude_degrees, shot_direction_degrees.
  • Physics flags: enable_advanced_effects, enable_magnus, enable_aerodynamic_jump, use_enhanced_spin_drift, use_form_factor, use_cluster_bc, enable_pitch_damping, enable_precession_nutation, enable_wind_shear (+ wind_shear_model), use_rk4, use_adaptive_rk45.
  • Powder: use_powder_sensitivity, powder_temp_sensitivity, powder_temp_f, powder_temp_curve = [[temp_f, velocity_fps], ...], powder_curve_temp_f.
  • Sampling/solver: enable_trajectory_sampling, sample_interval_yards, max_range_yards, time_step_seconds.

Returns: max_range_yards, max_height_yards, time_of_flight, impact_velocity_fps, impact_energy_ftlbs, points (array of {time, x, y, z, velocity_fps, energy_ftlbs} — x=range, y=drop, z=windage, in yards). Present when enabled: sampled_points, aerodynamic_jump, angular_state, max_yaw_angle_rad, max_precession_angle_rad, min_pitch_damping, transonic_mach.

BallisticsEngine.calculate_zero_angle(hash) => hash

Same bullet keys as solve, plus required target_distance_yards (and optional target_height_inches, wind, atmosphere). Returns zero_angle_radians, zero_angle_degrees, zero_angle_moa. Raises on non-convergence.

BallisticsEngine.monte_carlo(hash) => hash

Bullet keys (optionally nested under base_inputs) plus std-dev params: num_simulations(1000), velocity_std_dev_fps, angle_std_dev_radians, bc_std_dev, wind_speed_std_dev_mph, azimuth_std_dev_radians, target_distance_yards, base_wind_speed_mph, base_wind_direction_degrees, hit_radius_inches(≈11.8). Returns ranges_yards, impact_velocities_fps, impact_positions ([{vertical_inches, lateral_inches}, ...]), hit_probability, num_simulations.

Development

After checking out the repo, run bundle install to install dependencies.

To build the native extension:

bundle exec rake compile

To run tests:

bundle exec rake test

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/ajokela/ballistics-engine.

License

This project is licensed under either of:

at your option.