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