Skip to content

Dimensions

Unit

Bases: IntEnum

Enumeration of all supported unit types.

  • Angular: Radian, Degree, MOA, Mil, MRad, Thousandth, InchesPer100Yd, CmPer100m, OClock
  • Distance: Inch, Foot, Yard, Mile, NauticalMile, Millimeter, Centimeter, Meter, Kilometer, Line
  • Velocity: MPS (meters/second), KMH (km/hour), FPS (feet/second), MPH (miles/hour), KT (knots)
  • Weight: Grain, Ounce, Gram, Pound, Kilogram, Newton
  • Pressure: MmHg, InHg, Bar, hPa (hectopascal), PSI
  • Temperature: Fahrenheit, Celsius, Kelvin, Rankin
  • Energy: FootPound, Joule
  • Time: Second, Minute, Millisecond, Microsecond, Nanosecond, Picosecond

Each unit can be used as a callable constructor for creating unit instances:

Examples:

>>> # Create distance measurements
>>> distance = Unit.Meter(100)
>>> range_yards = Unit.Yard(109.4)
>>> # Create velocity measurements
>>> muzzle_velocity = Unit.FPS(2800)
>>> velocity_mps = Unit.MPS(853.4)
>>> # Angular measurements for ballistics
>>> elevation = Unit.MOA(2.5)
>>> windage = Unit.Mil(1.2)

Methods:

Name Description
counter

Generate a finite or infinite sequence of GenericDimension objects.

iterator

Create a sorted sequence of GenericDimension objects from raw numeric values.

parse

Parse a value with optional unit specification into a unit measurement.

Attributes:

Name Type Description
key str

Readable name of the unit of measure.

accuracy int

Default accuracy of the unit of measure.

symbol str

Short symbol of the unit of measure.

Attributes

key property
key: str

Readable name of the unit of measure.

accuracy property
accuracy: int

Default accuracy of the unit of measure.

symbol property
symbol: str

Short symbol of the unit of measure.

Functions

counter
counter(
    start: Number,
    step: Number,
    end: Optional[Number] = None,
    include_end: bool = True,
) -> Generator[GenericDimension, None, None]

Generate a finite or infinite sequence of GenericDimension objects.

This function acts as a counter for measurements, yielding GenericDimension instances at specified intervals, defined by start, step, and end. The underlying numeric values are handled as raw values of the given unit.

Parameters:

Name Type Description Default
start Number

The starting raw value for the sequence. Defaults to 0.

required
step Number

The increment/decrement step for the sequence. Must not be 0 for an infinite sequence. Defaults to 0.

required
end Optional[Number]

The raw value at which the sequence should stop (exclusive by default). If None, the sequence is infinite. Defaults to None.

None
include_end bool

If True and end is provided, the end value will be included in the sequence. Defaults to True.

True

Yields:

Type Description
GenericDimension

GenericDimension[Any]: A GenericDimension object representing the current measurement in the sequence.

Raises:

Type Description
ValueError

If step is 0 for an infinite sequence, or if step has the wrong direction for the given start and end range.

StopIteration

If the iteration limit (MAX_ITERATIONS) is reached during an infinite sequence.

Examples:

>>> list(Unit.Millisecond.counter(start=0, step=10, end=30))   
[<Time: 0.0ms (0.0)>, <Time: 10.0ms (0.01)>, <Time: 20.0ms (0.02)>, <Time: 30.0ms (0.03)>]
Source code in py_ballisticcalc/unit.py
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
def counter(self, start: Number, step: Number,
    end: Optional[Number] = None, include_end: bool = True) -> Generator[GenericDimension, None, None]:
    """Generate a finite or infinite sequence of `GenericDimension` objects.

    This function acts as a counter for measurements, yielding `GenericDimension`
    instances at specified intervals, defined by `start`, `step`, and `end`.
    The underlying numeric values are handled as raw values of the given unit.

    Args:
        start: The starting raw value for the sequence. Defaults to 0.
        step: The increment/decrement step for the sequence.
                            Must not be 0 for an infinite sequence. Defaults to 0.
        end: The raw value at which the sequence should stop (exclusive by default).
                                     If `None`, the sequence is infinite. Defaults to `None`.
        include_end: If `True` and `end` is provided, the `end` value will be
                            included in the sequence. Defaults to `True`.

    Yields:
        GenericDimension[Any]: A `GenericDimension` object representing the current measurement in the sequence.

    Raises:
        ValueError:
            If `step` is 0 for an infinite sequence, or if `step` has the wrong
                direction for the given `start` and `end` range.
        StopIteration:
            If the iteration limit (`MAX_ITERATIONS`) is reached during an infinite sequence.

    Examples:
        >>> list(Unit.Millisecond.counter(start=0, step=10, end=30))   
        [<Time: 0.0ms (0.0)>, <Time: 10.0ms (0.01)>, <Time: 20.0ms (0.02)>, <Time: 30.0ms (0.03)>]
    """
    _start: GenericDimension = self(start)
    _step: GenericDimension = self(step)
    _end: Optional[GenericDimension] = self(end) if end is not None else None

    _start_raw: Number = _start.raw_value
    _step_raw: Number = _step.raw_value
    _end_raw: Optional[Number] = _end.raw_value if _end is not None else None

    if _end_raw is not None and include_end:
        _end_raw += _step_raw
    for i, raw_value in enumerate(counter(_start_raw, _step_raw, _end_raw)):
        value: GenericDimension = self(0)
        value._value = raw_value
        yield value
        if i == MAX_ITERATIONS:
            raise ValueError("Reached generator limit %d" % MAX_ITERATIONS)
