Skip to content

ShotProps

ShotProps dataclass

ShotProps(
    shot: Shot,
    bc: float,
    drag_curve: PchipPrepared,
    look_angle_rad: float,
    twist_inch: float,
    length_inch: float,
    diameter_inch: float,
    weight_grains: float,
    barrel_elevation_rad: float,
    barrel_azimuth_rad: float,
    sight_height_ft: float,
    cant_cosine: float,
    cant_sine: float,
    alt0_ft: float,
    muzzle_velocity_fps: float,
    coriolis: Optional[Coriolis] = None,
)

Shot configuration and parameters for ballistic trajectory calculations.

Contains all shot-specific parameters converted to standard internal units (feet, seconds, grains, radians) used by the calculation engines. The class pre-computes expensive calculations (drag curve interpolation, atmospheric data, projectile properties) for repeated use during trajectory integration.

Examples:

from pyballistic import Shot, ShotProps

# Create shot configuration
shot = Shot(weapon=weapon, ammo=ammo, atmo=atmo)

# Convert to ShotProps
shot_props = ShotProps.from_shot(shot)

# Access pre-computed values
print(f"Stability coefficient: {shot_props.stability_coefficient}")

# Get drag coefficient at specific Mach number
drag = shot_props.drag_by_mach(1.5)

# Calculate spin drift at flight time
time = 1.2  # seconds
drift = shot_props.spin_drift(time)  # inches

# Get atmospheric conditions at altitude
altitude = shot_props.alt0_ft + 100  # 100 feet above initial altitude
density_ratio, mach_fps = shot_props.get_density_and_mach_for_altitude(altitude)
Computational Optimizations
  • Drag coefficient curves pre-computed for fast interpolation
  • Trigonometric values (cant_cosine, cant_sine) pre-calculated
  • Atmospheric parameters cached for repeated altitude lookups
  • Miller stability coefficient computed once during initialization
Notes

This class is designed for internal use by ballistic calculation engines. User code should typically work with Shot objects and let the Calculator handle the conversion to ShotProps automatically.

The original Shot object is retained for reference, but modifications to it after ShotProps creation will not affect the stored calculations. Create a new ShotProps instance if Shot parameters change.

Methods:

Name Description
from_shot

Initialize a ShotProps instance from a Shot instance.

get_density_and_mach_for_altitude

Get the air density and Mach number for a given altitude.

drag_by_mach

Calculate a standard drag factor (SDF) for the given Mach number.

spin_drift

Litz spin-drift approximation.

Functions

from_shot classmethod
from_shot(shot: Shot) -> ShotProps

Initialize a ShotProps instance from a Shot instance.

Source code in pyballistic/shot.py
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
@classmethod
def from_shot(cls, shot: Shot) -> ShotProps:
    """Initialize a ShotProps instance from a Shot instance."""
    muzzle_velocity_fps = shot.ammo.get_velocity_for_temp(shot.atmo.powder_temp) >> Velocity.FPS
    return cls(
        shot=shot,
        bc=shot.ammo.dm.BC,
        drag_curve=cls._precalc_drag_curve(shot.ammo.dm.drag_table),
        look_angle_rad=shot.look_angle >> Angular.Radian,
        twist_inch=shot.weapon.twist >> Distance.Inch,
        length_inch=shot.ammo.dm.length >> Distance.Inch,
        diameter_inch=shot.ammo.dm.diameter >> Distance.Inch,
        weight_grains=shot.ammo.dm.weight >> Weight.Grain,
        barrel_elevation_rad=shot.barrel_elevation >> Angular.Radian,
        barrel_azimuth_rad=shot.barrel_azimuth >> Angular.Radian,
        sight_height_ft=shot.weapon.sight_height >> Distance.Foot,
        cant_cosine=math.cos(shot.cant_angle >> Angular.Radian),
        cant_sine=math.sin(shot.cant_angle >> Angular.Radian),
        alt0_ft=shot.atmo.altitude >> Distance.Foot,
        muzzle_velocity_fps=muzzle_velocity_fps,
        coriolis=Coriolis.create(shot.latitude, shot.azimuth, muzzle_velocity_fps),
    )
get_density_and_mach_for_altitude
get_density_and_mach_for_altitude(
    drop: float,
) -> Tuple[float, float]

Get the air density and Mach number for a given altitude.

Parameters:

Name Type Description Default
drop float

The change in feet from the initial altitude.

required

Returns:

Type Description
Tuple[float, float]

A tuple containing the air density (in lb/ft³) and Mach number at the specified altitude.

Source code in pyballistic/shot.py
319
320
321
322
323
324
325
326
327
328
def get_density_and_mach_for_altitude(self, drop: float) -> Tuple[float, float]:
    """Get the air density and Mach number for a given altitude.

    Args:
        drop: The change in feet from the initial altitude.

    Returns:
        A tuple containing the air density (in lb/ft³) and Mach number at the specified altitude.
    """
    return self.shot.atmo.get_density_and_mach_for_altitude(self.alt0_ft + drop)
drag_by_mach
drag_by_mach(mach: float) -> float

Calculate a standard drag factor (SDF) for the given Mach number.

Formula:
    Drag force = V^2 * AirDensity * C_d * S / 2m
               = V^2 * density_ratio * SDF
Where:
    - density_ratio = LocalAirDensity / StandardDensity = rho / rho_0
    - StandardDensity of Air = rho_0 = 0.076474 lb/ft^3
    - S is cross-section = d^2 pi/4, where d is bullet diameter in inches
    - m is bullet mass in pounds
    - bc contains m/d^2 in units lb/in^2, which is multiplied by 144 to convert to lb/ft^2
Thus:
    - The magic constant found here = StandardDensity * pi / (4 * 2 * 144)

Parameters:

Name Type Description Default
mach float

The Mach number.

required

Returns:

Type Description
float

The standard drag factor at the given Mach number.

Source code in pyballistic/shot.py
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
def drag_by_mach(self, mach: float) -> float:
    """Calculate a standard drag factor (SDF) for the given Mach number.
    ```
    Formula:
        Drag force = V^2 * AirDensity * C_d * S / 2m
                   = V^2 * density_ratio * SDF
    Where:
        - density_ratio = LocalAirDensity / StandardDensity = rho / rho_0
        - StandardDensity of Air = rho_0 = 0.076474 lb/ft^3
        - S is cross-section = d^2 pi/4, where d is bullet diameter in inches
        - m is bullet mass in pounds
        - bc contains m/d^2 in units lb/in^2, which is multiplied by 144 to convert to lb/ft^2
    Thus:
        - The magic constant found here = StandardDensity * pi / (4 * 2 * 144)
    ```

    Args:
        mach: The Mach number.

    Returns:
        The standard drag factor at the given Mach number.
    """
    cd = pchip_eval(self.drag_curve, mach)
    return cd * 2.08551e-04 / self.bc
spin_drift
spin_drift(time: float) -> float

Litz spin-drift approximation.

Parameters:

Name Type Description Default
time float

Time of flight

required

Returns:

Name Type Description
float float

Windage due to spin drift, in inches

Source code in pyballistic/shot.py
355
356
357
358
359
360
361
362
363
364
365
366
367
368
def spin_drift(self, time: float) -> float:
    """Litz spin-drift approximation.

    Args:
        time: Time of flight

    Returns:
        float: Windage due to spin drift, in inches
    """
    if (self.stability_coefficient != 0) and (self.twist_inch != 0):
        sign = 1 if self.twist_inch > 0 else -1
        return sign * (1.25 * (self.stability_coefficient + 1.2)
                       * math.pow(time, 1.83)) / 12
    return 0