Skip to content

Commit

Permalink
Fixed and verified wind effect on trajectory.
Browse files Browse the repository at this point in the history
  • Loading branch information
dbookstaber committed Feb 11, 2024
1 parent 36e7f84 commit a08ebbb
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 46 deletions.
2 changes: 1 addition & 1 deletion py_ballisticcalc/conditions.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ def get_density_factor_and_mach_for_altitude(self, altitude: float):
@dataclass
class Wind(TypedUnits):
"""
Stores wind data at the desired distance.
Wind direction and velocity by down-range distance.
direction_from = 0 is blowing from behind shooter.
direction_from = 90 degrees is blowing from shooter's left towards right.
"""
Expand Down
47 changes: 16 additions & 31 deletions py_ballisticcalc/trajectory_calc.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ def __neg__(self):


class TrajectoryCalc:
"""All calculations are done in units of Feet and fps"""

def __init__(self, ammo: Ammo):
self.ammo = ammo
Expand Down Expand Up @@ -220,14 +221,14 @@ def _trajectory(self, ammo: Ammo, atmo: Atmo,
else:
if len_winds > 1:
next_wind_range = winds[0].until_distance() >> Distance.Foot
wind_vector = wind_to_vector(shot_info, winds[0])
wind_vector = wind_to_vector(winds[0])

if Settings.USE_POWDER_SENSITIVITY:
velocity = ammo.get_velocity_for_temp(atmo.temperature) >> Velocity.FPS
else:
velocity = ammo.mv >> Velocity.FPS

# x - distance towards target, y - drop and z - windage
# x: downrange distance (towards target), y: drop, z: windage
velocity_vector = Vector(math.cos(barrel_elevation) * math.cos(barrel_azimuth),
math.sin(barrel_elevation),
math.cos(barrel_elevation) * math.sin(barrel_azimuth)) * velocity
Expand All @@ -254,7 +255,7 @@ def _trajectory(self, ammo: Ammo, atmo: Atmo,

if range_vector.x >= next_wind_range:
current_wind += 1
wind_vector = wind_to_vector(shot_info, winds[current_wind])
wind_vector = wind_to_vector(winds[current_wind])

if current_wind == len_winds - 1:
next_wind_range = 1e7
Expand Down Expand Up @@ -362,36 +363,20 @@ def calculate_stability_coefficient(twist_rate: Distance, ammo: Ammo, atmo: Atmo
return sd * fv * ftp


def wind_to_vector(shot: Shot, wind: Wind):
"""
def wind_to_vector(wind: Wind) -> Vector:
"""Calculate wind vector to add to projectile velocity vector each iteration:
Aerodynamic drag is function of velocity relative to the air stream.
Wind angle of zero is blowing from behind shooter
90-degree is blowing towards shooter's right
Looks like we only start with data on wind direction in the x-z plane, not any vertical components.
Therefore, given wind velocity v with angle d:
The downrange "range" component is v*cos(d)
The orthogonal "cross" component is v*sin(d)
When the bullet has a vertical velocity component, meaning velocity angle to horizon a > 0
the sin(a) range component acts to lift it in the y axis, and only cos(a) acts in the x axis
... of course this is reduced by any amount of the velocity that points in the z axis
which we determine using the cant_angle
TODO: Right now this takes the initial (launch) elevation and cant_angle components.
This is an OK "flat-fire" approximation but we actually have the angle of travel at every instant
so we should use that instead.
TODO: Correct terms now that Shot class factors cant out of barrel elevation
Wind angle of 90-degree is blowing towards shooter's right
NB: Presently we can only define Wind in the x-z plane, not any vertical component.
"""
sight_cosine = math.cos(shot.barrel_elevation >> Angular.Radian)
sight_sine = math.sin(shot.barrel_elevation >> Angular.Radian)
cant_cosine = math.cos(shot.cant_angle >> Angular.Radian)
cant_sine = math.sin(shot.cant_angle >> Angular.Radian)
range_velocity = (wind.velocity >> Velocity.FPS) * math.cos(
wind.direction_from >> Angular.Radian)
cross_component = (wind.velocity >> Velocity.FPS) * math.sin(
wind.direction_from >> Angular.Radian)
range_factor = -range_velocity * sight_sine
return Vector(range_velocity * sight_cosine,
range_factor * cant_cosine + cross_component * cant_sine,
cross_component * cant_cosine - range_factor * cant_sine)
# Downrange (x-axis) wind velocity component:
range_component = (wind.velocity >> Velocity.FPS) * math.cos(wind.direction_from >> Angular.Radian)
# Cross (z-axis) wind velocity component:
cross_component = (wind.velocity >> Velocity.FPS) * math.sin(wind.direction_from >> Angular.Radian)
return Vector(range_component, 0, cross_component)


def create_trajectory_row(time: float, range_vector: Vector, velocity_vector: Vector,
Expand Down
19 changes: 6 additions & 13 deletions py_ballisticcalc_exts/py_ballisticcalc_exts/trajectory_calc.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ cdef class TrajectoryCalc:
def zero_angle(self, shot_info: Shot, distance: Distance):
return self._zero_angle(shot_info, distance)

def trajectory(self, shot_info: Shot, max_range: Distance, dist_step: [float, Distance],
def trajectory(self, shot_info: Shot, max_range: Distance, dist_step: Distance,
extra_data: bool = False):
cdef:
object step = Settings.Units.distance(dist_step)
Expand Down Expand Up @@ -242,7 +242,7 @@ cdef class TrajectoryCalc:
else:
if len_winds > 1:
next_wind_range = winds[0].until_distance() >> Distance.Foot
wind_vector = wind_to_vector(shot_info, winds[0])
wind_vector = wind_to_vector(winds[0])

if Settings.USE_POWDER_SENSITIVITY:
velocity = ammo.get_velocity_for_temp(atmo.temperature) >> Velocity.FPS
Expand Down Expand Up @@ -274,7 +274,7 @@ cdef class TrajectoryCalc:

if range_vector.x >= next_wind_range:
current_wind += 1
wind_vector = wind_to_vector(shot_info, winds[current_wind])
wind_vector = wind_to_vector(winds[current_wind])

if current_wind == len_winds - 1:
next_wind_range = 1e7
Expand Down Expand Up @@ -382,18 +382,11 @@ cdef double calculate_stability_coefficient(object twist_rate, object ammo, obje
double ftp = ((ft + 460) / (59 + 460)) * (29.92 / pt)
return sd * fv * ftp

cdef Vector wind_to_vector(object shot, object wind):
cdef Vector wind_to_vector(object wind):
cdef:
double sight_cosine = cos(shot.barrel_elevation >> Angular.Radian)
double sight_sine = sin(shot.barrel_elevation >> Angular.Radian)
double cant_cosine = cos(shot.cant_angle >> Angular.Radian)
double cant_sine = sin(shot.cant_angle >> Angular.Radian)
double range_velocity = (wind.velocity >> Velocity.FPS) * cos(wind.direction_from >> Angular.Radian)
double range_component = (wind.velocity >> Velocity.FPS) * cos(wind.direction_from >> Angular.Radian)
double cross_component = (wind.velocity >> Velocity.FPS) * sin(wind.direction_from >> Angular.Radian)
double range_factor = -range_velocity * sight_sine
return Vector(range_velocity * sight_cosine,
range_factor * cant_cosine + cross_component * cant_sine,
cross_component * cant_cosine - range_factor * cant_sine)
return Vector(range_component, 0., cross_component)

cdef create_trajectory_row(double time, Vector range_vector, Vector velocity_vector,
double velocity, double mach, double windage, double weight, object flag):
Expand Down
2 changes: 1 addition & 1 deletion tests/test_computer.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def test_cant_positive_elevation(self):
self.assertGreater(t.trajectory[5].windage.raw_value, t.trajectory[3].windage.raw_value)

def test_cant_zero_sight_height(self):
"""Cant_angle = 90 degrees with sight_height=0 should match baseline with:
"""Cant_angle = 90 degrees with sight_height=0 and barrel_elevation=0 should match baseline with:
drop+=baseline.sight_height, windage no change
"""
canted = Shot(weapon=Weapon(sight_height=0, twist=self.weapon.twist),
Expand Down

0 comments on commit a08ebbb

Please sign in to comment.