iterator
iterator(
    items: Sequence[Number],
    /,
    *,
    sort: bool = False,
    reverse: bool = False,
) -> Generator["GenericDimension[Any]", None, None]

Create a sorted sequence of GenericDimension objects from raw numeric values.

Parameters:

Name Type Description Default
items Sequence[Number]

A sequence of raw numeric values (integers or floats).

required
sort bool

If set to True, the elements will be sorted before yield.

False
reverse bool

If set to True, the elements are sorted in descending order. Defaults to False.

False

Yields:

Name Type Description
_GenericDimensionType 'GenericDimension[Any]'

A GenericDimension object of the specific type implied by u, in sorted order.

Examples:

>>> list(Unit.Foot.iterator([5, 1, 2], sort=True))  # Inferred as Iterable[Distance]
[<Distance: 1.0ft (12.0)>, <Distance: 2.0ft (24.0)>, <Distance: 5.0ft (60.0)>]
Source code in py_ballisticcalc/unit.py
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
def iterator(self, items: Sequence[Number], /, *,
             sort: bool = False,
             reverse: bool = False) -> Generator["GenericDimension[Any]", None, None]:
    """Create a sorted sequence of `GenericDimension` objects from raw numeric values.

    Args:
        items: A sequence of raw numeric values (integers or floats).
        sort: If set to `True`, the elements will be sorted before yield.
        reverse: If set to `True`, the elements are sorted in descending order. Defaults to `False`.

    Yields:
        _GenericDimensionType: A `GenericDimension` object of the specific type implied by `u`, in sorted order.

    Examples:
        >>> list(Unit.Foot.iterator([5, 1, 2], sort=True))  # Inferred as Iterable[Distance]
        [<Distance: 1.0ft (12.0)>, <Distance: 2.0ft (24.0)>, <Distance: 5.0ft (60.0)>]
    """
    iter_ = iterator(items, sort=sort, reverse=reverse)
    for v in iter_:
        yield self(v)
parse staticmethod
parse(
    input_: Union[str, Number],
    preferred: Optional[Union[Unit, str]] = None,
) -> Optional[Union[GenericDimension[Any], Any, Unit]]

Parse a value with optional unit specification into a unit measurement.

Parameters:

Name Type Description Default
input_ Union[str, Number]

Value to parse - can be a number or string with optional unit.

required
preferred Optional[Union[Unit, str]]

Preferred unit to use for numeric inputs, either as Unit enum or string alias.

None

Returns:

Type Description
Optional[Union[GenericDimension[Any], Any, Unit]]

Parsed unit measurement if successful, raises exception on failure.

Raises:

Type Description
TypeError

If input type is not supported.

UnitAliasError

If unit alias cannot be parsed.

Examples:

