private static TrajectoryPoint[] Calculate(double bc, DragTableId id) { var dragTable = new MyDrag(); var ammo = new Ammunition( weight: new Measurement <WeightUnit>(168, WeightUnit.Grain), ballisticCoefficient: new BallisticCoefficient(bc, id), muzzleVelocity: new Measurement <VelocityUnit>(555, VelocityUnit.MetersPerSecond), bulletDiameter: new Measurement <DistanceUnit>(0.224, DistanceUnit.Inch), bulletLength: new Measurement <DistanceUnit>(0.9, DistanceUnit.Inch)); //define ACOG scope var sight = new Sight( sightHeight: new Measurement <DistanceUnit>(3.5, DistanceUnit.Inch), verticalClick: new Measurement <AngularUnit>(1.0 / 3.0, AngularUnit.InchesPer100Yards), horizontalClick: new Measurement <AngularUnit>(1.0 / 3.0, AngularUnit.InchesPer100Yards) ); //M16 rifling var rifling = new Rifling( riflingStep: new Measurement <DistanceUnit>(12, DistanceUnit.Inch), direction: TwistDirection.Right); //standard 100 yard ACOG zeroing var zero = new ZeroingParameters( distance: new Measurement <DistanceUnit>(50, DistanceUnit.Yard), ammunition: null, atmosphere: null ); //define rifle by sight, zeroing and rifling parameters var rifle = new Rifle(sight: sight, zero: zero, rifling: rifling); //define atmosphere var atmosphere = new Atmosphere( altitude: new Measurement <DistanceUnit>(0, DistanceUnit.Foot), pressure: new Measurement <PressureUnit>(29.92, PressureUnit.InchesOfMercury), pressureAtSeaLevel: false, temperature: new Measurement <TemperatureUnit>(59, TemperatureUnit.Fahrenheit), humidity: 0.78); var calc = new TrajectoryCalculator(); //shot parameters var shot = new ShotParameters() { MaximumDistance = new Measurement <DistanceUnit>(2000, DistanceUnit.Meter), Step = new Measurement <DistanceUnit>(100, DistanceUnit.Meter), //calculate sight angle for the specified zero distance SightAngle = calc.SightAngle(ammo, rifle, atmosphere, id == DragTableId.GC ? dragTable : null) }; //calculate trajectory return(calc.Calculate(ammo, rifle, atmosphere, shot, null, id == DragTableId.GC ? dragTable : null)); }
public void CustomTable() { TableLoader template = TableLoader.FromResource("g1_nowind"); const double velocityAccuracyInPercent = 0.005, dropAccuracyInMOA = 0.2, windageAccuracyInMOA = 0.2; var table = DragTable.Get(template.Ammunition.BallisticCoefficient.Table); template.Ammunition.BallisticCoefficient = new BallisticCoefficient(template.Ammunition.BallisticCoefficient.Value, DragTableId.GC); var cal = new TrajectoryCalculator(); ShotParameters shot = new ShotParameters() { Step = new Measurement <DistanceUnit>(50, DistanceUnit.Yard), MaximumDistance = new Measurement <DistanceUnit>(1000, DistanceUnit.Yard), SightAngle = cal.SightAngle(template.Ammunition, template.Rifle, template.Atmosphere, table), ShotAngle = template.ShotParameters?.ShotAngle, CantAngle = template.ShotParameters?.CantAngle, }; var trajectory = cal.Calculate(template.Ammunition, template.Rifle, template.Atmosphere, shot, null, table); trajectory.Length.Should().Be(template.Trajectory.Count); for (int i = 0; i < trajectory.Length; i++) { var point = trajectory[i]; var templatePoint = template.Trajectory[i]; point.Distance.In(templatePoint.Distance.Unit).Should().BeApproximately(templatePoint.Distance.Value, templatePoint.Distance.Value * velocityAccuracyInPercent, $"@{point.Distance:N0}"); point.Velocity.In(templatePoint.Velocity.Unit).Should().BeApproximately(templatePoint.Velocity.Value, templatePoint.Velocity.Value * velocityAccuracyInPercent, $"@{point.Distance:N0}"); var dropAccuracyInInch = Measurement <AngularUnit> .Convert(dropAccuracyInMOA, AngularUnit.MOA, AngularUnit.InchesPer100Yards) * templatePoint.Distance.In(DistanceUnit.Yard) / 100; var windageAccuracyInInch = Measurement <AngularUnit> .Convert(windageAccuracyInMOA, AngularUnit.MOA, AngularUnit.InchesPer100Yards) * templatePoint.Distance.In(DistanceUnit.Yard) / 100; point.Drop.In(DistanceUnit.Inch).Should().BeApproximately(templatePoint.Drop.In(DistanceUnit.Inch), dropAccuracyInInch, $"@{point.Distance:N0}"); point.Windage.In(DistanceUnit.Inch).Should().BeApproximately(templatePoint.Windage.In(DistanceUnit.Inch), windageAccuracyInInch, $"@{point.Distance:N0}"); } }
public void Custom2() { var template = TableLoader.FromResource("custom2"); using var stream = typeof(TrajectoryCalculatorTest).Assembly.GetManifestResourceStream($"BallisticCalculator.Test.resources.drg2.txt"); var table = DrgDragTable.Open(stream); const double velocityAccuracyInPercent = 0.015, dropAccuracyInMOA = 0.25; var cal = new TrajectoryCalculator(); ShotParameters shot = new ShotParameters() { Step = new Measurement <DistanceUnit>(100, DistanceUnit.Meter), MaximumDistance = new Measurement <DistanceUnit>(1500, DistanceUnit.Meter), SightAngle = cal.SightAngle(template.Ammunition, template.Rifle, template.Atmosphere, table), ShotAngle = template.ShotParameters?.ShotAngle, CantAngle = template.ShotParameters?.CantAngle, }; var winds = template.Wind == null ? null : new Wind[] { template.Wind }; var trajectory = cal.Calculate(template.Ammunition, template.Rifle, template.Atmosphere, shot, winds, table); trajectory.Length.Should().Be(template.Trajectory.Count); for (int i = 0; i < trajectory.Length; i++) { var point = trajectory[i]; var templatePoint = template.Trajectory[i]; point.Distance.In(templatePoint.Distance.Unit).Should().BeApproximately(templatePoint.Distance.Value, templatePoint.Distance.Value * velocityAccuracyInPercent, $"@{point.Distance:N0}"); point.Velocity.In(templatePoint.Velocity.Unit).Should().BeApproximately(templatePoint.Velocity.Value, templatePoint.Velocity.Value * velocityAccuracyInPercent, $"@{point.Distance:N0}"); if (i > 0) { var dropAccuracyInInch = Measurement <AngularUnit> .Convert(dropAccuracyInMOA, AngularUnit.MOA, AngularUnit.InchesPer100Yards) * templatePoint.Distance.In(DistanceUnit.Yard) / 100; point.Drop.In(DistanceUnit.Inch).Should().BeApproximately(templatePoint.Drop.In(DistanceUnit.Inch), dropAccuracyInInch, $"@{point.Distance:N0}"); } } }
public void CustomTableNotSpecifiedException() { TableLoader template = TableLoader.FromResource("g1_nowind"); template.Ammunition.BallisticCoefficient = new BallisticCoefficient(template.Ammunition.BallisticCoefficient.Value, DragTableId.GC); var cal = new TrajectoryCalculator(); ((Action)(() => cal.SightAngle(template.Ammunition, template.Rifle, template.Atmosphere))) .Should().Throw <ArgumentNullException>(); ShotParameters shot = new ShotParameters() { Step = new Measurement <DistanceUnit>(50, DistanceUnit.Yard), MaximumDistance = new Measurement <DistanceUnit>(1000, DistanceUnit.Yard), SightAngle = new Measurement <AngularUnit>(10, AngularUnit.MOA), ShotAngle = template.ShotParameters?.ShotAngle, CantAngle = template.ShotParameters?.CantAngle, }; ((Action)(() => cal.Calculate(template.Ammunition, template.Rifle, template.Atmosphere, shot, null))) .Should().Throw <ArgumentNullException>(); }
public void TrajectoryTest(string name, double velocityAccuracyInPercent, double dropAccuracyInMOA, double windageAccuracyInMOA) { TableLoader template = TableLoader.FromResource(name); var cal = new TrajectoryCalculator(); ShotParameters shot = new ShotParameters() { Step = new Measurement <DistanceUnit>(50, DistanceUnit.Yard), MaximumDistance = new Measurement <DistanceUnit>(1000, DistanceUnit.Yard), SightAngle = cal.SightAngle(template.Ammunition, template.Rifle, template.Atmosphere), ShotAngle = template.ShotParameters?.ShotAngle, CantAngle = template.ShotParameters?.CantAngle, }; var winds = template.Wind == null ? null : new Wind[] { template.Wind }; var trajectory = cal.Calculate(template.Ammunition, template.Rifle, template.Atmosphere, shot, winds); trajectory.Length.Should().Be(template.Trajectory.Count); for (int i = 0; i < trajectory.Length; i++) { var point = trajectory[i]; var templatePoint = template.Trajectory[i]; point.Distance.In(templatePoint.Distance.Unit).Should().BeApproximately(templatePoint.Distance.Value, templatePoint.Distance.Value * velocityAccuracyInPercent, $"@{point.Distance:N0}"); point.Velocity.In(templatePoint.Velocity.Unit).Should().BeApproximately(templatePoint.Velocity.Value, templatePoint.Velocity.Value * velocityAccuracyInPercent, $"@{point.Distance:N0}"); var dropAccuracyInInch = Measurement <AngularUnit> .Convert(dropAccuracyInMOA, AngularUnit.MOA, AngularUnit.InchesPer100Yards) * templatePoint.Distance.In(DistanceUnit.Yard) / 100; var windageAccuracyInInch = Measurement <AngularUnit> .Convert(windageAccuracyInMOA, AngularUnit.MOA, AngularUnit.InchesPer100Yards) * templatePoint.Distance.In(DistanceUnit.Yard) / 100; point.Drop.In(DistanceUnit.Inch).Should().BeApproximately(templatePoint.Drop.In(DistanceUnit.Inch), dropAccuracyInInch, $"@{point.Distance:N0}"); point.Windage.In(DistanceUnit.Inch).Should().BeApproximately(templatePoint.Windage.In(DistanceUnit.Inch), windageAccuracyInInch, $"@{point.Distance:N0}"); } }
public TableLoader(Stream table) { using var text = new StreamReader(table, Encoding.UTF8, true, 4096, true); CsvReader csv = new CsvReader(text, ";"); List <string> units = new List <string>(); while (csv.Read()) { if (csv.FieldsCount == 0) { continue; } if (csv[0] == "ammo") { if (csv.FieldsCount < 6) { Ammunition = new Ammunition( ballisticCoefficient: new BallisticCoefficient(csv[1]), weight: new Measurement <WeightUnit>(csv[2]), muzzleVelocity: new Measurement <VelocityUnit>(csv[3])); } else { Ammunition = new Ammunition( weight: new Measurement <WeightUnit>(csv[2]), ballisticCoefficient: new BallisticCoefficient(csv[1]), muzzleVelocity: new Measurement <VelocityUnit>(csv[3]), bulletDiameter: new Measurement <DistanceUnit>(csv[4]), bulletLength: new Measurement <DistanceUnit>(csv[5])); } } else if (csv[0] == "rifle") { if (csv.FieldsCount < 5) { Rifle = new Rifle( sight: new Sight(sightHeight: new Measurement <DistanceUnit>(csv[1]), Measurement <AngularUnit> .ZERO, Measurement <AngularUnit> .ZERO), zero: new ZeroingParameters(distance: new Measurement <DistanceUnit>(csv[2]), null, null), rifling: null); } else { Rifle = new Rifle( sight: new Sight(sightHeight: new Measurement <DistanceUnit>(csv[1]), Measurement <AngularUnit> .ZERO, Measurement <AngularUnit> .ZERO), zero: new ZeroingParameters(distance: new Measurement <DistanceUnit>(csv[2]), null, null), rifling: new Rifling(new Measurement <DistanceUnit>(csv[3]), csv[4] == "left" ? TwistDirection.Left : TwistDirection.Right)); } } else if (csv[0] == "wind") { Wind = new Wind(new Measurement <VelocityUnit>(csv[1]), new Measurement <AngularUnit>(csv[2])); } else if (csv[0] == "atmosphere") { Atmosphere = new Atmosphere( altitude: new Measurement <DistanceUnit>(csv[4]), pressure: new Measurement <PressureUnit>(csv[3]), temperature: new Measurement <TemperatureUnit>(csv[1]), humidity: double.Parse(csv[2], CultureInfo.InvariantCulture) / 100.0); } else if (csv[0] == "shot") { ShotParameters = new ShotParameters() { ShotAngle = new Measurement <AngularUnit>(csv[1]), CantAngle = new Measurement <AngularUnit>(csv[2]), }; } else { if (char.IsDigit(csv[0][0])) { if (units.Count == 0) { throw new InvalidOperationException("The header with the unit names for the table is not found"); } var distance = new Measurement <DistanceUnit>(csv[0] + units[0]); var drop = new Measurement <DistanceUnit>(csv[1] + units[1]); var windage = new Measurement <DistanceUnit>(csv[3] + units[3]); var velocity = new Measurement <VelocityUnit>(csv[5] + units[5]); var mach = double.Parse(csv[6], CultureInfo.InvariantCulture); var energy = new Measurement <EnergyUnit>(csv[7] + units[7]); var time = double.Parse(csv[8], CultureInfo.InvariantCulture); mTrajectory.Add(new TrajectoryPoint( time: TimeSpan.FromSeconds(time), distance: distance, velocity: velocity, mach: mach, drop: drop, windage: windage, energy: energy, optimalGameWeight: Measurement <WeightUnit> .ZERO )); } else { for (int i = 0; i < csv.FieldsCount; i++) { units.Add(csv[i]); } } } } }
internal static TrajectoryPoint[] M193(bool hasWind, Measurement <DistanceUnit> step) { //define M193 projectile out of 20 inch barrel var ammo = new Ammunition( weight: new Measurement <WeightUnit>(55, WeightUnit.Grain), ballisticCoefficient: new BallisticCoefficient(0.202, DragTableId.G1), muzzleVelocity: new Measurement <VelocityUnit>(3240, VelocityUnit.FeetPerSecond), bulletDiameter: new Measurement <DistanceUnit>(0.224, DistanceUnit.Inch), bulletLength: new Measurement <DistanceUnit>(0.76, DistanceUnit.Inch)); //define ACOG scope var sight = new Sight( sightHeight: new Measurement <DistanceUnit>(3.5, DistanceUnit.Inch), verticalClick: new Measurement <AngularUnit>(1.0 / 3.0, AngularUnit.InchesPer100Yards), horizontalClick: new Measurement <AngularUnit>(1.0 / 3.0, AngularUnit.InchesPer100Yards) ); //M16 rifling var rifling = new Rifling( riflingStep: new Measurement <DistanceUnit>(12, DistanceUnit.Inch), direction: TwistDirection.Right); //standard 100 yard ACOG zeroing var zero = new ZeroingParameters( distance: new Measurement <DistanceUnit>(100, DistanceUnit.Yard), ammunition: null, atmosphere: null ); //define rifle by sight, zeroing and rifling parameters var rifle = new Rifle(sight: sight, zero: zero, rifling: rifling); //define atmosphere var atmosphere = new Atmosphere( altitude: new Measurement <DistanceUnit>(0, DistanceUnit.Foot), pressure: new Measurement <PressureUnit>(29.92, PressureUnit.InchesOfMercury), pressureAtSeaLevel: false, temperature: new Measurement <TemperatureUnit>(59, TemperatureUnit.Fahrenheit), humidity: 0.78); var calc = new TrajectoryCalculator(); //shot parameters var shot = new ShotParameters() { MaximumDistance = new Measurement <DistanceUnit>(1000, DistanceUnit.Yard), Step = step, //calculate sight angle for the specified zero distance SightAngle = calc.SightAngle(ammo, rifle, atmosphere) }; //define winds Wind[] wind = hasWind ? new Wind[2] { new Wind() { Direction = new Measurement <AngularUnit>(45, AngularUnit.Degree), Velocity = new Measurement <VelocityUnit>(10, VelocityUnit.MilesPerHour), MaximumRange = new Measurement <DistanceUnit>(500, DistanceUnit.Yard), }, new Wind() { Direction = new Measurement <AngularUnit>(15, AngularUnit.Degree), Velocity = new Measurement <VelocityUnit>(5, VelocityUnit.MilesPerHour), } } : null; //calculate trajectory return(calc.Calculate(ammo, rifle, atmosphere, shot, wind)); }