13#include <simgear/math/sg_geodesy.hxx>
14#include <simgear/misc/sg_path.hxx>
15#include <simgear/props/props_io.hxx>
16#include <simgear/structure/exception.hxx>
33const double FGSubmodelMgr::lbs_to_slugs = 0.031080950172;
37 x_offset = y_offset = z_offset = 0.0;
43 contrail_altitude = 30000;
57 _serviceable_node =
fgGetNode(
"/sim/submodels/serviceable",
true);
58 _serviceable_node->setBoolValue(
true);
60 _user_lat_node =
fgGetNode(
"/position/latitude-deg",
true);
61 _user_lon_node =
fgGetNode(
"/position/longitude-deg",
true);
62 _user_alt_node =
fgGetNode(
"/position/altitude-ft",
true);
64 _user_heading_node =
fgGetNode(
"/orientation/heading-deg",
true);
65 _user_pitch_node =
fgGetNode(
"/orientation/pitch-deg",
true);
66 _user_roll_node =
fgGetNode(
"/orientation/roll-deg",
true);
67 _user_yaw_node =
fgGetNode(
"/orientation/yaw-deg",
true);
68 _user_alpha_node =
fgGetNode(
"/orientation/alpha-deg",
true);
70 _user_speed_node =
fgGetNode(
"/velocities/uBody-fps",
true);
72 _user_wind_from_east_node =
fgGetNode(
"/environment/wind-from-east-fps",
true);
73 _user_wind_from_north_node =
fgGetNode(
"/environment/wind-from-north-fps",
true);
75 _user_speed_down_fps_node =
fgGetNode(
"/velocities/speed-down-fps",
true);
76 _user_speed_east_fps_node =
fgGetNode(
"/velocities/speed-east-fps",
true);
77 _user_speed_north_fps_node =
fgGetNode(
"/velocities/speed-north-fps",
true);
79 _contrail_altitude_node =
fgGetNode(
"/environment/params/contrail-altitude",
true);
80 contrail_altitude = _contrail_altitude_node->getDoubleValue();
81 _contrail_trigger =
fgGetNode(
"ai/submodels/contrails",
true);
82 _contrail_trigger->setBoolValue(
false);
103 std::for_each(submodels.begin(), submodels.end(), [](
submodel* sm) { delete sm; });
113 submodel_iterator = submodels.begin();
114 while (submodel_iterator != submodels.end()) {
115 (*submodel_iterator)->prop->untie(
"count");
122 if (!_serviceable_node->getBoolValue())
131 FGAIManager::ai_list_iterator sm_list_itr = sm_list.begin();
132 FGAIManager::ai_list_iterator end = sm_list.end();
134 for (; sm_list_itr != end; ++sm_list_itr) {
142 int parent_subID = (*sm_list_itr)->_getSubID();
143 int id = (*sm_list_itr)->getID();
145 if (parent_subID == 0 ||
id == -1)
151 _hit = (*sm_list_itr)->_getCollisionData();
152 _impact = (*sm_list_itr)->_getImpactData();
153 _expiry = (*sm_list_itr)->_getExpiryData();
159 if (_impact || _hit || _expiry) {
163 submodel_iterator = submodels.begin();
165 while (submodel_iterator != submodels.end()) {
166 int child_ID = (*submodel_iterator)->id;
169 if (parent_subID == child_ID) {
170 _parent_lat = (*sm_list_itr)->_getImpactLat();
171 _parent_lon = (*sm_list_itr)->_getImpactLon();
172 _parent_elev = (*sm_list_itr)->_getImpactElevFt();
173 _parent_hdg = (*sm_list_itr)->_getImpactHdg();
174 _parent_pitch = (*sm_list_itr)->_getImpactPitch();
175 _parent_roll = (*sm_list_itr)->_getImpactRoll();
176 _parent_speed = (*sm_list_itr)->_getImpactSpeed();
177 (*submodel_iterator)->first_time =
true;
180 if (release(*submodel_iterator, dt)) {
181 (*sm_list_itr)->setDie(
true);
191 _contrail_trigger->setBoolValue(_user_alt_node->getDoubleValue() > contrail_altitude);
194 for (
auto sm : submodels) {
195 bool trigger =
false;
196 if (sm->trigger_node) {
197 trigger = sm->trigger_node->getBoolValue();
200 if (trigger && sm->count != 0) {
203 SG_LOG(SG_AI, SG_DEBUG,
204 "Submodels release: " << sm->id
205 <<
" name " << sm->name
206 <<
" count " << sm->count
207 <<
" slaved " << sm->slaved);
211 sm->first_time =
true;
216bool FGSubmodelMgr::release(submodel* sm,
double dt)
219 if (!sm->first_time && !sm->repeat) {
225 if (
sm->timer <
sm->delay) {
235 sm->first_time =
false;
240 FGAIBallistic* ballist =
new FGAIBallistic;
254 ballist->
setSpeed(IC.speed / SG_KT_TO_FPS);
285 aiManager()->attach(ballist);
294 SGPropertyNode_ptr path_node =
fgGetNode(
"/sim/submodels/path");
298 const string& path = path_node->getStringValue();
299 bool serviceable = _serviceable_node->getBoolValue();
300 setData(
id, path, serviceable,
"/ai/submodels/submodel", submodels);
304void FGSubmodelMgr::transform(submodel* sm)
307 if (sm->contents_node != 0 && !sm->slaved) {
309 sm->contents = sm->contents_node->getChild(
"level-lbs", 0, 1)->getDoubleValue();
311 IC.
mass = (sm->weight + sm->contents) * lbs_to_slugs;
315 sm->contents_node->getChild(
"level-gal_us", 0, 1)->setDoubleValue(0);
320 IC.mass =
sm->weight * lbs_to_slugs;
327 if (
sm->speed_node != 0)
328 sm->speed =
sm->speed_node->getDoubleValue();
332 if (_impact || _hit || _expiry) {
336 IC.lat = _parent_lat;
337 IC.lon = _parent_lon;
338 IC.alt = _parent_elev;
339 IC.roll = _parent_roll;
340 IC.elevation = _parent_pitch;
341 IC.azimuth = _parent_hdg;
342 IC.speed = _parent_speed;
343 IC.speed_down_fps = 0;
344 IC.speed_east_fps = 0;
345 IC.speed_north_fps = 0;
346 }
else if (
id == 0) {
348 IC.lat = _user_lat_node->getDoubleValue();
349 IC.lon = _user_lon_node->getDoubleValue();
350 IC.alt = _user_alt_node->getDoubleValue();
351 IC.roll = _user_roll_node->getDoubleValue();
352 IC.elevation = _user_pitch_node->getDoubleValue();
353 IC.azimuth = _user_heading_node->getDoubleValue();
354 IC.speed = _user_speed_node->getDoubleValue();
355 IC.speed_down_fps = _user_speed_down_fps_node->getDoubleValue();
356 IC.speed_east_fps = _user_speed_east_fps_node->getDoubleValue();
357 IC.speed_north_fps = _user_speed_north_fps_node->getDoubleValue();
364 IC.wind_from_east = _user_wind_from_east_node->getDoubleValue();
365 IC.wind_from_north = _user_wind_from_north_node->getDoubleValue();
370 IC.speed_east_fps += IC.wind_from_east;
371 IC.speed_north_fps += IC.wind_from_north;
374 userpos.setLatitudeDeg(IC.lat);
375 userpos.setLongitudeDeg(IC.lon);
376 userpos.setElevationFt(IC.alt);
378 if (
sm->offsets_in_meter) {
379 _x_offset = -
sm->x_offset->get_value() * SG_METER_TO_FEET;
380 _y_offset =
sm->y_offset->get_value() * SG_METER_TO_FEET;
381 _z_offset =
sm->z_offset->get_value() * SG_METER_TO_FEET;
383 _x_offset =
sm->x_offset->get_value();
384 _y_offset =
sm->y_offset->get_value();
385 _z_offset =
sm->z_offset->get_value();
392 const double l_yaw_offset =
sm->yaw_offset->get_value();
393 const double l_pitch_offset =
sm->pitch_offset->get_value();
395 SGQuatd ic_quat = SGQuatd::fromYawPitchRollDeg(IC.azimuth, IC.elevation, IC.roll);
396 ic_quat *= SGQuatd::fromYawPitchRollDeg(l_yaw_offset, l_pitch_offset, 0.0);
399 SGVec3d total_speed = SGVec3d(IC.speed_north_fps, IC.speed_east_fps, IC.speed_down_fps);
400 total_speed += ic_quat.rotate(SGVec3d(
sm->speed, 0, 0));
402 IC.speed = length(total_speed);
406 const double total_speed_north = total_speed.x();
407 const double total_speed_east = total_speed.y();
408 const double total_speed_down = total_speed.z();
410 IC.azimuth = atan2(total_speed_east, total_speed_north) * SG_RADIANS_TO_DEGREES;
415 else if (IC.azimuth >= 360)
418 IC.elevation = -atan(total_speed_down / sqrt(total_speed_north * total_speed_north + total_speed_east * total_speed_east)) * SG_RADIANS_TO_DEGREES;
421 ic_quat.getEulerDeg(IC.azimuth, IC.elevation, ic_roll);
425void FGSubmodelMgr::loadAI()
427 SG_LOG(SG_AI, SG_DEBUG,
"Submodels: Loading AI submodels");
431 if (sm_list.empty()) {
432 SG_LOG(SG_AI, SG_ALERT,
"Submodels: Unable to read AI submodel list");
436 FGAIManager::ai_list_iterator sm_list_itr = sm_list.begin();
437 FGAIManager::ai_list_iterator end = sm_list.end();
439 while (sm_list_itr != end) {
440 string path = (*sm_list_itr)->_getSMPath();
447 int id = (*sm_list_itr)->getID();
448 bool serviceable = (*sm_list_itr)->_getServiceable();
453 setData(
id, path, serviceable,
"/ai/submodels/submodel", submodels);
458void FGSubmodelMgr::setData(
int id,
const string& path,
bool serviceable,
const string& property_path, submodel_vector_type& models)
463 if (!config.exists()) {
464 SG_LOG(SG_AI, SG_DEV_ALERT,
"missing AI submodels file: " << config);
469 SG_LOG(SG_AI, SG_DEBUG,
470 "Submodels: Trying to read AI submodels file: " << config);
471 readProperties(config, &root);
472 }
catch (
const sg_exception&) {
473 SG_LOG(SG_AI, SG_ALERT,
474 "Submodels: Unable to read AI submodels file: " << config);
478 vector<SGPropertyNode_ptr> children = root.getChildren(
"submodel");
479 vector<SGPropertyNode_ptr>::iterator it = children.begin();
480 vector<SGPropertyNode_ptr>::iterator end = children.end();
482 for (
int i = 0; it != end; ++it,
i++) {
485 SGPropertyNode_ptr entry_node = *it;
486 sm->name = entry_node->getStringValue(
"name",
"none_defined");
487 sm->model = entry_node->getStringValue(
"model",
"Models/Geometry/rocket.ac");
488 sm->speed = entry_node->getDoubleValue(
"speed", 2329.4);
489 sm->repeat = entry_node->getBoolValue(
"repeat",
false);
490 sm->delay = entry_node->getDoubleValue(
"delay", 0.25);
491 sm->count = entry_node->getIntValue(
"count", 1);
492 sm->slaved = entry_node->getBoolValue(
"slaved",
false);
493 sm->drag_area = entry_node->getDoubleValue(
"eda", 0.034);
494 sm->life = entry_node->getDoubleValue(
"life", 900.0);
495 sm->buoyancy = entry_node->getDoubleValue(
"buoyancy", 0);
496 sm->wind = entry_node->getBoolValue(
"wind",
false);
497 sm->cd = entry_node->getDoubleValue(
"cd", 0.193);
498 sm->weight = entry_node->getDoubleValue(
"weight", 0.25);
499 sm->aero_stabilised = entry_node->getBoolValue(
"aero-stabilised",
true);
500 sm->no_roll = entry_node->getBoolValue(
"no-roll",
false);
501 sm->collision = entry_node->getBoolValue(
"collision",
false);
502 sm->expiry = entry_node->getBoolValue(
"expiry",
false);
503 sm->impact = entry_node->getBoolValue(
"impact",
false);
504 sm->impact_report = entry_node->getStringValue(
"impact-reports");
505 sm->fuse_range = entry_node->getDoubleValue(
"fuse-range", 0.0);
506 sm->contents_node =
fgGetNode(entry_node->getStringValue(
"contents",
"none"),
false);
507 sm->speed_node =
fgGetNode(entry_node->getStringValue(
"speed-prop",
"none"),
false);
508 sm->submodel = entry_node->getStringValue(
"submodel-path",
"");
509 sm->force_stabilised = entry_node->getBoolValue(
"force-stabilised",
false);
510 sm->ext_force = entry_node->getBoolValue(
"external-force",
false);
511 sm->force_path = entry_node->getStringValue(
"force-path",
"");
512 sm->random = entry_node->getBoolValue(
"random",
false);
514 SGPropertyNode_ptr prop_root =
fgGetNode(
"/",
true);
516 SGPropertyNode_ptr a, b;
519 a = entry_node->getNode(
"offsets",
false);
520 sm->offsets_in_meter = (a != 0);
523 b = a->getNode(
"x-m");
524 sm->x_offset =
new simgear::Value(*prop_root, b ? *b : n);
526 b = a->getNode(
"y-m");
527 sm->y_offset =
new simgear::Value(*prop_root, b ? *b : n);
529 b = a->getNode(
"z-m");
530 sm->z_offset =
new simgear::Value(*prop_root, b ? *b : n);
532 b = a->getNode(
"heading-deg");
533 sm->yaw_offset =
new simgear::Value(*prop_root, b ? *b : n);
535 b = a->getNode(
"pitch-deg");
536 sm->pitch_offset =
new simgear::Value(*prop_root, b ? *b : n);
540 b = entry_node->getNode(
"x-offset");
541 sm->x_offset =
new simgear::Value(*prop_root, b ? *b : n);
544 b = entry_node->getNode(
"y-offset");
545 sm->y_offset =
new simgear::Value(*prop_root, b ? *b : n);
548 b = entry_node->getNode(
"z-offset");
549 sm->z_offset =
new simgear::Value(*prop_root, b ? *b : n);
552 b = entry_node->getNode(
"yaw-offset");
553 sm->yaw_offset =
new simgear::Value(*prop_root, b ? *b : n);
556 b = entry_node->getNode(
"pitch-offset");
557 sm->pitch_offset =
new simgear::Value(*prop_root, b ? *b : n);
561 SG_LOG(SG_AI, SG_DEV_WARN,
"Submodels: <*-offset> is deprecated. Use <offsets> instead");
566 a = entry_node->getNode(
"randomness",
true);
569 b = a->getNode(
"azimuth");
570 sm->azimuth_error =
new simgear::Value(*prop_root, b ? *b : n);
573 b = a->getNode(
"elevation");
574 sm->elevation_error =
new simgear::Value(*prop_root, b ? *b : n);
577 b = a->getNode(
"cd");
579 b = a->getNode(
"cd",
true);
580 b->setDoubleValue(0.1);
582 sm->cd_randomness =
new simgear::Value(*prop_root, *b);
585 b = a->getNode(
"life");
587 b = a->getNode(
"life",
true);
588 b->setDoubleValue(0.5);
590 sm->life_randomness =
new simgear::Value(*prop_root, *b);
592 if (
sm->contents_node != 0)
593 sm->contents =
sm->contents_node->getDoubleValue();
595 string trigger_path = entry_node->getStringValue(
"trigger",
"");
596 if (!trigger_path.empty()) {
598 sm->trigger_node->setBoolValue(
sm->trigger_node->getBoolValue());
600 sm->trigger_node = 0;
603 if (
sm->speed_node != 0)
604 sm->speed =
sm->speed_node->getDoubleValue();
606 sm->timer =
sm->delay;
608 sm->first_time =
false;
609 sm->serviceable = serviceable;
613 sm->prop->tie(
"delay", SGRawValuePointer<double>(&(
sm->delay)));
614 sm->prop->tie(
"count", SGRawValuePointer<int>(&(
sm->count)));
615 sm->prop->tie(
"repeat", SGRawValuePointer<bool>(&(
sm->repeat)));
616 sm->prop->tie(
"id", SGRawValuePointer<int>(&(
sm->id)));
617 sm->prop->tie(
"sub-id", SGRawValuePointer<int>(&(
sm->sub_id)));
618 sm->prop->tie(
"serviceable", SGRawValuePointer<bool>(&(
sm->serviceable)));
619 sm->prop->tie(
"random", SGRawValuePointer<bool>(&(
sm->random)));
620 sm->prop->tie(
"slaved", SGRawValuePointer<bool>(&(
sm->slaved)));
621 const string&
name =
sm->name;
622 sm->prop->setStringValue(
"name",
name.c_str());
625 sm->prop->setStringValue(
"submodel",
submodel.c_str());
627 const string& force_path =
sm->force_path;
628 sm->prop->setStringValue(
"force_path", force_path.c_str());
630 if (
sm->contents_node != 0)
631 sm->prop->tie(
"contents-lbs", SGRawValuePointer<double>(&(
sm->contents)));
634 models.push_back(sm);
638void FGSubmodelMgr::loadSubmodels()
640 SG_LOG(SG_AI, SG_DEBUG,
"Submodels: Loading sub submodels");
644 submodel_iterator = submodels.begin();
646 while (submodel_iterator != submodels.end()) {
650 SG_LOG(SG_AI, SG_DEBUG,
"found path sub sub " <<
submodel <<
" index " << index <<
" name " << (*submodel_iterator)->
name);
652 if ((*submodel_iterator)->sub_id == 0) {
653 (*submodel_iterator)->sub_id = index;
655 bool serviceable =
true;
656 setData(index,
submodel, serviceable,
"/ai/submodels/subsubmodel", subsubmodels);
663 submodels.reserve(submodels.size() + subsubmodels.size());
666 subsubmodel_iterator = subsubmodels.begin();
667 while (subsubmodel_iterator != subsubmodels.end()) {
668 submodels.push_back(*subsubmodel_iterator);
669 ++subsubmodel_iterator;
672 subsubmodels.clear();
675SGVec3d FGSubmodelMgr::getCartOffsetPos(submodel* sm)
const
682 if (
sm->offsets_in_meter) {
683 offset = SGVec3d(-
sm->x_offset->get_value(),
684 sm->y_offset->get_value(),
685 -
sm->z_offset->get_value());
687 offset = SGVec3d(
sm->x_offset->get_value() * SG_FEET_TO_METER,
688 sm->y_offset->get_value() * SG_FEET_TO_METER,
689 -
sm->z_offset->get_value() * SG_FEET_TO_METER);
693 SGQuatd hlTrans = SGQuatd::fromLonLat(userpos);
697 hlTrans *= SGQuatd::fromYawPitchRollDeg(
704 SGVec3d off = hlTrans.backTransform(offset);
707 return SGVec3d::fromGeod(userpos) + off;
710void FGSubmodelMgr::setOffsetPos(submodel* sm)
713 SGVec3d cartoffsetPos = getCartOffsetPos(sm);
714 SGGeodesy::SGCartToGeod(cartoffsetPos, offsetpos);
717void FGSubmodelMgr::valueChanged(SGPropertyNode* prop)
748void FGSubmodelMgr::setParentNode(
int id)
750 const SGPropertyNode_ptr ai =
fgGetNode(
"/ai/models",
true);
752 for (
int i = ai->nChildren() - 1;
i >= -1;
i--) {
753 SGPropertyNode_ptr model;
757 model = _selected_ac;
759 model = ai->getChild(
i);
760 int parent_id = model->getIntValue(
"id");
761 if (!model->nChildren()) {
764 if (parent_id ==
id) {
766 _selected_ac = model;
774 if (_selected_ac != 0) {
776 IC.lat = _selected_ac->getDoubleValue(
"position/latitude-deg");
777 IC.lon = _selected_ac->getDoubleValue(
"position/longitude-deg");
778 IC.alt = _selected_ac->getDoubleValue(
"position/altitude-ft");
779 IC.roll = _selected_ac->getDoubleValue(
"orientation/roll-deg");
780 IC.elevation = _selected_ac->getDoubleValue(
"orientation/pitch-deg");
781 IC.azimuth = _selected_ac->getDoubleValue(
"orientation/true-heading-deg");
782 IC.speed = _selected_ac->getDoubleValue(
"velocities/true-airspeed-kt") * SG_KT_TO_FPS;
783 IC.speed_down_fps = -_selected_ac->getDoubleValue(
"velocities/vertical-speed-fps");
784 IC.speed_east_fps = _selected_ac->getDoubleValue(
"velocities/speed-east-fps");
785 IC.speed_north_fps = _selected_ac->getDoubleValue(
"velocities/speed-north-fps");
787 SG_LOG(SG_AI, SG_ALERT,
"AISubmodel: parent node not found ");
794 SGSubsystemMgr::POST_FDM);
void setImpactReportNode(const std::string &)
void setSubmodel(const std::string &)
void setParentNodes(const SGPropertyNode_ptr)
void setFuseRange(double f)
void setDragArea(double a)
void setStabilisation(bool val)
void setLifeRandomness(double randomness)
void setWind_from_east(double fps)
void setForcePath(const std::string &)
void setLife(double seconds)
void setContentsNode(const SGPropertyNode_ptr)
void setCollision(bool c)
void setAzimuth(double az)
void setForceStabilisation(bool val)
void setElevation(double el)
void setCdRandomness(double randomness)
void setAzimuthRandomError(double error)
void setWind_from_north(double fps)
void setExternalForce(bool f)
void setElevationRandomError(double error)
void setBuoyancy(double fpss)
void setSpeed(double speed_KTAS)
void setName(const std::string &n)
void setLatitude(double latitude)
void setYoffset(double y_offset)
void setAltitude(double altitude_ft)
void setZoffset(double z_offset)
void setLongitude(double longitude)
void setPath(const char *model)
void setXoffset(double x_offset)
void setYawoffset(double z_offset)
void setPitchoffset(double x_offset)
std::vector< FGAIBasePtr > ai_list_type
SGPath resolve_aircraft_path(const std::string &branch) const
Given a path to an aircraft-related resource file, resolve it against the appropriate root.
void update(double dt) override
const double sm(5280 *foot)
SGPropertyNode * fgGetNode(const char *path, bool create)
Get a property node.
SGSubsystemMgr::Registrant< FGSubmodelMgr > registrantFGSubmodelMgr(SGSubsystemMgr::POST_FDM)