>>> # Parse numeric value with preferred unit
>>> Unit.parse(100, Unit.Meter)
<Distance: 100.0m (3937.0079)>
>>> # Parse string with embedded unit
>>> Unit.parse('2yd')
<Distance: 2.0yd (72.0)>
>>> # Parse with PreferredUnit string
>>> Unit.parse(50, 'grain')
<Weight: 50.0gr (50.0)>
Source code in py_ballisticcalc/unit.py
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
@staticmethod
def parse(input_: Union[str, Number],
          preferred: Optional[Union[Unit, str]] = None) -> Optional[Union[GenericDimension[Any], Any, Unit]]:
    """Parse a value with optional unit specification into a unit measurement.

    Args:
        input_: Value to parse - can be a number or string with optional unit.
        preferred: Preferred unit to use for numeric inputs, either as Unit enum or string alias.

    Returns:
        Parsed unit measurement if successful, raises exception on failure.

    Raises:
        TypeError: If input type is not supported.
        UnitAliasError: If unit alias cannot be parsed.

    Examples:
        >>> # Parse numeric value with preferred unit
        >>> Unit.parse(100, Unit.Meter)
        <Distance: 100.0m (3937.0079)>

        >>> # Parse string with embedded unit
        >>> Unit.parse('2yd')
        <Distance: 2.0yd (72.0)>

        >>> # Parse with PreferredUnit string
        >>> Unit.parse(50, 'grain')
        <Weight: 50.0gr (50.0)>
    """

    def create_as_preferred(value_):
        if isinstance(preferred, Unit):
            return preferred(float(value_))
        if isinstance(preferred, str):
            if units_ := Unit._parse_unit(preferred):
                return units_(float(value_))
        raise UnitAliasError(f"Unsupported {preferred=} unit alias")

    if isinstance(input_, (float, int)):
        return create_as_preferred(input_)

    if not isinstance(input_, str):
        raise TypeError(f"type, [str, float, int] expected for 'input_', got {type(input_)}")

    input_string = input_.replace(" ", "")
    if match := re.match(r'^-?(?:\d+\.\d*|\.\d+|\d+\.?)$', input_string):
        value = match.group()
        return create_as_preferred(value)

    if match := re.match(r'(^-?(?:\d+\.\d*|\.\d+|\d+\.?))(.*$)', input_string):
        value, alias = match.groups()
        if units := Unit._parse_unit(alias):
            return units(float(value))
        raise UnitAliasError(f"Unsupported unit {alias=}")

    raise UnitAliasError(f"Can't parse unit {input_=}")

GenericDimension

GenericDimension(value: Number, units: Unit)

Bases: Generic[_GenericDimensionType]

Abstract base class for typed unit dimensions.

This class provides the foundation for all unit measurements in the ballistic calculation system. Each dimension (Distance, Velocity, Angular, etc.) inherits from this class and defines its own conversion factors and raw unit representation.

Attributes:

Name Type Description
_value Number

Internal value stored in the dimension's raw unit.

_defined_units Unit

The unit type this instance was created with.

_conversion_factors Mapping[Unit, float]

Mapping of units to their conversion factors.

Examples:

>>> # Subclasses define their own conversion factors
>>> class Distance(GenericDimension):
...     _conversion_factors = {Unit.Meter: 39.3701, Unit.Yard: 36.0}
>>> # Create and convert measurements
>>> meters = Distance(100, Unit.Meter)
>>> yards = meters.convert(Unit.Yard)
>>> print(f"100m = {yards.unit_value:.1f} yards")
100m = 109.4 yards

Parameters:

Name Type Description Default
value Number

Numeric value of the measurement in the specified units.

required
units Unit

Unit enum specifying the unit type for the value.

required

Methods:

Name Description
new_from_raw

Create a new instance from a raw value in base units.

from_raw

Convert a raw value to the specified units.

to_raw

Convert a value in specified units to the raw unit.

convert

Convert this measurement to different units within the same dimension.

get_in

Get the numeric value of this measurement in specified units.

Source code in py_ballisticcalc/unit.py
738
739
740
741
742
743
744
745
746
def __init__(self, value: Number, units: Unit):
    """Initialize a unit measurement with value and unit type.

    Args:
        value: Numeric value of the measurement in the specified units.
        units: Unit enum specifying the unit type for the value.
    """
    self._value: Number = self.__class__.to_raw(value, units)
    self._defined_units: Unit = units

Attributes

units property
units: Unit

Get the unit type this dimension instance was defined with.

Returns:

Type Description
Unit

Unit enum representing the unit type of this measurement.

unit_value property
unit_value: Number

Get the numeric value in the defined units.

Returns:

Type Description
Number

Numeric value in the units this measurement was created with.

Note

Equivalent to get_in(self.units) but more efficient as a property.

raw_value property
raw_value: Number

Get the internal raw value used for calculations.

Returns:

Type Description
Number

Numeric value in the dimension's raw unit (e.g., inches for Distance).

Functions

new_from_raw classmethod
new_from_raw(raw_value: float, to_units: Unit) -> Self

Create a new instance from a raw value in base units.

Parameters:

Name Type Description Default
raw_value float

Value in the dimension's raw unit (e.g., inches for Distance).

required
to_units Unit

Target unit type for the new instance.

required

Returns:

Type Description
Self

New instance with the raw value converted to the specified units.

