1 // FGAIBallistic - FGAIBase-derived class creates a ballistic object
3 // Written by David Culp, started November 2003.
4 // - davidculp2@comcast.net
6 // With major additions by Mathias Froehlich & Vivian Meazza 2004-2007
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License as
10 // published by the Free Software Foundation; either version 2 of the
11 // License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful, but
14 // WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // General Public License for more details.
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the Free Software
20 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26 #include <simgear/math/point3d.hxx>
27 #include <simgear/math/sg_random.h>
28 #include <simgear/scene/material/mat.hxx>
32 #include <Scenery/scenery.hxx>
34 #include "AIBallistic.hxx"
38 const double FGAIBallistic::slugs_to_kgs = 14.5939029372;
40 FGAIBallistic::FGAIBallistic() :
41 FGAIBase(otBallistic),
42 _aero_stabilised(false),
50 _impact_reported(false),
51 _impact_report_node(fgGetNode("/ai/models/model-impact", true)),
57 FGAIBallistic::~FGAIBallistic() {
60 void FGAIBallistic::readFromScenario(SGPropertyNode* scFileNode) {
64 FGAIBase::readFromScenario(scFileNode);
66 setAzimuth(scFileNode->getDoubleValue("azimuth", 0.0));
67 setElevation(scFileNode->getDoubleValue("elevation", 0.0));
68 setDragArea(scFileNode->getDoubleValue("eda", 0.007));
69 setLife(scFileNode->getDoubleValue("life", 900.0));
70 setBuoyancy(scFileNode->getDoubleValue("buoyancy", 0));
71 setWind_from_east(scFileNode->getDoubleValue("wind_from_east", 0));
72 setWind_from_north(scFileNode->getDoubleValue("wind_from_north", 0));
73 setWind(scFileNode->getBoolValue("wind", false));
74 setRoll(scFileNode->getDoubleValue("roll", 0.0));
75 setCd(scFileNode->getDoubleValue("cd", 0.029));
76 setMass(scFileNode->getDoubleValue("mass", 0.007));
77 setStabilisation(scFileNode->getBoolValue("aero_stabilized", false));
78 setNoRoll(scFileNode->getBoolValue("no-roll", false));
79 setRandom(scFileNode->getBoolValue("random", false));
80 setImpact(scFileNode->getBoolValue("impact", false));
81 setImpactReportNode(scFileNode->getStringValue("impact-reports"));
82 setName(scFileNode->getStringValue("name", "Bomb"));
85 bool FGAIBallistic::init(bool search_in_AI_path) {
86 FGAIBase::init(search_in_AI_path);
88 props->setStringValue("material/name", _mat_name.c_str());
89 props->setStringValue("name", _name.c_str());
91 // start with high value so that animations don't trigger yet
92 _ht_agl_ft = 10000000;
100 void FGAIBallistic::bind() {
102 props->tie("sim/time/elapsed-sec",
103 SGRawValueMethods<FGAIBallistic,double>(*this,
104 &FGAIBallistic::_getTime));
105 props->tie("material/load-resistance",
106 SGRawValuePointer<double>(&_load_resistance));
107 props->tie("material/solid",
108 SGRawValuePointer<bool>(&_solid));
109 props->tie("altitude-agl-ft",
110 SGRawValuePointer<double>(&_ht_agl_ft));
113 void FGAIBallistic::unbind() {
114 // FGAIBase::unbind();
115 props->untie("sim/time/elapsed-sec");
116 props->untie("material/load-resistance");
117 props->untie("material/solid");
118 props->untie("altitude-agl-ft");
121 void FGAIBallistic::update(double dt) {
122 FGAIBase::update(dt);
127 void FGAIBallistic::setAzimuth(double az) {
131 void FGAIBallistic::setElevation(double el) {
132 pitch = _elevation = el;
135 void FGAIBallistic::setRoll(double rl) {
139 void FGAIBallistic::setStabilisation(bool val) {
140 _aero_stabilised = val;
143 void FGAIBallistic::setNoRoll(bool nr) {
147 void FGAIBallistic::setDragArea(double a) {
151 void FGAIBallistic::setLife(double seconds) {
155 void FGAIBallistic::setBuoyancy(double fpss) {
159 void FGAIBallistic::setWind_from_east(double fps) {
160 _wind_from_east = fps;
163 void FGAIBallistic::setWind_from_north(double fps) {
164 _wind_from_north = fps;
167 void FGAIBallistic::setWind(bool val) {
171 void FGAIBallistic::setCd(double c) {
175 void FGAIBallistic::setMass(double m) {
179 void FGAIBallistic::setRandom(bool r) {
183 void FGAIBallistic::setImpact(bool i) {
187 void FGAIBallistic::setImpactReportNode(const string& path) {
189 _impact_report_node = fgGetNode(path.c_str(), true);
192 void FGAIBallistic::setName(const string& n) {
196 void FGAIBallistic::Run(double dt) {
198 // cout << "life timer 1" << _life_timer << dt << endl;
199 if (_life_timer > life)
202 double speed_north_deg_sec;
203 double speed_east_deg_sec;
204 double wind_speed_from_north_deg_sec;
205 double wind_speed_from_east_deg_sec;
206 double Cdm; // Cd adjusted by Mach Number
208 //randomise Cd by +- 5%
210 _Cd = _Cd * 0.95 + (0.05 * sg_random());
212 // Adjust Cd by Mach number. The equations are based on curves
213 // for a conventional shell/bullet (no boat-tail).
215 Cdm = 0.0125 * Mach + _Cd;
216 else if (Mach < 1.2 )
217 Cdm = 0.3742 * pow(Mach, 2) - 0.252 * Mach + 0.0021 + _Cd;
219 Cdm = 0.2965 * pow(Mach, -1.1506) + _Cd;
221 //cout << " Mach , " << Mach << " , Cdm , " << Cdm << " ballistic speed kts //"<< speed << endl;
223 // drag = Cd * 0.5 * rho * speed * speed * drag_area;
224 // rho is adjusted for altitude in void FGAIBase::update,
225 // using Standard Atmosphere (sealevel temperature 15C)
226 // acceleration = drag/mass;
227 // adjust speed by drag
228 speed -= (Cdm * 0.5 * rho * speed * speed * _drag_area/_mass) * dt;
230 // don't let speed become negative
234 double speed_fps = speed * SG_KT_TO_FPS;
236 // calculate vertical and horizontal speed components
237 vs = sin( pitch * SG_DEGREES_TO_RADIANS ) * speed_fps;
238 double hs = cos( pitch * SG_DEGREES_TO_RADIANS ) * speed_fps;
240 // convert horizontal speed (fps) to degrees per second
241 speed_north_deg_sec = cos(hdg / SG_RADIANS_TO_DEGREES) * hs / ft_per_deg_lat;
242 speed_east_deg_sec = sin(hdg / SG_RADIANS_TO_DEGREES) * hs / ft_per_deg_lon;
244 // if wind not required, set to zero
246 _wind_from_north = 0;
250 // convert wind speed (fps) to degrees per second
251 wind_speed_from_north_deg_sec = _wind_from_north / ft_per_deg_lat;
252 wind_speed_from_east_deg_sec = _wind_from_east / ft_per_deg_lon;
255 pos.setLatitudeDeg( pos.getLatitudeDeg()
256 + (speed_north_deg_sec - wind_speed_from_north_deg_sec) * dt );
257 pos.setLongitudeDeg( pos.getLongitudeDeg()
258 + (speed_east_deg_sec - wind_speed_from_east_deg_sec) * dt );
260 // adjust vertical speed for acceleration of gravity and buoyancy
261 vs -= (_gravity - _buoyancy) * dt;
263 // adjust altitude (feet)
264 altitude_ft += vs * dt;
265 pos.setElevationFt(altitude_ft);
267 // recalculate pitch (velocity vector) if aerostabilized
268 /*cout << _name << ": " << "aero_stabilised " << _aero_stabilised
269 << " pitch " << pitch <<" vs " << vs <<endl ;*/
271 if (_aero_stabilised)
272 pitch = atan2( vs, hs ) * SG_RADIANS_TO_DEGREES;
274 // recalculate total speed
275 speed = sqrt( vs * vs + hs * hs) / SG_KT_TO_FPS;
277 if (_report_impact && !_impact_reported)
280 // set destruction flag if altitude less than sea level -1000
281 if (altitude_ft < -1000.0)
286 double FGAIBallistic::_getTime() const {
287 // cout << "life timer 2" << _life_timer << endl;
291 void FGAIBallistic::handle_impact() {
293 const SGMaterial* material;
295 // try terrain intersection
296 if (!globals->get_scenery()->get_elevation_m(pos.getLatitudeDeg(), pos.getLongitudeDeg(),
297 10000.0, elevation_m, &material))
301 const vector<string> names = material->get_names();
304 _mat_name = names[0].c_str();
306 _solid = material->get_solid();
307 _load_resistance = material->get_load_resistance();
308 props->setStringValue("material/name", _mat_name.c_str());
309 //cout << "material " << _mat_name << " solid " << _solid << " load " << _load_resistance << endl;
312 _ht_agl_ft = pos.getElevationFt() - elevation_m * SG_METER_TO_FEET;
314 // report impact by setting property-tied variables
315 if (_ht_agl_ft <= 0) {
316 _impact_reported = true;
317 double speed_mps = speed * SG_KT_TO_MPS;
319 SGPropertyNode *n = props->getNode("impact", true);
320 n->setDoubleValue("longitude-deg", pos.getLongitudeDeg());
321 n->setDoubleValue("latitude-deg", pos.getLatitudeDeg());
322 n->setDoubleValue("elevation-m", elevation_m);
323 n->setDoubleValue("heading-deg", hdg);
324 n->setDoubleValue("pitch-deg", pitch);
325 n->setDoubleValue("roll-deg", roll);
326 n->setDoubleValue("speed-mps", speed_mps);
327 n->setDoubleValue("energy-kJ", (_mass * slugs_to_kgs)
328 * speed_mps * speed_mps / (2 * 1000));
330 _impact_report_node->setStringValue(props->getPath());