11#include <simgear/math/sg_random.hxx>
12#include <simgear/math/sg_geodesy.hxx>
13#include <simgear/scene/model/modellib.hxx>
42_aero_stabilised(false),
55_force_stabilised(false),
57_slave_load_to_ac(false),
59_report_collision(false),
61_external_force(false),
63_impact_report_node(
fgGetNode(
"/ai/models/model-impact", true))
78 setRandom(scFileNode->getBoolValue(
"random",
false));
79 setAzimuth(scFileNode->getDoubleValue(
"azimuth", 0.0));
80 setElevation(scFileNode->getDoubleValue(
"elevation", 0));
81 setDragArea(scFileNode->getDoubleValue(
"eda", 0.007));
82 setLife(scFileNode->getDoubleValue(
"life", 900.0));
83 setBuoyancy(scFileNode->getDoubleValue(
"buoyancy", 0));
86 setWind(scFileNode->getBoolValue(
"wind",
false));
87 setRoll(scFileNode->getDoubleValue(
"roll", 0.0));
88 setCd(scFileNode->getDoubleValue(
"cd", 0.029));
90 setWeight(scFileNode->getDoubleValue(
"weight", 0.25));
92 setNoRoll(scFileNode->getBoolValue(
"no-roll",
false));
93 setImpact(scFileNode->getBoolValue(
"impact",
false));
94 setExpiry(scFileNode->getBoolValue(
"expiry",
false));
95 setCollision(scFileNode->getBoolValue(
"collision",
false));
97 setName(scFileNode->getStringValue(
"name",
"Rocket"));
98 setFuseRange(scFileNode->getDoubleValue(
"fuse-range", 0.0));
99 setSMPath(scFileNode->getStringValue(
"submodel-path",
""));
100 setSubID(scFileNode->getIntValue(
"SubID", 0));
102 setForcePath(scFileNode->getStringValue(
"force-path",
""));
104 setXoffset(scFileNode->getDoubleValue(
"x-offset", 0.0));
105 setYoffset(scFileNode->getDoubleValue(
"y-offset", 0.0));
106 setZoffset(scFileNode->getDoubleValue(
"z-offset", 0.0));
108 setRolloffset(scFileNode->getDoubleValue(
"roll-offset", 0.0));
109 setYawoffset(scFileNode->getDoubleValue(
"yaw-offset", 0.0));
111 setLoadOffset(scFileNode->getDoubleValue(
"load-offset", 0.0));
112 setSlaved(scFileNode->getBoolValue(
"slaved",
false));
113 setSlavedLoad(scFileNode->getBoolValue(
"slaved-load",
false));
144 props->setStringValue(
"material/name",
"");
146 props->setStringValue(
"submodels/path",
_path.c_str());
149 props->setStringValue(
"force/path", _force_path.c_str());
150 props->setStringValue(
"contents/path", _contents_path.c_str());
185 tie(
"sim/time/elapsed-sec",
186 SGRawValueMethods<FGAIBallistic,double>(*
this,
192 tie(
"material/solid",
193 SGRawValuePointer<bool>(&_solid));
194 tie(
"altitude-agl-ft",
196 tie(
"controls/slave-to-ac",
197 SGRawValueMethods<FGAIBallistic,bool>
199 tie(
"controls/invisible",
202 if (_external_force || _slave_to_ac) {
203 tie(
"controls/force_stabilized",
204 SGRawValuePointer<bool>(&_force_stabilised));
205 tie(
"position/global-x",
207 tie(
"position/global-y",
209 tie(
"position/global-z",
211 tie(
"velocities/vertical-speed-fps",
213 tie(
"velocities/true-airspeed-kt",
214 SGRawValuePointer<double>(&
speed));
215 tie(
"velocities/horizontal-speed-fps",
216 SGRawValuePointer<double>(&
hs));
217 tie(
"position/altitude-ft",
219 tie(
"position/latitude-deg",
221 tie(
"position/longitude-deg",
223 tie(
"orientation/hdg-deg",
224 SGRawValuePointer<double>(&
hdg));
225 tie(
"orientation/pitch-deg",
226 SGRawValuePointer<double>(&
pitch));
227 tie(
"orientation/roll-deg",
228 SGRawValuePointer<double>(&
roll));
229 tie(
"controls/slave-load-to-ac",
230 SGRawValueMethods<FGAIBallistic,bool>
232 tie(
"position/load-offset",
233 SGRawValueMethods<FGAIBallistic,double>
235 tie(
"load/distance-to-hitch-ft",
236 SGRawValueMethods<FGAIBallistic,double>
238 tie(
"load/elevation-to-hitch-deg",
239 SGRawValueMethods<FGAIBallistic,double>
241 tie(
"load/bearing-to-hitch-deg",
242 SGRawValueMethods<FGAIBallistic,double>
244 tie(
"material/load-resistance",
245 SGRawValuePointer<double>(&_load_resistance));
266 hdg =
_azimuth = az - _az_random_error + 2 * _az_random_error * sg_random();
272 _az_random_error = error;
276 _el_random_error = error;
281 pitch =
_elevation = el - _el_random_error + 2 * _el_random_error * sg_random();
291 _aero_stabilised = val;
295 _force_stabilised = val;
308 life = seconds * _life_randomness + (seconds * (1 -_life_randomness) * sg_random());
335 _cd_randomness = randomness;
347 _life_randomness = randomness;
359 _report_collision = c;
372 _impact_report_node =
fgGetNode(path.c_str(),
true);
409 _contents_path = path;
412 _contents_node =
fgGetNode(path.c_str(),
true);
418 _contents_node = node;
419 _contents_path = _contents_node->getDisplayName();
459double FGAIBallistic::getMass()
const {
464 if (_contents_node) {
465 _contents_lb = _contents_node->getChild(
"level-lbs", 0, 1)->getDoubleValue();
470void FGAIBallistic::setContents(
double c) {
472 _contents_lb = _contents_node->getChild(
"level-gal_us", 0, 1)->setDoubleValue(c);
476 _slave_load_to_ac = l;
480 return _slave_load_to_ac;
485 if (!_force_path.empty()) {
486 SGPropertyNode *fnode =
fgGetNode(_force_path.c_str(), 0,
true );
487 _force_node = fnode->getChild(
"force-lb", 0,
true);
494 const simgear::BVHMaterial* mat = 0;
497 const SGMaterial* material =
dynamic_cast<const SGMaterial*
>(mat);
501 const std::vector<string>& names = material->get_names();
502 _solid = material->get_solid();
503 _load_resistance = material->get_load_resistance();
504 _frictionFactor = material->get_friction_factor();
507 props->setStringValue(
"material/name", names[0].c_str());
509 props->setStringValue(
"material/name",
"");
511 _mat_name = names[0];
527double FGAIBallistic::getRecip(
double az) {
538 double c = dt / (coeff + dt);
543 double c = dt / (coeff + dt);
548 double c = dt / (coeff + dt);
553 double c = dt / (coeff + dt);
558 double recip = getRecip(
hdg);
559 double c = dt / (coeff + dt);
562 if (tgt_hdg < recip && tgt_hdg < hdg && hdg > 180) {
563 hdg = ((tgt_hdg + 360) * c) + (
hdg * (1 - c));
565 }
else if (tgt_hdg > recip && tgt_hdg >
hdg &&
hdg <= 180){
566 hdg = ((tgt_hdg - 360) * c) + (
hdg * (1 - c));
569 hdg = (tgt_hdg * c) + (
hdg * (1 - c));
599void FGAIBallistic::slaveToAC(
double dt) {
603 double hdg, pch, rll;
616 pch =
manager->get_user_pitch();
617 rll =
manager->get_user_roll();
650 if (_life_timer >
life &&
life != -1) {
670 double cd_min = _cd * 0.9;
671 double cd_max = _cd * 1.1;
674 _cd = _init_cd * (1 - _cd_randomness + 2 * _cd_randomness * sg_random());
676 if (_cd < cd_min) _cd = cd_min;
677 if (_cd > cd_max) _cd = cd_max;
686 const double rho = rhoKgM3 / SG_SLUGFT3_TO_KGPM3;
689 Cdm = 0.0125 * Mach + _cd;
691 Cdm = 0.3742 * pow(Mach, 2) - 0.252 * Mach + 0.0021 + _cd;
693 Cdm = 0.2965 * pow(Mach, -1.1506) + _cd;
706 speed -= (Cdm * 0.5 * rho *
speed *
speed * _drag_area/_mass) * dt * SG_KT_TO_FPS;
732 double force_speed_north_deg_sec = 0.0;
733 double force_speed_east_deg_sec = 0.0;
734 double v_force_acc_fpss = 0.0;
735 double force_speed_north_fps = 0.0;
736 double force_speed_east_fps = 0.0;
737 double h_force_lbs = 0.0;
738 double normal_force_fpss = 0.0;
739 double friction_force_speed_north_fps = 0.0;
740 double friction_force_speed_east_fps = 0.0;
741 double friction_force_speed_north_deg_sec = 0.0;
742 double friction_force_speed_east_deg_sec = 0.0;
743 double force_elevation_deg = 0.0;
745 if (_external_force) {
748 SGPropertyNode *n =
fgGetNode(_force_path.c_str(),
true);
749 double force_lbs = n->getChild(
"force-lb", 0,
true)->getDoubleValue();
750 force_elevation_deg = n->getChild(
"force-elevation-deg", 0,
true)->getDoubleValue();
751 double force_azimuth_deg = n->getChild(
"force-azimuth-deg", 0,
true)->getDoubleValue();
754 double v_force_lbs = force_lbs * sin( force_elevation_deg * SG_DEGREES_TO_RADIANS );
755 h_force_lbs = force_lbs * cos( force_elevation_deg * SG_DEGREES_TO_RADIANS );
757 double normal_force_lbs = 0.0;
758 double dynamic_friction_force_lbs = 0.0;
761 if (!_report_impact &&
getHtAGL(10000)) {
762 double deadzone = 0.1;
764 if (
_ht_agl_ft <= (_ground_offset + deadzone) && _solid) {
765 normal_force_lbs = (_mass *
slugs_to_lbs) - v_force_lbs;
767 if (normal_force_lbs < 0.0)
768 normal_force_lbs = 0.0;
770 pos.setElevationFt(0 + _ground_offset);
778 double static_friction_force_lbs = mu * normal_force_lbs * _frictionFactor;
781 if (h_force_lbs <= static_friction_force_lbs &&
hs <= 5) {
782 h_force_lbs =
hs = 0;
786 dynamic_friction_force_lbs = static_friction_force_lbs * 0.95;
796 v_force_acc_fpss = v_force_lbs / _mass;
797 normal_force_fpss = normal_force_lbs / _mass;
798 double h_force_acc_fpss = h_force_lbs / _mass;
799 double dynamic_friction_acc_fpss = dynamic_friction_force_lbs / _mass;
802 double hs_force_fps = h_force_acc_fpss * dt;
803 double friction_force_fps = dynamic_friction_acc_fpss * dt;
806 force_speed_north_fps = cos(force_azimuth_deg * SG_DEGREES_TO_RADIANS) * hs_force_fps;
807 force_speed_east_fps = sin(force_azimuth_deg * SG_DEGREES_TO_RADIANS) * hs_force_fps;
809 friction_force_speed_north_fps = cos(getRecip(
hdg) * SG_DEGREES_TO_RADIANS) * friction_force_fps;
810 friction_force_speed_east_fps = sin(getRecip(
hdg) * SG_DEGREES_TO_RADIANS) * friction_force_fps;
813 force_speed_north_deg_sec = force_speed_north_fps /
ft_per_deg_lat;
816 friction_force_speed_north_deg_sec = friction_force_speed_north_fps /
ft_per_deg_lat;
817 friction_force_speed_east_deg_sec = friction_force_speed_east_fps /
ft_per_deg_lon;
825 hs = sqrt(((
_speed_north_fps + force_speed_north_fps + friction_force_speed_north_fps)
826 * (
_speed_north_fps + force_speed_north_fps + friction_force_speed_north_fps))
827 + ((
_speed_east_fps + force_speed_east_fps + friction_force_speed_east_fps)
828 * (
_speed_east_fps + force_speed_east_fps + friction_force_speed_east_fps)));
835 vs_fps -= (gravity - _buoyancy - v_force_acc_fpss - normal_force_fpss) * dt;
841 if (_slave_load_to_ac) {
851 double deadzone = 0.1;
853 if (
_ht_agl_ft <= (0 + _ground_offset + deadzone) && _solid) {
854 pos.setElevationFt(0 + _ground_offset);
857 pos.setElevationFt(
_offsetpos.getElevationFt() + _load_offset);
862 pos.setLatitudeDeg(
pos.getLatitudeDeg()
864 + force_speed_north_deg_sec + friction_force_speed_north_deg_sec) * dt );
865 pos.setLongitudeDeg(
pos.getLongitudeDeg()
867 + force_speed_east_deg_sec + friction_force_speed_east_deg_sec) * dt );
882 (
_speed_north_fps + force_speed_north_fps + friction_force_speed_north_fps))
883 * SG_RADIANS_TO_DEGREES;
889 if (_aero_stabilised) {
891 const double coeff = 0.9;
897 else if (_force_stabilised) {
900 const double coeff = 0.9;
903 if (ratio > 1) ratio = 1;
904 if (ratio < -1) ratio = -1;
906 double force_pitch = acos(ratio) * SG_RADIANS_TO_DEGREES;
908 if (force_pitch <= force_elevation_deg)
909 force_pitch = force_elevation_deg;
912 setPch(force_pitch,dt, coeff);
936void FGAIBallistic::handleEndOfLife(
double elevation) {
949void FGAIBallistic::handle_impact() {
951 double start =
pos.getElevationM() + 100;
957 SG_LOG(SG_AI, SG_DEBUG,
"AIBallistic: terrain impact material" << _mat_name);
963void FGAIBallistic::handle_expiry() {
965 handleEndOfLife(
pos.getElevationM());
968void FGAIBallistic::handle_collision()
971 pos.getLatitudeDeg(),
pos.getLongitudeDeg(), _fuse_range);
974 report_impact(
pos.getElevationM(),
object);
979void FGAIBallistic::report_impact(
double elevation,
const FGAIBase *
object)
989 SGPropertyNode *n =
props->getNode(
"impact",
true);
992 n->setStringValue(
"type",
static_cast<std::string
>(object->
getTypeString()));
994 n->setStringValue(
"type",
"terrain");
996 SG_LOG(SG_AI, SG_DEBUG,
"AIBallistic: object impact " <<
_name
1007 _impact_report_node->setStringValue(
props->getPath());
1012 SGVec3d cartuserPos =
globals->get_aircraft_position_cart();
1020 SGVec3d _off(
_x_offset * SG_FEET_TO_METER,
1025 SGQuatd hlTrans = SGQuatd::fromLonLat(
globals->get_aircraft_position());
1029 hlTrans *= SGQuatd::fromYawPitchRollDeg(
1036 SGVec3d off = hlTrans.backTransform(_off);
1039 SGVec3d offsetPos = cartuserPos + off;
1045 SGVec3d cartoffsetPos = getCartOffsetPos(inpos, heading,
pitch,
roll);
1046 SGGeodesy::SGCartToGeod(cartoffsetPos,
_offsetpos);
1054 SGVec3d diff = carthitchPos - cartPos;
1055 double distance = norm(diff);
1056 return distance * SG_METER_TO_FEET;
1063 double daltM =
_offsetpos.getElevationM() -
pos.getElevationM();
1065 if (fabs(distance) < SGLimits<float>::min()) {
1068 double sAngle = daltM/distance;
1069 sAngle = SGMiscd::min(1, SGMiscd::max(-1, sAngle));
1070 angle = SGMiscd::rad2deg(asin(sAngle));
1088 double az1, az2, distance;
1090 geo_inverse_wgs_84(
_offsetpos,
globals->get_aircraft_position(), &az1, &az2, &distance);
1092 double rel_brg = az1 -
hdg;
1094 SG_NORMALIZE_RANGE(rel_brg, -180.0, 180.0);
1102 SGVec3d cartuserPos =
globals->get_aircraft_position_cart();
1104 SGVec3d diff = cartuserPos - carthitchPos;
1106 double distance = norm(diff);
1109 double daltM =
globals->get_aircraft_position().getElevationM() -
_offsetpos.getElevationM();
1112 if (fabs(distance) < SGLimits<float>::min()) {
1116 double sAngle = daltM/distance;
1117 sAngle = SGMiscd::min(1, SGMiscd::max(-1, sAngle));
1118 angle = SGMiscd::rad2deg(asin(sAngle));
1125 double c = dt / (coeff + dt);
1132void FGAIBallistic::calcVSHS() {
1145void FGAIBallistic::calcNE() {
1155SGVec3d FGAIBallistic::getCartOffsetPos(SGGeod inpos,
double user_heading,
1156 double user_pitch,
double user_roll
1159 SGVec3d cartuserPos = SGVec3d::fromGeod(inpos);
1165 SGVec3d _off(
_x_offset * SG_FEET_TO_METER,
1170 SGQuatd hlTrans = SGQuatd::fromLonLat(inpos);
1174 hlTrans *= SGQuatd::fromYawPitchRollDeg(
1181 SGVec3d off = hlTrans.backTransform(_off);
1184 SGVec3d offsetPos = cartuserPos + off;
1191 SGVec3d cartoffsetPos = SGVec3d::fromGeod(offsetpos);
1192 SGVec3d diff = cartoffsetPos - _oldcartoffsetPos;
1194 double distance = norm(diff);
1196 speed = (distance / dt) * SG_MPS_TO_KT;
1200 double daltM = offsetpos.getElevationM() -
_oldoffsetpos.getElevationM();
1202 if (fabs(distance) < SGLimits<float>::min()) {
1206 double sAngle = daltM / distance;
1207 sAngle = SGMiscd::min(1, SGMiscd::max(-1, sAngle));
1208 angle = SGMiscd::rad2deg(asin(sAngle));
1220 double az1, az2, dist;
1221 geo_inverse_wgs_84(
_oldoffsetpos, offsetpos, &az1, &az2, &dist);
1234 _oldcartoffsetPos = cartoffsetPos;
static const Gravity * instance()
virtual double getGravity(const SGGeod &position) const =0
void update(double dt) override
SGPropertyNode_ptr _p_lat_node
bool init(ModelSearchOrder searchOrder) override
bool getSlavedLoad() const
void setTgtXOffset(double x)
void setSlavedLoad(bool s)
double getLoadOffset() const
SGPropertyNode_ptr _force_elevation_node
void setTgtZOffset(double z)
void readFromScenario(SGPropertyNode *scFileNode) override
void setOffsetVelocity(double dt, SGGeod pos)
double getTgtYOffset() const
SGPropertyNode_ptr _p_rll_node
SGPropertyNode_ptr _p_vel_node
void setImpactReportNode(const std::string &)
void setSubmodel(const std::string &)
SGPropertyNode_ptr _p_lon_node
void setParentNodes(const SGPropertyNode_ptr)
SGPropertyNode_ptr _p_spd_node
bool getHtAGL(double start)
void setFuseRange(double f)
double getTgtXOffset() const
void setDragArea(double a)
void setBnk(double r, double dt, double c)
void setStabilisation(bool val)
static const double slugs_to_kgs
void setLifeRandomness(double randomness)
void setSpd(double s, double dt, double c)
SGVec3d getCartHitchPos() const
int setHdg(double az, double dt, double c)
void setWind_from_east(double fps)
void setTgtYOffset(double y)
void setForcePath(const std::string &)
void setLife(double seconds)
SGPropertyNode_ptr _p_ori_node
SGPropertyNode_ptr _force_node
void setOffsetPos(SGGeod pos, double heading, double pitch, double roll)
void setPch(double e, double dt, double c)
double getBearingToHitch() const
double getElevHitchToUser() const
SGPropertyNode_ptr _p_pos_node
void setHt(double h, double dt, double c)
void setContentsNode(const SGPropertyNode_ptr)
double getTgtZOffset() const
void setCollision(bool c)
double getRelBrgHitchToUser() const
static const double slugs_to_lbs
SGPropertyNode_ptr _p_pch_node
double getElevToHitch() const
void setSMPath(const std::string &)
void setTgtOffsets(double dt, double c)
void setAzimuth(double az)
void setLoadOffset(double l)
double getDistanceToHitch() const
void setForceStabilisation(bool val)
void setElevation(double el)
void setGroundOffset(double g)
void setContentsPath(const std::string &)
SGPropertyNode_ptr _p_hdg_node
void setCdRandomness(double randomness)
void setAzimuthRandomError(double error)
SGPropertyNode_ptr _force_azimuth_node
void setWind_from_north(double fps)
void setExternalForce(bool f)
SGPropertyNode_ptr _p_alt_node
SGPropertyNode_ptr _pnode
void setElevationRandomError(double error)
SGPropertyNode_ptr _p_agl_node
FGAIBallistic(object_type ot=object_type::otBallistic)
void setBuoyancy(double fpss)
void setSpeed(double speed_KTAS)
void setName(const std::string &n)
simgear::TiedPropertyList _tiedProperties
virtual void readFromScenario(SGPropertyNode *scFileNode)
double _getElevationFt() const
SGVec3d getCartPos() const
void setParentName(const std::string &p)
double _getLatitude() const
void _setAltitude(double _alt)
void setYoffset(double y_offset)
double _getCartPosX() const
FGAIBase(object_type ot, bool enableHot)
bool getGroundElevationM(const SGGeod &pos, double &elev, const simgear::BVHMaterial **material) const
void setBank(double bank)
SGPropertyNode_ptr _selected_ac
double _getCartPosY() const
virtual bool init(ModelSearchOrder searchOrder)
virtual void update(double dt)
void setZoffset(double z_offset)
double _getCartPosZ() const
double _getLongitude() const
void _setLatitude(double latitude)
double speed_east_deg_sec
virtual std::string_view getTypeString(void) const
void setXoffset(double x_offset)
void setYawoffset(double z_offset)
void _setVS_fps(double _vs)
void setHeading(double heading)
void _setLongitude(double longitude)
double speed_north_deg_sec
double _getVS_fps() const
void setPitch(double newpitch)
void tie(const char *aRelPath, const SGRawValue< T > &aRawValue)
Tied-properties helper, record nodes which are tied for easy un-tie-ing.
void setPitchoffset(double x_offset)
void setRolloffset(double y_offset)
static double densityAtAltitudeFt(const double alt, const double Tsl=atmodel::ISA::T0)
static double machFromKnotsAtAltitudeFt(const double knots, const double altFt, const double Tsl=atmodel::ISA::T0)
SGGeod get_aircraft_position() const
SGPropertyNode * fgGetNode(const char *path, bool create)
Get a property node.