Source code in py_ballisticcalc/unit.py
820
821
822
823
824
825
826
827
828
829
830
831
832
@classmethod
def new_from_raw(cls, raw_value: float, to_units: Unit) -> Self:
    """Create a new instance from a raw value in base units.

    Args:
        raw_value: Value in the dimension's raw unit (e.g., inches for Distance).
        to_units: Target unit type for the new instance.

    Returns:
        New instance with the raw value converted to the specified units.
    """
    value_in_units = raw_value / cls._get_conversion_factor(to_units)
    return cls(value_in_units, to_units)
from_raw classmethod
from_raw(raw_value: float, unit: Unit) -> Number

Convert a raw value to the specified units.

Parameters:

Name Type Description Default
raw_value float

Value in the dimension's raw unit.

required
unit Unit

Target unit type for conversion.

required

Returns:

Type Description
Number

Numeric value converted to the specified units.

Note

Static conversion method that doesn't create a unit instance.

Source code in py_ballisticcalc/unit.py
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
@classmethod
def from_raw(cls, raw_value: float, unit: Unit) -> Number:
    """Convert a raw value to the specified units.

    Args:
        raw_value: Value in the dimension's raw unit.
        unit: Target unit type for conversion.

    Returns:
        Numeric value converted to the specified units.

    Note:
        Static conversion method that doesn't create a unit instance.
    """
    cls._validate_unit_type(unit)
    return raw_value / cls._get_conversion_factor(unit)
to_raw classmethod
to_raw(value: Number, units: Unit) -> Number

Convert a value in specified units to the raw unit.

Parameters:

Name Type Description Default
value Number

Numeric value to convert.

required
units Unit

Unit type of the input value.

required

Returns:

Type Description
Number

Value converted to the dimension's raw unit.

Note

Used internally for storing values in consistent raw units.

Source code in py_ballisticcalc/unit.py
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
@classmethod
def to_raw(cls, value: Number, units: Unit) -> Number:
    """Convert a value in specified units to the raw unit.

    Args:
        value: Numeric value to convert.
        units: Unit type of the input value.

    Returns:
        Value converted to the dimension's raw unit.

    Note:
        Used internally for storing values in consistent raw units.
    """
    cls._validate_unit_type(units)
    return value * cls._get_conversion_factor(units)
convert
convert(units: Unit) -> Self

Convert this measurement to different units within the same dimension.

Parameters:

Name Type Description Default
units Unit

Target unit type for conversion.

required

Returns:

Type Description
Self

New instance of the same dimension type with value in target units.

Raises:

Type Description
UnitConversionError

If target units are incompatible with this dimension.

Examples:

>>> distance = Distance.Meter(100)
>>> print(f"100m = {distance.convert(Distance.Yard)}")
100m = 109.4yd
Source code in py_ballisticcalc/unit.py
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
def convert(self, units: Unit) -> Self:
    """Convert this measurement to different units within the same dimension.

    Args:
        units: Target unit type for conversion.

    Returns:
        New instance of the same dimension type with value in target units.

    Raises:
        UnitConversionError: If target units are incompatible with this dimension.

    Examples:
        >>> distance = Distance.Meter(100)
        >>> print(f"100m = {distance.convert(Distance.Yard)}")
        100m = 109.4yd
    """
    return self.__class__.new_from_raw(self._value, units)
get_in
get_in(units: Unit) -> Number

Get the numeric value of this measurement in specified units.

Parameters:

Name Type Description Default
units Unit

Target unit type for the value.

required

Returns:

Type Description
Number

Numeric value in the specified units (float or int).

Raises:

Type Description
UnitConversionError

If target units are incompatible with this dimension.

Examples:

>>> distance = Distance.Meter(100)
>>> print(f"100m = {distance.get_in(Distance.Yard):.5f} yards")
100m = 109.36133 yards
Source code in py_ballisticcalc/unit.py
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
def get_in(self, units: Unit) -> Number:
    """Get the numeric value of this measurement in specified units.

    Args:
        units: Target unit type for the value.

    Returns:
        Numeric value in the specified units (float or int).

    Raises:
        UnitConversionError: If target units are incompatible with this dimension.

    Examples:
        >>> distance = Distance.Meter(100)
        >>> print(f"100m = {distance.get_in(Distance.Yard):.5f} yards")
        100m = 109.36133 yards
    """
    return self.__class__.from_raw(self._value, units)

counter

counter(
    start: Number = 0,
    step: Number = 1,
    end: Optional[Number] = None,
) -> Iterable[Number]

Generate a sequence of numbers with optional bounds.

Creates an arithmetic sequence starting at 'start' with a constant increment/decrement of 'step'. Can generate infinite sequences or bounded sequences up to 'end'.

Parameters:

Name Type Description Default
start Number

Initial value for the sequence. Defaults to 0.

