public void RecangularPrismVolume(double a, DistanceUnit ua, double b, DistanceUnit ub, double c, DistanceUnit uc, double e, VolumeUnit eu)
        {
            var x = MeasurementMath.RectangleArea(new Measurement <DistanceUnit>(a, ua), new Measurement <DistanceUnit>(b, ub));
            var v = MeasurementMath.RecangularPrismVolume(x, new Measurement <DistanceUnit>(c, uc));

            v.In(eu).Should().BeApproximately(e, 1e-6);
        }
        public void Tan(double value, AngularUnit unit, double expected)
        {
            var v = new Measurement <AngularUnit>(value, unit);

            MeasurementMath.Tan(v).Should().BeApproximately(expected, 1e-10);
            MeasurementMath.Atan(expected).In(unit).Should().BeApproximately(value, 1e-7);
        }
        public void Pow(double value, AngularUnit unit, double exp, double expected)
        {
            var v  = new Measurement <AngularUnit>(value, unit);
            var v1 = MeasurementMath.Pow(v, exp);

            v1.Value.Should().BeApproximately(expected, 1e-10);
            v1.Unit.Should().Be(unit);
        }
 /// <summary>
 /// Constructor
 /// </summary>
 /// <param name="time"></param>
 /// <param name="weight"></param>
 /// <param name="distance"></param>
 /// <param name="velocity"></param>
 /// <param name="mach"></param>
 /// <param name="drop"></param>
 /// <param name="windage"></param>
 public TrajectoryPoint(TimeSpan time, Measurement <WeightUnit> weight, Measurement <DistanceUnit> distance,
                        Measurement <VelocityUnit> velocity, double mach, Measurement <DistanceUnit> drop,
                        Measurement <DistanceUnit> windage)
     : this(time, distance, velocity, mach, drop, windage,
            MeasurementMath.KineticEnergy(weight, velocity),
            BallisticMath.OptimalGameWeight(weight, velocity))
 {
 }
        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);
        }
        public TrajectoryPoint(TimeSpan time, Measurement <DistanceUnit> distance,
                               Measurement <VelocityUnit> velocity, double mach, Measurement <DistanceUnit> drop,
                               Measurement <DistanceUnit> windage, Measurement <EnergyUnit> energy,
                               Measurement <WeightUnit> optimalGameWeight)
        {
            Time           = time;
            Distance       = distance;
            Velocity       = velocity;
            Drop           = drop;
            DropAdjustment = MeasurementMath.Atan(Drop / Distance);
            Mach           = mach;

            Windage           = windage;
            WindageAdjustment = MeasurementMath.Atan(Windage / Distance);
            Energy            = energy;
            OptimalGameWeight = optimalGameWeight;
        }
        public void Velocity(double distance, DistanceUnit distanceUnit, double velocity, VelocityUnit velocityUnit, double time)
        {
            var      distance1 = new Measurement <DistanceUnit>(distance, distanceUnit);
            var      velocity1 = new Measurement <VelocityUnit>(velocity, velocityUnit);
            TimeSpan ts1       = TimeSpan.FromSeconds(time);

            TimeSpan ts2 = MeasurementMath.TravelTime(distance1, velocity1);

            ts2.TotalSeconds.Should().BeApproximately(time, 1e-5);

            var distance2 = MeasurementMath.DistanceTraveled(velocity1, ts1);

            distance2.In(distanceUnit).Should().BeApproximately(distance, 1e-5);

            var velocity2 = MeasurementMath.Velocity(distance1, ts1);

            velocity2.In(velocityUnit).Should().BeApproximately(velocity, 1e-5);
        }
Пример #8
0
        /// <summary>
        /// <para>Draws a square target on the specified canvas</para>
        /// <para>Use this method before drawing the reticle</para>
        /// </summary>
        /// <param name="trajectory">The trajectory to get the BDC parameters</param>
        /// <param name="targetSize">The size of a side of a rectangular target</param>
        /// <param name="targetDistance">The distance to the target</param>
        /// <param name="color">The color of the target</param>
        public void DrawTarget(IEnumerable <TrajectoryPoint> trajectory,
                               Measurement <DistanceUnit> targetSize, Measurement <DistanceUnit> targetDistance,
                               string color)
        {
            var angularTargetSize = MeasurementMath.Atan(targetSize / targetDistance);
            var trajectoryPoint   = FindByDistance(trajectory, targetDistance);

            if (trajectoryPoint != null)
            {
                var centerY = trajectoryPoint.DropAdjustment;
                var centerX = trajectoryPoint.WindageAdjustment;
                var x0      = centerX - angularTargetSize / 2;
                var y0      = centerY + angularTargetSize / 2;

                DrawElement(new ReticleRectangle()
                {
                    TopLeft = new ReticlePosition(x0, y0),
                    Size    = new ReticlePosition(angularTargetSize, angularTargetSize),
                    Fill    = false,
                    Color   = color,
                });
            }
        }
        public void RectangleArea(double a, DistanceUnit ua, double b, DistanceUnit ub, double e, AreaUnit eu)
        {
            var v = MeasurementMath.RectangleArea(new Measurement <DistanceUnit>(a, ua), new Measurement <DistanceUnit>(b, ub));

            v.In(eu).Should().BeApproximately(e, 1e-6);
        }
        public void KineticEnergy(double m, WeightUnit wu, double v, VelocityUnit vu, double e, EnergyUnit eu)
        {
            var r = MeasurementMath.KineticEnergy(new Measurement <WeightUnit>(m, wu), new Measurement <VelocityUnit>(v, vu));

            r.In(eu).Should().BeApproximately(e, 1e-6);
        }
Пример #11
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");
        }
Пример #12
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);
        }