26#include <simgear/structure/exception.hxx>
27#include <simgear/props/props_io.hxx>
37#include "simgear/debug/debug_types.h"
45#include <FDM/SP/AISim.hpp>
79 _wind_north = _props->getNode(
"environment/wind-from-north-fps",
true);
80 _wind_east = _props->getNode(
"environment/wind-from-east-fps",
true);
81 _wind_down = _props->getNode(
"environment/wind-from-down-fps",
true);
82 _control_fdm_atmo = _props->getNode(
"environment/params/control-fdm-atmosphere",
true);
83 _temp_degc = _props->getNode(
"environment/temperature-degc",
true);
84 _pressure_inhg = _props->getNode(
"environment/pressure-inhg",
true);
85 _density_slugft = _props->getNode(
"environment/density-slugft3",
true);
86 _data_logging = _props->getNode(
"/sim/temp/fdm-data-logging",
true);
87 _replay_master = _props->getNode(
"/sim/freeze/replay-state",
true);
90 _max_radius_nm = _props->getNode(
"fdm/ai-wake/max-radius-nm",
true);
91 _ai_wake_enabled = _props->getNode(
"fdm/ai-wake/enabled",
true);
93 _nanCheckFailed =
false;
95 _lastValidPos = SGGeod::invalid();
97 createImplementation();
102 _initialFdmProperties =
new SGPropertyNode;
104 if (!copyProperties(_props->getNode(
"fdm",
true),
105 _initialFdmProperties))
107 SG_LOG(SG_FLIGHT, SG_ALERT,
"Failed to save initial FDM property state");
112 SG_LOG(SG_FLIGHT, SG_DEV_ALERT,
"FDM connection to the AI manager: FAILED");
119 fgSetBool(
"/sim/fdm-initialized",
false);
128 _control_fdm_atmo.clear();
130 _pressure_inhg.clear();
131 _density_slugft .clear();
132 _data_logging.clear();
133 _replay_master.clear();
140 if ( copyProperties(_initialFdmProperties,
fgGetNode(
"/fdm",
true)) ) {
141 SG_LOG( SG_FLIGHT, SG_INFO,
"Preserved state restored successfully" );
143 SG_LOG( SG_FLIGHT, SG_WARN,
144 "FDM: Some errors restoring preserved state" );
153 _tankProperties.bind();
154 if (_impl && _impl->get_inited()) {
155 if (_impl->get_bound()) {
156 throw sg_exception(
"FDMShell::bind of bound FGInterface impl");
164 if( _impl ) _impl->unbind();
165 _tankProperties.unbind();
168void FDMShell::doInitAndBind()
170 SG_LOG(SG_FLIGHT, SG_INFO,
"Scenery loaded, will init FDM");
173 if (_impl->get_bound()) {
179 fgSetBool(
"/sim/signals/fdm-initialized",
true);
180 }
catch (std::exception& e) {
182 string{
"The aircraft flight dynamics model contains errors and cannot be used. ("} + e.what() +
")");
185 if (!copyProperties(_props->getNode(
"fdm",
true),
186 _initialFdmProperties)) {
187 SG_LOG(SG_FLIGHT, SG_ALERT,
"Failed to save initial FDM property state");
197 if (_nanCheckFailed) {
201 if (!_impl->get_inited()) {
203 double lon =
fgGetDouble(
"/sim/presets/longitude-deg");
204 double lat =
fgGetDouble(
"/sim/presets/latitude-deg");
206 double range = 1000.0;
207 SGGeod geod = SGGeod::fromDeg(lon, lat);
208 const auto startUpPositionFialized =
fgGetBool(
"/sim/position-finalized",
false);
209 if (startUpPositionFialized &&
globals->get_scenery()->scenery_available(geod, range)) {
214 if (!_impl->get_inited()) {
219 if (_ai_wake_enabled->getBoolValue()) {
220 for (
FGAIBase* base : _ai_mgr->get_ai_list()) {
223 SGVec3d pos = _impl->getCartPosition();
224 const SGSharedPtr<FGAIAircraft> aircraft =
dynamic_cast<FGAIAircraft*
>(base);
225 double range = _ai_mgr->calcRangeFt(pos, aircraft)*SG_FEET_TO_METER;
227 if (!aircraft->onGround() && aircraft->getSpeed() > 0.0
228 && range < _max_radius_nm->getDoubleValue()*SG_NM_TO_METER) {
229 _impl->add_ai_wake(aircraft);
232 }
catch (sg_exception& e) {
233 SG_LOG(SG_FLIGHT, SG_WARN,
"caught exception updating AI model:"
234 << base->_getName()<<
", which will be killed."
235 "\n\tError:" << e.getFormattedMessage());
242 _impl->set_Velocities_Local_Airmass(
243 _wind_north->getDoubleValue(),
244 _wind_east->getDoubleValue(),
245 _wind_down->getDoubleValue());
247 if (_control_fdm_atmo->getBoolValue()) {
249 double tempDegC = _temp_degc->getDoubleValue();
250 _impl->set_Static_temperature((9.0/5.0) * (tempDegC + 273.15));
253 double pressureInHg = _pressure_inhg->getDoubleValue();
254 _impl->set_Static_pressure(pressureInHg * 70.726566);
256 _impl->set_Density(_density_slugft->getDoubleValue());
259 bool doLog = _data_logging->getBoolValue();
260 if (doLog != _dataLogging) {
261 _dataLogging = doLog;
262 _impl->ToggleDataLogging(doLog);
265 switch(_replay_master->getIntValue())
280 validateOutputProperties();
288void FDMShell::createImplementation()
292 double dt = 1.0 /
fgGetInt(
"/sim/model-hz");
295 bool fdmUnavailable =
false;
297 if ( model ==
"ufo" ) {
298 _impl =
new FGUFO( dt );
299 }
else if ( model ==
"external" ) {
302 _impl =
new FGNullFDM( dt );
303 }
else if ( model.find(
"network") == 0 ) {
304 string host =
"localhost";
308 string net_options = model.substr(8);
309 string::size_type begin, end;
312 end = net_options.find(
",", begin );
313 if ( end != string::npos ) {
314 host = net_options.substr(begin, end - begin);
318 end = net_options.find(
",", begin );
319 if ( end != string::npos ) {
320 port1 =
atoi( net_options.substr(begin, end - begin).c_str() );
324 end = net_options.find(
",", begin );
325 if ( end != string::npos ) {
326 port2 =
atoi( net_options.substr(begin, end - begin).c_str() );
330 end = net_options.find(
",", begin );
331 if ( end != string::npos ) {
332 port3 =
atoi( net_options.substr(begin, end - begin).c_str() );
335 _impl =
new FGExternalNet( dt, host, port1, port2, port3 );
336 }
else if ( model.find(
"pipe") == 0 ) {
339 string pipe_path =
"";
340 string pipe_protocol =
"";
341 string pipe_options = model.substr(5);
342 string::size_type begin, end;
345 end = pipe_options.find(
",", begin );
346 if ( end != string::npos ) {
347 pipe_path = pipe_options.substr(begin, end - begin);
351 pipe_protocol = pipe_options.substr(begin);
352 _impl =
new FGExternalPipe( dt, pipe_path, pipe_protocol );
353 }
else if ( model ==
"null" ) {
354 _impl =
new FGNullFDM( dt );
356 else if ( model ==
"larcsim" ) {
357 throw sg_exception(
string(
"LaRCsim/UIUC support is removed from this version of FlightGear.\n"
358 "If you still need it, please use the 2024.1 release instead."));
360 else if ( model ==
"jsb" ) {
362 _impl =
new FGJSBsim( dt );
364 fdmUnavailable =
true;
368 else if ( model ==
"ada" ) {
369 _impl =
new FGADA( dt );
370 }
else if ( model ==
"acms" ) {
371 _impl =
new FGACMS( dt );
372 }
else if ( model ==
"balloon" ) {
373 _impl =
new FGBalloonSim( dt );
374 }
else if ( model ==
"magic" ) {
375 _impl =
new FGMagicCarpet( dt );
376 }
else if ( model ==
"aisim" ) {
377 _impl =
new FGAISim( dt );
380 else if (( model ==
"ada" )||(model ==
"acms")||( model ==
"balloon" )||( model ==
"magic" )||( model ==
"aisim" ))
382 fdmUnavailable =
true;
385 else if ( model ==
"yasim" ) {
387 _impl =
new YASim( dt );
389 fdmUnavailable =
true;
392 throw sg_exception(
string(
"Unrecognized flight model '") + model
393 +
"', cannot init flight dynamics model.");
399 throw sg_exception(
string(
"Support for flight model '") + model
400 + (
"' is not available with this binary (deprecated/disabled).\n"
401 "If you still need it, please rebuild FlightGear and enable its support."));
405void FDMShell::validateOutputProperties()
411 if (SGMisc<double>::isNaN(
p.getLatitudeDeg()) ||
412 SGMisc<double>::isNaN(
p.getLongitudeDeg()) ||
413 SGMisc<double>::isNaN(
p.getElevationFt())) {
414 SG_LOG(SG_FLIGHT, SG_ALERT,
"FDM position became invalid. Last valid position was:" << _lastValidPos);
415 _nanCheckFailed =
true;
420 if (SGMisc<double>::isNaN(_impl->get_V_true_kts())) {
421 SG_LOG(SG_FLIGHT, SG_ALERT,
"FDM velocity became invalid");
422 _nanCheckFailed =
true;
425 if (_nanCheckFailed) {
429 "The flight dynamics model (FDM) has become invalid. The simulation will be stopped, so you can restart at a new location.");
438 SGSubsystemMgr::FDM);
void update(double dt) override
FGInterface * getInterface() const
SGGeod get_aircraft_position() const
SGSubsystemMgr::Registrant< FDMShell > registrantFDMShell(SGSubsystemMgr::FDM)
int fgGetInt(const char *name, int defaultValue)
Get an int value for a property.
std::string fgGetString(const char *name, const char *defaultValue)
Get a string value for a property.
MessageBoxResult modalMessageBox(const std::string &caption, const std::string &msg, const std::string &moreText)
void sentryReportException(const std::string &, const std::string &)
void fatalMessageBoxThenExit(const std::string &caption, const std::string &msg, const std::string &moreText, int exitStatus, bool reportToSentry)
static int atoi(const string &str)
bool fgGetBool(char const *name, bool def)
Get a bool value for a property.
bool fgSetBool(char const *name, bool val)
Set a bool value for a property.
double fgGetDouble(const char *name, double defaultValue)
Get a double value for a property.
SGPropertyNode * fgGetNode(const char *path, bool create)
Get a property node.