0
step Number

Increment/decrement step value. Cannot be 0 for infinite iteration. Positive values create ascending sequences, negative values create descending sequences. Defaults to 1.

1
end Optional[Number]

Final value (exclusive) for bounded sequences. If None, creates an infinite sequence. Defaults to None.

None

Yields:

Name Type Description
Number Iterable[Number]

The next value in the arithmetic sequence.

Raises:

Type Description
ValueError

If 'step' is 0 for infinite iteration, or if 'step' has the wrong sign for the given 'start' and 'end' range (e.g., positive step with start > end).

Examples:

>>> # Finite ascending sequence
>>> list(counter(0, 1, 5))
[0, 1, 2, 3, 4]
>>> # Finite descending sequence
>>> list(counter(10, -2, 0))
[10, 8, 6, 4, 2]
>>> # Infinite sequence (first 3 values)
>>> iter_seq = counter(1, 0.5)
>>> [next(iter_seq) for _ in range(3)]
[1, 1.5, 2.0]
Source code in py_ballisticcalc/unit.py
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
def counter(start: Number = 0, step: Number = 1, end: Optional[Number] = None) -> Iterable[Number]:
    """Generate a sequence of numbers with optional bounds.

    Creates an arithmetic sequence starting at 'start' with a constant increment/decrement of 'step'.
    Can generate infinite sequences or bounded sequences up to 'end'.

    Args:
        start: Initial value for the sequence. Defaults to 0.
        step: Increment/decrement step value. Cannot be 0 for infinite iteration.
              Positive values create ascending sequences, negative values create descending sequences. Defaults to 1.
        end: Final value (exclusive) for bounded sequences. If None, creates an infinite sequence. Defaults to None.

    Yields:
        Number: The next value in the arithmetic sequence.

    Raises:
        ValueError: If 'step' is 0 for infinite iteration, or if 'step' has the wrong sign
                    for the given 'start' and 'end' range (e.g., positive step with start > end).

    Examples:
        >>> # Finite ascending sequence
        >>> list(counter(0, 1, 5))
        [0, 1, 2, 3, 4]

        >>> # Finite descending sequence
        >>> list(counter(10, -2, 0))
        [10, 8, 6, 4, 2]

        >>> # Infinite sequence (first 3 values)
        >>> iter_seq = counter(1, 0.5)
        >>> [next(iter_seq) for _ in range(3)]
        [1, 1.5, 2.0]
    """
    if step == 0:
        if end is None:
            raise ValueError("For infinite iteration, 'step' cannot be zero.")
        else:
            if (end > start and start <= end) or (end < start and start >= end) or (start == end):
                yield start
            return

    current = start
    if end is None:
        while True:
            yield current
            current += step
    else:
        if step > 0:
            if start > end:
                raise ValueError("For an incremental step (step > 0), 'start' cannot be greater than 'end'.")
            while current < end:
                yield current
                current += step
        else:  # step < 0
            if start < end:
                raise ValueError("For a decrementing step (step < 0), 'start' cannot be less than 'end'.")
            while current > end:
                yield current
                current += step

iterator

iterator(
    items: Sequence[Number],
    /,
    *,
    sort: bool = False,
    key: Optional[Callable[[Number], Any]] = None,
    reverse: bool = False,
) -> Generator[Number, None, None]

Create a generator from a sequence of numbers with optional sorting.

Provides a flexible iterator interface for numeric sequences. Supports optional sorting with custom key functions and reverse ordering.

Parameters:

Name Type Description Default
items Sequence[Number]

Sequence of numeric values (integers or floats) to iterate over.

required
sort bool

If True, sort the items before iteration. Defaults to False.

False
key Optional[Callable[[Number], Any]]

Optional function to extract comparison key from each item. Used only when sort=True. Defaults to None.

None
reverse bool

If True, reverse the iteration order (used with sorting). Defaults to False.

False

Yields:

Name Type Description
Number Number

Each numeric value from the sequence in the specified order.

Examples:

