public void Abs(double value, AngularUnit unit, double expected)
        {
            var v  = new Measurement <AngularUnit>(value, unit);
            var v1 = MeasurementMath.Abs(v);

            v1.Value.Should().BeApproximately(expected, 1e-10);
            v1.Unit.Should().Be(unit);

            var v2 = v.Abs();

            v2.Value.Should().BeApproximately(expected, 1e-10);
            v2.Unit.Should().Be(unit);
        }
Example #2
0
        /// <summary>
        /// Calculates the sight angle for the specified zero distance
        /// </summary>
        /// <param name="ammunition"></param>
        /// <param name="rifle"></param>
        /// <param name="atmosphere"></param>
        /// <returns></returns>
        public Measurement <AngularUnit> SightAngle(Ammunition ammunition, Rifle rifle, Atmosphere atmosphere)
        {
            Measurement <DistanceUnit> rangeTo         = rifle.Zero.Distance * 2;
            Measurement <DistanceUnit> step            = rifle.Zero.Distance / 100;
            Measurement <DistanceUnit> calculationStep = GetCalculationStep(step);

            if (rifle.Zero.Atmosphere != null)
            {
                atmosphere = rifle.Zero.Atmosphere;
            }

            if (atmosphere == null)
            {
                atmosphere = new Atmosphere();
            }

            if (rifle.Zero.Ammunition != null)
            {
                ammunition = rifle.Zero.Ammunition;
            }

            Measurement <DistanceUnit> alt0 = atmosphere.Altitude;
            Measurement <DistanceUnit> altDelta = new Measurement <DistanceUnit>(1, DistanceUnit.Meter);
            double densityFactor = 0, drag;
            Measurement <VelocityUnit> mach = new Measurement <VelocityUnit>(0, VelocityUnit.MetersPerSecond);

            var sightAngle    = new Measurement <AngularUnit>(150, AngularUnit.MOA);
            var barrelAzimuth = new Measurement <AngularUnit>(0, AngularUnit.Radian);

            for (int approximation = 0; approximation < 100; approximation++)
            {
                var barrelElevation = sightAngle;

                Measurement <VelocityUnit> velocity = ammunition.MuzzleVelocity;
                TimeSpan time = new TimeSpan(0);

                //x - distance towards target,
                //y - drop and
                //z - windage
                var rangeVector = new Vector <DistanceUnit>(new Measurement <DistanceUnit>(0, DistanceUnit.Meter),
                                                            -rifle.Sight.SightHeight,
                                                            new Measurement <DistanceUnit>(0, DistanceUnit.Meter));

                var velocityVector = new Vector <VelocityUnit>(velocity * barrelElevation.Cos() * barrelAzimuth.Cos(),
                                                               velocity * barrelElevation.Sin(),
                                                               velocity * barrelElevation.Cos() * barrelAzimuth.Sin());

                Measurement <DistanceUnit> maximumRange   = rangeTo;
                Measurement <DistanceUnit> lastAtAltitude = new Measurement <DistanceUnit>(-1000000, DistanceUnit.Meter);
                DragTableNode dragTableNode = null;

                double adjustBallisticFactorForVelocityUnits = Measurement <VelocityUnit> .Convert(1, velocity.Unit, VelocityUnit.FeetPerSecond);

                double ballisicFactor = 2.08551e-04 * adjustBallisticFactorForVelocityUnits / ammunition.BallisticCoefficient.Value;
                var    earthGravity   = (new Measurement <VelocityUnit>(Measurement <AccelerationUnit> .Convert(1, AccelerationUnit.EarthGravity, AccelerationUnit.MeterPerSecondSquare),
                                                                        VelocityUnit.MetersPerSecond)).To(velocity.Unit);

                //run all the way down the range
                while (rangeVector.X <= maximumRange)
                {
                    Measurement <DistanceUnit> alt = alt0 + rangeVector.Y;

                    //update density and Mach velocity each 10 feet
                    if (MeasurementMath.Abs(lastAtAltitude - alt) > altDelta)
                    {
                        atmosphere.AtAltitude(alt, out densityFactor, out mach);
                        lastAtAltitude = alt;
                    }

                    if (velocity < MinimumVelocity || rangeVector.Y < -MaximumDrop)
                    {
                        break;
                    }

                    TimeSpan deltaTime = BallisticMath.TravelTime(calculationStep, velocityVector.X);

                    double currentMach = velocity / mach;

                    //find Mach node for the first time
                    if (dragTableNode == null)
                    {
                        dragTableNode = DragTable.Get(ammunition.BallisticCoefficient.Table).Find(currentMach);
                    }

                    //walk towards the beginning the table as velocity drops
                    while (dragTableNode.Previous.Mach > currentMach)
                    {
                        dragTableNode = dragTableNode.Previous;
                    }

                    drag = ballisicFactor * densityFactor * dragTableNode.CalculateDrag(currentMach) * velocity.Value;

                    velocityVector = new Vector <VelocityUnit>(
                        velocityVector.X - deltaTime.TotalSeconds * drag * velocityVector.X,
                        velocityVector.Y - deltaTime.TotalSeconds * drag * velocityVector.Y - earthGravity * deltaTime.TotalSeconds,
                        velocityVector.Z - deltaTime.TotalSeconds * drag * velocityVector.Z);

                    var deltaRangeVector = new Vector <DistanceUnit>(calculationStep,
                                                                     new Measurement <DistanceUnit>(velocityVector.Y.In(VelocityUnit.MetersPerSecond) * deltaTime.TotalSeconds, DistanceUnit.Meter),
                                                                     new Measurement <DistanceUnit>(velocityVector.Z.In(VelocityUnit.MetersPerSecond) * deltaTime.TotalSeconds, DistanceUnit.Meter));

                    rangeVector += deltaRangeVector;

                    if (rangeVector.X >= rifle.Zero.Distance)
                    {
                        if (Math.Abs(rangeVector.Y.In(DistanceUnit.Millimeter)) < 1)
                        {
                            return(sightAngle);
                        }

                        sightAngle += new Measurement <AngularUnit>(-rangeVector.Y.In(DistanceUnit.Centimeter) / rifle.Zero.Distance.In(DistanceUnit.Meter) * 100, AngularUnit.CmPer100Meters);
                        break;
                    }

                    velocity = velocityVector.Magnitude;
                    time     = time.Add(BallisticMath.TravelTime(deltaRangeVector.Magnitude, velocity));
                }
            }
            throw new InvalidOperationException("Cannot find zero parameters");
        }
