ballistics_engine/
form_factor.rs

1/// Form factor calculations for drag enhancement
2use crate::DragModel;
3
4/// Get default form factor based on bullet type
5pub fn get_default_form_factor(bullet_name: &str, drag_model: &DragModel) -> f64 {
6    let name_upper = bullet_name.to_uppercase();
7
8    match drag_model {
9        DragModel::G1 => {
10            if name_upper.contains("MATCH")
11                || name_upper.contains("SMK")
12                || name_upper.contains("SCENAR")
13                || name_upper.contains("BERGER")
14            {
15                0.90 // Match bullets
16            } else if name_upper.contains("VLD")
17                || name_upper.contains("HYBRID")
18                || name_upper.contains("ELD")
19            {
20                0.85 // Very low drag bullets
21            } else if name_upper.contains("FMJ") {
22                1.10 // Full metal jacket
23            } else if name_upper.contains("HUNT") || name_upper.contains("SP") {
24                1.05 // Hunting bullets
25            } else {
26                1.00 // Default
27            }
28        }
29        DragModel::G7 => {
30            if name_upper.contains("MATCH")
31                || name_upper.contains("SMK")
32                || name_upper.contains("SCENAR")
33            {
34                0.95 // Match bullets
35            } else if name_upper.contains("VLD") || name_upper.contains("HYBRID") {
36                0.90 // Very low drag bullets
37            } else if name_upper.contains("FMJ") {
38                1.15 // Full metal jacket (less efficient for G7)
39            } else {
40                1.05 // Default G7
41            }
42        }
43        _ => 1.00, // Default form factor for other models
44    }
45}
46
47/// Apply form factor to drag coefficient
48pub fn apply_form_factor_to_drag(
49    cd_base: f64,
50    bullet_name: Option<&str>,
51    drag_model: &DragModel,
52    use_form_factor: bool,
53) -> f64 {
54    if !use_form_factor {
55        return cd_base;
56    }
57
58    // Get form factor based on bullet type
59    let form_factor = if let Some(name) = bullet_name {
60        get_default_form_factor(name, drag_model)
61    } else {
62        1.0 // No correction if no bullet name
63    };
64
65    // Apply form factor to drag coefficient
66    cd_base * form_factor
67}
68
69#[cfg(test)]
70mod tests {
71    use super::*;
72
73    #[test]
74    fn test_g1_form_factors() {
75        let drag_model = DragModel::G1;
76
77        // Match bullets should have lower form factor
78        assert_eq!(get_default_form_factor("168gr SMK", &drag_model), 0.90);
79        assert_eq!(get_default_form_factor("Berger Match", &drag_model), 0.90);
80        assert_eq!(get_default_form_factor("Lapua Scenar", &drag_model), 0.90);
81
82        // VLD bullets should have even lower
83        assert_eq!(get_default_form_factor("VLD Target", &drag_model), 0.85);
84        assert_eq!(get_default_form_factor("ELD-X", &drag_model), 0.85); // ELD without "Match"
85        assert_eq!(get_default_form_factor("Hybrid OTM", &drag_model), 0.85); // Hybrid without "Match"
86
87        // FMJ should have higher form factor
88        assert_eq!(get_default_form_factor("M855 FMJ", &drag_model), 1.10);
89
90        // Hunting bullets
91        assert_eq!(get_default_form_factor("Hunting SP", &drag_model), 1.05);
92
93        // Default
94        assert_eq!(get_default_form_factor("Generic Bullet", &drag_model), 1.00);
95    }
96
97    #[test]
98    fn test_g7_form_factors() {
99        let drag_model = DragModel::G7;
100
101        // Match bullets
102        assert_eq!(get_default_form_factor("175gr SMK", &drag_model), 0.95);
103
104        // VLD bullets
105        assert_eq!(get_default_form_factor("VLD Hunting", &drag_model), 0.90);
106
107        // FMJ less efficient with G7
108        assert_eq!(get_default_form_factor("147gr FMJ", &drag_model), 1.15);
109
110        // Default G7
111        assert_eq!(get_default_form_factor("Generic", &drag_model), 1.05);
112    }
113
114    #[test]
115    fn test_apply_form_factor() {
116        let cd_base = 0.5;
117        let drag_model = DragModel::G1;
118
119        // With form factor enabled
120        let cd_with_ff = apply_form_factor_to_drag(cd_base, Some("168gr SMK"), &drag_model, true);
121        assert_eq!(cd_with_ff, 0.5 * 0.90); // 0.45
122
123        // With form factor disabled
124        let cd_without_ff =
125            apply_form_factor_to_drag(cd_base, Some("168gr SMK"), &drag_model, false);
126        assert_eq!(cd_without_ff, 0.5);
127
128        // No bullet name
129        let cd_no_name = apply_form_factor_to_drag(cd_base, None, &drag_model, true);
130        assert_eq!(cd_no_name, 0.5); // No correction
131    }
132
133    #[test]
134    fn test_case_insensitive() {
135        let drag_model = DragModel::G1;
136
137        // Should work with various cases
138        assert_eq!(get_default_form_factor("smk", &drag_model), 0.90);
139        assert_eq!(get_default_form_factor("SMK", &drag_model), 0.90);
140        assert_eq!(get_default_form_factor("SmK", &drag_model), 0.90);
141    }
142}