>>> # Basic iteration
>>> list(iterator([3, 1, 4, 2]))
[3, 1, 4, 2]
>>> # Sorted iteration
>>> list(iterator([3, 1, 4, 2], sort=True))
[1, 2, 3, 4]
>>> # Reverse sorted iteration
>>> list(iterator([3, 1, 4, 2], sort=True, reverse=True))
[4, 3, 2, 1]
>>> # Custom key function
>>> list(iterator([-3, 1, -4, 2], sort=True, key=abs))
[1, 2, -3, -4]
Source code in py_ballisticcalc/unit.py
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
def iterator(items: Sequence[Number], /, *,
             sort: bool = False,
             key: Optional[Callable[[Number], Any]] = None,
             reverse: bool = False) -> Generator[Number, None, None]:
    """Create a generator from a sequence of numbers with optional sorting.

    Provides a flexible iterator interface for numeric sequences.
    Supports optional sorting with custom key functions and reverse ordering.

    Args:
        items: Sequence of numeric values (integers or floats) to iterate over.
        sort: If True, sort the items before iteration. Defaults to False.
        key: Optional function to extract comparison key from each item.
             Used only when sort=True. Defaults to None.
        reverse: If True, reverse the iteration order (used with sorting).
                 Defaults to False.

    Yields:
        Number: Each numeric value from the sequence in the specified order.

    Examples:
        >>> # Basic iteration
        >>> list(iterator([3, 1, 4, 2]))
        [3, 1, 4, 2]

        >>> # Sorted iteration
        >>> list(iterator([3, 1, 4, 2], sort=True))
        [1, 2, 3, 4]

        >>> # Reverse sorted iteration
        >>> list(iterator([3, 1, 4, 2], sort=True, reverse=True))
        [4, 3, 2, 1]

        >>> # Custom key function
        >>> list(iterator([-3, 1, -4, 2], sort=True, key=abs))
        [1, 2, -3, -4]
    """
    if sort:
        items = sorted(items, key=key, reverse=reverse)
    for v in items:
        yield v

Angular

Angular(value: Number, units: Unit)

Bases: GenericDimension

Angular measurements. Raw value is radians.

This class tries to normalize angles to the range (-π, π].

Methods:

Name Description
to_raw

Normalize angle to (-π, π].

Source code in py_ballisticcalc/unit.py
738
739
740
741
742
743
744
745
746
def __init__(self, value: Number, units: Unit):
    """Initialize a unit measurement with value and unit type.

    Args:
        value: Numeric value of the measurement in the specified units.
        units: Unit enum specifying the unit type for the value.
    """
    self._value: Number = self.__class__.to_raw(value, units)
    self._defined_units: Unit = units

Functions

to_raw classmethod
to_raw(value: Number, units: Unit) -> Number

Normalize angle to (-π, π].

Source code in py_ballisticcalc/unit.py
1115
1116
1117
1118
1119
1120
1121
1122
@override
@classmethod
def to_raw(cls, value: Number, units: Unit) -> Number:
    """Normalize angle to (-π, π]."""
    radians = super().to_raw(value, units)
    # Normalize to [-π, π)
    r = (radians + pi) % (2.0 * pi) - pi
    return r if r > -pi else pi  # move -π to +π

Distance

Distance(value: Number, units: Unit)

Bases: GenericDimension

Distance measurements. Raw value is inches.

Source code in py_ballisticcalc/unit.py
738
739
740
741
742
743
744
745
746
def __init__(self, value: Number, units: Unit):
    """Initialize a unit measurement with value and unit type.

    Args:
        value: Numeric value of the measurement in the specified units.
        units: Unit enum specifying the unit type for the value.
    """
    self._value: Number = self.__class__.to_raw(value, units)
    self._defined_units: Unit = units

Energy

Energy(value: Number, units: Unit)

Bases: GenericDimension

Energy measurements. Raw unit is foot-pounds.

Source code in py_ballisticcalc/unit.py
738
739
740
741
742
743
744
745
746
def __init__(self, value: Number, units: Unit):
    """Initialize a unit measurement with value and unit type.

    Args:
        value: Numeric value of the measurement in the specified units.
        units: Unit enum specifying the unit type for the value.
    """
    self._value: Number = self.__class__.to_raw(value, units)
    self._defined_units: Unit = units

Pressure

Pressure(value: Number, units: Unit)

Bases: GenericDimension

Pressure unit. Raw value is mmHg.

Source code in py_ballisticcalc/unit.py
738
739
740
741
742
743
744
745
746
def __init__(self, value: Number, units: Unit):
    """Initialize a unit measurement with value and unit type.

    Args:
        value: Numeric value of the measurement in the specified units.
        units: Unit enum specifying the unit type for the value.
    """
    self._value: Number = self.__class__.to_raw(value, units)
    self._defined_units: Unit = units

Temperature

Temperature(value: Number, units: Unit)

Bases: GenericDimension

Temperature unit. Raw value is Fahrenheit.

This dimension only supports addition and subtraction operations, and tries to clamp results at absolute zero.

Methods:

Name Description
new_from_raw

Create Temperature from raw Fahrenheit value into target units.

