# -*- coding: utf-8 -*-
"""
This module contains useful or interesting functions that are related to ballistics but don't fit well anywhere else.
"""
# CONSTANTS
PROPELLANT_GAS_VELOCITY_CONSTANTS = {
'HighPoweredRifle': 1.75,
'AverageLengthShotgun': 1.50,
'LongBarrelShotgun': 1.25,
'PistolAndRevolver': 1.50
}
GENERIC_PROPELLANT_GAS_VELOCITY = 5000.0
# HELPER FUNCTIONS
[docs]def propellant_gas_velocity_multiplier(firearm_code):
"""Provides the Propellant Gas Velocity multiplier for a given firearm_code.
Parameters
----------
firearm_code : {'HPR', 'ALS', 'LBS', 'PAR'}
* HPR = High Powered Rifle
* ALS = Average Length Shotgun
* LBS = Long Barrel Shotgun
* PAR = Pistol And Revolver
Returns
-------
float
Propellant Gas Velocity multiplier for a given firearm type
Notes
-----
Refer to http://www.saami.org/PubResources/GunRecoilFormulae.pdf
The PROPELLANT_GAS_VELOCITY_CONSTANTS were sourced from the SAAMI GunRecoilFormulae document and were originally
derived from "extensive experiments by the British, published in "British Text Book of Small Arms" published in 1929
and confirmed by later work in [the United States]".
"""
pgvcs = {
'HPR': PROPELLANT_GAS_VELOCITY_CONSTANTS['HighPoweredRifle'],
'ALS': PROPELLANT_GAS_VELOCITY_CONSTANTS['AverageLengthShotgun'],
'LBS': PROPELLANT_GAS_VELOCITY_CONSTANTS['LongBarrelShotgun'],
'PAR': PROPELLANT_GAS_VELOCITY_CONSTANTS['PistolAndRevolver']
}
return pgvcs[firearm_code.upper()]
[docs]def propellant_gas_energy(charge_weight_in_grains, muzzle_velocity_in_fps, firearm_code=None):
"""Calculates the propellant gas energy for a given load and (optionally) firearm type.
Parameters
----------
charge_weight_in_grains : float
Weight in grains of the powder charge.
firearm_code : str
Argument to use for the :func:`~ballistics.extras.propellant_gas_velocity_multiplier` function.
muzzle_velocity_in_fps : int
Muzzle velocity for this load (field measurement or from loading manual reference table).
Returns
-------
float
Propellant gas energy generated for a given load/firearm combination
"""
if firearm_code:
result = charge_weight_in_grains * propellant_gas_velocity_multiplier(firearm_code) * muzzle_velocity_in_fps
else:
result = charge_weight_in_grains * GENERIC_PROPELLANT_GAS_VELOCITY
return result
# Primary Functions
[docs]def approximate_free_recoil_energy(firearm_weight_in_lbs, ejecta_weight_in_grains, charge_weight_in_grains,
muzzle_velocity_in_fps, firearm_code=None, decimal_places=0):
"""Calculates the approximate free recoil energy for a given load.
Parameters
----------
firearm_weight_in_lbs : float
Weight in lbs of the firearm plus any accessories.
ejecta_weight_in_grains : float
Weight in grains of the projectile plus wad (if fired from a shotgun).
muzzle_velocity_in_fps : int
Muzzle velocity for this load (field measurement or from loading manual reference table).
charge_weight_in_grains : float
Weight in grains of the powder charge.
firearm_code : str, optional
Argument passed to :func:`~ballistics.extras.propellant_gas_velocity_multiplier` function, default is ``None``
decimal_places : int, optional
How many decimal places to return, (default value is 0 which returns the floor of the result).
Returns
-------
int or float
Energy in foot-pounds of recoil (ft.lbf) generated by the input parameters. By default ``decimal_places`` is 0
which will return the nearest integer value. If a value greater than 0 is passed in, the function will return a
float value rounded to ``decimal_places``.
Notes
-----
Refer to http://www.saami.org/PubResources/GunRecoilFormulae.pdf
The accuracy of the value returned from this function is directly related to the accuracy of the charge
weight and projectile velocity. If a ``firearm_code`` is not passed in, a generic value is used and will result
in decreased accuracy of the returned result.
"""
mass = firearm_weight_in_lbs / 64.34
firearm_weight_in_grains = firearm_weight_in_lbs * 7000.0
ejecta_energy = float(ejecta_weight_in_grains) * muzzle_velocity_in_fps
propellant_energy = propellant_gas_energy(charge_weight_in_grains, muzzle_velocity_in_fps, firearm_code)
velocity = pow((ejecta_energy + propellant_energy) / firearm_weight_in_grains, 2)
return round(mass * velocity, decimal_places)
[docs]def approximate_recoil_velocity(firearm_weight_in_lbs, ejecta_weight_in_grains, muzzle_velocity_in_fps,
charge_weight_in_grains, firearm_code=None, decimal_places=0):
"""Calculates the approximate recoil velocity for a given load.
Parameters
----------
firearm_weight_in_lbs : float
Weight in lbs of the firearm plus any accessories.
ejecta_weight_in_grains : float
Weight in grains of the projectile plus wad (if fired from a shotgun).
muzzle_velocity_in_fps : int
Muzzle velocity for this load (field measurement or from loading manual reference table).
charge_weight_in_grains : float
Weight in grains of the powder charge.
firearm_code : str, optional
Argument passed to :func:`~ballistics.extras.propellant_gas_velocity_multiplier` function, default is ``None``
decimal_places : int, optional
How many decimal places to return, (default value is 0 which returns the floor of the result).
Returns
-------
int or float
Velocity in feet per second (fps) generated by the input parameters. By default ``decimal_places`` is 0 which
will return the nearest integer value. If a value greater than 0 is passed in, the function will return a float
value rounded to ``decimal_places``.
Notes
-----
The accuracy of the value returned from this function is directly related to the accuracy of the charge
weight and projectile velocity. If a ``firearm_code`` is not passed in, a generic value is used and will result
in decreased accuracy of the returned result.
"""
ejecta_energy = float(ejecta_weight_in_grains) * muzzle_velocity_in_fps
firearm_weight_in_grains = firearm_weight_in_lbs * 7000.0
propellant_energy = propellant_gas_energy(charge_weight_in_grains, muzzle_velocity_in_fps, firearm_code)
velocity = (ejecta_energy + propellant_energy) / firearm_weight_in_grains
return round(velocity, decimal_places)
[docs]def approximate_recoil_impulse(ejecta_weight_in_grains, muzzle_velocity_in_fps, charge_weight_in_grains,
firearm_code=None, decimal_places=0):
"""Calculates the approximate recoil impulse for a given load.
Parameters
----------
ejecta_weight_in_grains : float
Weight in grains of the projectile plus wad (if fired from a shotgun).
muzzle_velocity_in_fps : int
Muzzle velocity for this load (field measurement or from loading manual reference table).
charge_weight_in_grains : float
Weight in grains of the powder charge.
firearm_code : str, optional
Argument passed to :func:`~ballistics.extras.propellant_gas_velocity_multiplier` function, default is ``None``
decimal_places : int, optional
How many decimal places to return, (default value is 0 which returns the floor of the result).
Returns
-------
int or float
Impulse in pounds per second (lbs.sec) generated by the input parameters. By default ``decimal_places`` is 0
which will return the nearest integer value. If a value greater than 0 is passed in, the function will return a
float value rounded to ``decimal_places``.
Notes
-----
The accuracy of the value returned from this function is directly related to the accuracy of the charge
weight and projectile velocity. If a ``firearm_code`` is not passed in, a generic value is used and will result
in decreased accuracy of the returned result.
"""
ejecta_weight_in_kgs = ejecta_weight_in_grains * 0.0000648 # 1gr == 0.0000648kg
charge_weight_in_kgs = charge_weight_in_grains * 0.0000648 # 1gr == 0.0000648kg
muzzle_velocity_in_meters_per_second = muzzle_velocity_in_fps * 0.3048037 # 1ft == 0.30480370641307 meter
ejecta_energy = ejecta_weight_in_kgs * muzzle_velocity_in_meters_per_second
if firearm_code:
gas_velocity = propellant_gas_velocity_multiplier(firearm_code) * muzzle_velocity_in_fps
charge_energy = charge_weight_in_kgs * gas_velocity * 0.3048037
else:
charge_energy = charge_weight_in_kgs * GENERIC_PROPELLANT_GAS_VELOCITY * 0.3048037
return round((ejecta_energy + charge_energy) * 0.224809, decimal_places) # 1 newton == 0.224809 pound force