Example #3
0
        /// <summary>
        /// Calculates the trajectory for the specified parameters.
        /// </summary>
        /// <param name="ammunition"></param>
        /// <param name="rifle"></param>
        /// <param name="atmosphere"></param>
        /// <param name="shot"></param>
        /// <param name="wind"></param>
        /// <returns></returns>
        public TrajectoryPoint[] Calculate(Ammunition ammunition, Rifle rifle, Atmosphere atmosphere, ShotParameters shot, Wind[] wind)
        {
            Measurement <DistanceUnit> rangeTo         = shot.MaximumDistance;
            Measurement <DistanceUnit> step            = shot.Step;
            Measurement <DistanceUnit> calculationStep = GetCalculationStep(step);

            if (atmosphere == null)
            {
                atmosphere = new Atmosphere();
            }

            Measurement <DistanceUnit> alt0 = atmosphere.Altitude;
            Measurement <DistanceUnit> altDelta = new Measurement <DistanceUnit>(1, DistanceUnit.Meter);
            double densityFactor = 0, drag;
            Measurement <VelocityUnit> mach = new Measurement <VelocityUnit>(0, VelocityUnit.MetersPerSecond);

            double stabilityCoefficient = 1;
            bool   calculateDrift;

            if (rifle.Rifling != null && ammunition.BulletDiameter != null && ammunition.BulletLength != null)
            {
                stabilityCoefficient = CalculateStabilityCoefficient(ammunition, rifle, atmosphere);
                calculateDrift       = true;
            }
            else
            {
                calculateDrift = false;
            }

            TrajectoryPoint[] trajectoryPoints = new TrajectoryPoint[(int)(Math.Floor(rangeTo / step)) + 1];

            var barrelAzimuth   = new Measurement <AngularUnit>(0.0, AngularUnit.Radian);
            var barrelElevation = shot.SightAngle;

            if (shot.ShotAngle != null)
            {
                barrelElevation += shot.ShotAngle.Value;
            }

            Measurement <VelocityUnit> velocity = ammunition.MuzzleVelocity;
            TimeSpan time = new TimeSpan(0);

            int currentWind = 0;
            Measurement <DistanceUnit> nextWindRange = new Measurement <DistanceUnit>(1e7, DistanceUnit.Meter);
            Vector <VelocityUnit>      windVector;

            if (wind == null || wind.Length < 1)
            {
                windVector = new Vector <VelocityUnit>();
            }
            else
            {
                if (wind.Length > 1 && wind[0].MaximumRange != null)
                {
                    nextWindRange = wind[0].MaximumRange.Value;
                }
                windVector = WindVector(shot, wind[0], velocity.Unit);
            }

            //x - distance towards target,
            //y - drop and
            //z - windage
            var rangeVector = new Vector <DistanceUnit>(new Measurement <DistanceUnit>(0, DistanceUnit.Meter),
                                                        -rifle.Sight.SightHeight,
                                                        new Measurement <DistanceUnit>(0, DistanceUnit.Meter));

            var velocityVector = new Vector <VelocityUnit>(velocity * barrelElevation.Cos() * barrelAzimuth.Cos(),
                                                           velocity * barrelElevation.Sin(),
                                                           velocity * barrelElevation.Cos() * barrelAzimuth.Sin());

            int currentItem = 0;
            Measurement <DistanceUnit> maximumRange      = rangeTo + calculationStep;
            Measurement <DistanceUnit> nextRangeDistance = new Measurement <DistanceUnit>(0, DistanceUnit.Meter);

            Measurement <DistanceUnit> lastAtAltitude = new Measurement <DistanceUnit>(-1000000, DistanceUnit.Meter);
            DragTableNode dragTableNode = null;

            double adjustBallisticFactorForVelocityUnits = Measurement <VelocityUnit> .Convert(1, velocity.Unit, VelocityUnit.FeetPerSecond);

            double ballisicFactor = 2.08551e-04 * adjustBallisticFactorForVelocityUnits / ammunition.BallisticCoefficient.Value;
            var    earthGravity   = (new Measurement <VelocityUnit>(Measurement <AccelerationUnit> .Convert(1, AccelerationUnit.EarthGravity, AccelerationUnit.MeterPerSecondSquare),
                                                                    VelocityUnit.MetersPerSecond)).To(velocity.Unit);

            //run all the way down the range
            while (rangeVector.X <= maximumRange)
            {
                Measurement <DistanceUnit> alt = alt0 + rangeVector.Y;

                //update density and Mach velocity each 10 feet of altitude
                if (MeasurementMath.Abs(lastAtAltitude - alt) > altDelta)
                {
                    atmosphere.AtAltitude(alt, out densityFactor, out mach);
                    lastAtAltitude = alt;
                }

                if (velocity < MinimumVelocity || rangeVector.Y < -MaximumDrop)
                {
                    break;
                }

                if (rangeVector.X >= nextWindRange)
                {
                    currentWind++;
                    windVector = WindVector(shot, wind[currentWind], velocity.Unit);

                    if (currentWind == wind.Length - 1 || wind[currentWind].MaximumRange == null)
                    {
                        nextWindRange = new Measurement <DistanceUnit>(1e7, DistanceUnit.Meter);
                    }
                    else
                    {
                        nextWindRange = wind[currentWind].MaximumRange.Value;
                    }
                }

                if (rangeVector.X >= nextRangeDistance)
                {
                    var windage = rangeVector.Z;
                    if (calculateDrift)
                    {
                        windage += new Measurement <DistanceUnit>(1.25 * (stabilityCoefficient + 1.2) * Math.Pow(time.TotalSeconds, 1.83) * (rifle.Rifling.Direction == TwistDirection.Right ? 1 : -1), DistanceUnit.Inch);
                    }

                    trajectoryPoints[currentItem] = new TrajectoryPoint(
                        time: time,
                        weight: ammunition.Weight,
                        distance: rangeVector.X,
                        velocity: velocity,
                        mach: velocity / mach,
                        drop: rangeVector.Y,
                        windage: windage);
                    nextRangeDistance += step;
                    currentItem++;
                    if (currentItem == trajectoryPoints.Length)
                    {
                        break;
                    }
                }

                TimeSpan deltaTime = BallisticMath.TravelTime(calculationStep, velocityVector.X);

                var velocityAdjusted = velocityVector - windVector;
                velocity = velocityAdjusted.Magnitude;
                double currentMach = velocity / mach;

                //find Mach node for the first time
                if (dragTableNode == null)
                {
                    dragTableNode = DragTable.Get(ammunition.BallisticCoefficient.Table).Find(currentMach);
                }

                //walk towards the beginning the table as velocity drops
                while (dragTableNode.Mach > currentMach)
                {
                    dragTableNode = dragTableNode.Previous;
                }

                drag = ballisicFactor * densityFactor * dragTableNode.CalculateDrag(currentMach) * velocity.Value;

                velocityVector = new Vector <VelocityUnit>(
                    velocityVector.X - deltaTime.TotalSeconds * drag * velocityAdjusted.X,
                    velocityVector.Y - deltaTime.TotalSeconds * drag * velocityAdjusted.Y
                    - earthGravity * deltaTime.TotalSeconds,
                    velocityVector.Z - deltaTime.TotalSeconds * drag * velocityAdjusted.Z);

                var deltaRangeVector = new Vector <DistanceUnit>(calculationStep,
                                                                 new Measurement <DistanceUnit>(velocityVector.Y.In(VelocityUnit.MetersPerSecond) * deltaTime.TotalSeconds, DistanceUnit.Meter),
                                                                 new Measurement <DistanceUnit>(velocityVector.Z.In(VelocityUnit.MetersPerSecond) * deltaTime.TotalSeconds, DistanceUnit.Meter));

                rangeVector += deltaRangeVector;
                velocity     = velocityVector.Magnitude;
                time         = time.Add(BallisticMath.TravelTime(deltaRangeVector.Magnitude, velocity));
            }

            return(trajectoryPoints);
        }