Source code in py_ballisticcalc/unit.py
738
739
740
741
742
743
744
745
746
def __init__(self, value: Number, units: Unit):
    """Initialize a unit measurement with value and unit type.

    Args:
        value: Numeric value of the measurement in the specified units.
        units: Unit enum specifying the unit type for the value.
    """
    self._value: Number = self.__class__.to_raw(value, units)
    self._defined_units: Unit = units

Functions

new_from_raw classmethod
new_from_raw(
    raw_value: float, to_units: Unit
) -> Temperature

Create Temperature from raw Fahrenheit value into target units.

Unlike other dimensions, Temperature uses affine conversions; this method relies on from_raw instead of dividing by a scale factor.

Source code in py_ballisticcalc/unit.py
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
@override
@classmethod
def new_from_raw(cls, raw_value: float, to_units: Unit) -> Temperature:
    """Create Temperature from raw Fahrenheit value into target units.

    Unlike other dimensions, Temperature uses affine conversions; this method
        relies on `from_raw` instead of dividing by a scale factor.
    """
    # Temperature conversion uses affine transforms; do not divide by a factor.
    value_in_units = cls.from_raw(raw_value, to_units)
    return cls(value_in_units, to_units)

Time

Time(value: Number, units: Unit)

Bases: GenericDimension

Time measurements. Raw unit is seconds.

Source code in py_ballisticcalc/unit.py
738
739
740
741
742
743
744
745
746
def __init__(self, value: Number, units: Unit):
    """Initialize a unit measurement with value and unit type.

    Args:
        value: Numeric value of the measurement in the specified units.
        units: Unit enum specifying the unit type for the value.
    """
    self._value: Number = self.__class__.to_raw(value, units)
    self._defined_units: Unit = units

Velocity

Velocity(value: Number, units: Unit)

Bases: GenericDimension

Velocity measurements. Raw unit is meters per second.

Source code in py_ballisticcalc/unit.py
738
739
740
741
742
743
744
745
746
def __init__(self, value: Number, units: Unit):
    """Initialize a unit measurement with value and unit type.

    Args:
        value: Numeric value of the measurement in the specified units.
        units: Unit enum specifying the unit type for the value.
    """
    self._value: Number = self.__class__.to_raw(value, units)
    self._defined_units: Unit = units

Weight

Weight(value: Number, units: Unit)

Bases: GenericDimension

Weight unit. Raw value is grains.

Source code in py_ballisticcalc/unit.py
738
739
740
741
742
743
744
745
746
def __init__(self, value: Number, units: Unit):
    """Initialize a unit measurement with value and unit type.

    Args:
        value: Numeric value of the measurement in the specified units.
        units: Unit enum specifying the unit type for the value.
    """
    self._value: Number = self.__class__.to_raw(value, units)
    self._defined_units: Unit = units

UnitAliases = {('radian', 'rad'): Unit.Radian, ('degree', 'deg'): Unit.Degree, ('moa',): Unit.MOA, ('mil',): Unit.Mil, ('mrad',): Unit.MRad, ('thousandth', 'ths'): Unit.Thousandth, ('inch/100yd', 'in/100yd', 'in/100yard', 'inper100yd'): Unit.InchesPer100Yd, ('centimeter/100m', 'cm/100m', 'cm/100meter', 'centimeter/100meter', 'cmper100m'): Unit.CmPer100m, ('hour', 'h'): Unit.OClock, ('inch', 'in'): Unit.Inch, ('foot', 'feet', 'ft'): Unit.Foot, ('yard', 'yd'): Unit.Yard, ('mile', 'mi', 'mi.'): Unit.Mile, ('nauticalmile', 'nm', 'nmi'): Unit.NauticalMile, ('millimeter', 'mm'): Unit.Millimeter, ('centimeter', 'cm'): Unit.Centimeter, ('meter', 'm'): Unit.Meter, ('kilometer', 'km'): Unit.Kilometer, ('line', 'ln', 'liniа'): Unit.Line, ('footpound', 'foot-pound', 'ft⋅lbf', 'ft⋅lb', 'foot*pound', 'ft*lbf', 'ft*lb'): Unit.FootPound, ('joule', 'J'): Unit.Joule, ('mmHg',): Unit.MmHg, ('inHg', '"Hg'): Unit.InHg, ('bar',): Unit.Bar, ('hectopascal', 'hPa'): Unit.hPa, ('psi', 'lbf/in2'): Unit.PSI, ('fahrenheit', '°F', 'F', 'degF'): Unit.Fahrenheit, ('celsius', '°C', 'C', 'degC'): Unit.Celsius, ('kelvin', '°K', 'K', 'degK'): Unit.Kelvin, ('rankin', '°R', 'R', 'degR'): Unit.Rankin, ('meter/second', 'm/s', 'meter/s', 'm/second', 'mps'): Unit.MPS, ('kilometer/hour', 'km/h', 'kilometer/h', 'km/hour', 'kmh'): Unit.KMH, ('foot/second', 'feet/second', 'ft/s', 'foot/s', 'feet/s', 'ft/second', 'fps'): Unit.FPS, ('mile/hour', 'mi/h', 'mile/h', 'mi/hour', 'mph'): Unit.MPH, ('knot', 'kn', 'kt'): Unit.KT, ('grain', 'gr', 'grn'): Unit.Grain, ('ounce', 'oz'): Unit.Ounce, ('gram', 'g'): Unit.Gram, ('pound', 'lb'): Unit.Pound, ('kilogram', 'kilogramme', 'kg'): Unit.Kilogram, ('newton', 'N'): Unit.Newton, ('minute', 'min'): Unit.Minute, ('second', 's', 'sec'): Unit.Second, ('millisecond', 'ms'): Unit.Millisecond, ('microsecond', 'us', 'µs'): Unit.Microsecond, ('nanosecond', 'ns'): Unit.Nanosecond, ('picosecond', 'ps'): Unit.Picosecond} module-attribute

UnitAliases: UnitAliasesType = {
    ('radian', 'rad'): Unit.Radian,
    ('degree', 'deg'): Unit.Degree,
    ('moa',): Unit.MOA,
    ('mil',): Unit.Mil,
    ('mrad',): Unit.MRad,
    ('thousandth', 'ths'): Unit.Thousandth,
    ('inch/100yd', 'in/100yd', 'in/100yard', 'inper100yd'): Unit.InchesPer100Yd,
    ('centimeter/100m', 'cm/100m', 'cm/100meter', 'centimeter/100meter', 'cmper100m'): Unit.CmPer100m,
    ('hour', 'h'): Unit.OClock,

    ('inch', 'in'): Unit.Inch,
    ('foot', 'feet', 'ft'): Unit.Foot,
    ('yard', 'yd'): Unit.Yard,
    ('mile', 'mi', 'mi.'): Unit.Mile,
    ('nauticalmile', 'nm', 'nmi'): Unit.NauticalMile,
    ('millimeter', 'mm'): Unit.Millimeter,
    ('centimeter', 'cm'): Unit.Centimeter,
    ('meter', 'm'): Unit.Meter,
    ('kilometer', 'km'): Unit.Kilometer,
    ('line', 'ln', 'liniа'): Unit.Line,

    ('footpound', 'foot-pound', 'ft⋅lbf', 'ft⋅lb', 'foot*pound', 'ft*lbf', 'ft*lb'): Unit.FootPound,
    ('joule', 'J'): Unit.Joule,

    ('mmHg',): Unit.MmHg,
    ('inHg', '"Hg'): Unit.InHg,
    ('bar',): Unit.Bar,
    ('hectopascal', 'hPa'): Unit.hPa,
    ('psi', 'lbf/in2'): Unit.PSI,

    ('fahrenheit', '°F', 'F', 'degF'): Unit.Fahrenheit,
    ('celsius', '°C', 'C', 'degC'): Unit.Celsius,
    ('kelvin', '°K', 'K', 'degK'): Unit.Kelvin,
    ('rankin', '°R', 'R', 'degR'): Unit.Rankin,

    ('meter/second', 'm/s', 'meter/s', 'm/second', 'mps'): Unit.MPS,
    ('kilometer/hour', 'km/h', 'kilometer/h', 'km/hour', 'kmh'): Unit.KMH,
    ('foot/second', 'feet/second', 'ft/s', 'foot/s', 'feet/s', 'ft/second', 'fps'): Unit.FPS,
    ('mile/hour', 'mi/h', 'mile/h', 'mi/hour', 'mph'): Unit.MPH,
    ('knot', 'kn', 'kt'): Unit.KT,

    ('grain', 'gr', 'grn'): Unit.Grain,
    ('ounce', 'oz'): Unit.Ounce,
    ('gram', 'g'): Unit.Gram,
    ('pound', 'lb'): Unit.Pound,
    ('kilogram', 'kilogramme', 'kg'): Unit.Kilogram,
    ('newton', 'N'): Unit.Newton,

    ('minute', 'min'): Unit.Minute,
    ('second', 's', 'sec'): Unit.Second,
    ('millisecond', 'ms'): Unit.Millisecond,
    ('microsecond', 'us', 'µs'): Unit.Microsecond,
    ('nanosecond', 'ns'): Unit.Nanosecond,
    ('picosecond', 'ps'): Unit.Picosecond,
}