14#include <simgear/sg_inlines.h>
15#include <simgear/math/sg_geodesy.hxx>
30 simgear::Emesary::GlobalTransmitter::instance()->Register(
this);
35 simgear::Emesary::GlobalTransmitter::instance()->DeRegister(
this);
44 setRadius(scFileNode->getDoubleValue(
"turn-radius-ft", 2000));
45 setSign(scFileNode->getStringValue(
"pennant-number"));
50 setMaxLat(scFileNode->getDoubleValue(
"max-lat", 0));
51 setMinLat(scFileNode->getDoubleValue(
"min-lat", 0));
52 setMaxLong(scFileNode->getDoubleValue(
"max-long", 0));
53 setMinLong(scFileNode->getDoubleValue(
"min-long", 0));
54 setMPControl(scFileNode->getBoolValue(
"mp-control",
false));
55 setAIControl(scFileNode->getBoolValue(
"ai-control",
false));
56 setCallSign(scFileNode->getStringValue(
"callsign",
""));
58 _angled_deck_degrees = scFileNode->getDoubleValue(
"angled-deck-degrees", -8.5);
62 _flolsHeadingOffsetDeg = flolsNode->getDoubleValue(
"heading-offset-deg", 0.0);
63 _flolsApproachAngle = flolsNode->getDoubleValue(
"glidepath-angle-deg", 3.5);
66 _flolsPosOffset(2) = -(_deck_altitude_ft * SG_FEET_TO_METER + 10);
73 _flolsTouchdownPosition = _flolsPosOffset;
77 _towerPosition(2) = -(_deck_altitude_ft * SG_FEET_TO_METER + 10);
78 SG_LOG(SG_AI, SG_INFO,
"AICarrier: tower-position not defined - using default");
82 _lsoPosition(2) = -(_deck_altitude_ft * SG_FEET_TO_METER + 10);
83 SG_LOG(SG_AI, SG_INFO,
"AICarrier: lso-position not defined - using default");
87 std::vector<SGPropertyNode_ptr>
props = scFileNode->getChildren(
"parking-pos");
88 std::vector<SGPropertyNode_ptr>::const_iterator it;
89 for (it =
props.begin(); it !=
props.end(); ++it) {
90 const std::string
name = (*it)->getStringValue(
"name",
"unnamed");
94 double offset_x = -(*it)->getDoubleValue(
"x-offset-m", 0);
95 double offset_y = (*it)->getDoubleValue(
"y-offset-m", 0);
96 double offset_z = -(*it)->getDoubleValue(
"z-offset-m", 0);
97 double hd = (*it)->getDoubleValue(
"heading-offset-deg", 0);
98 ParkPosition pp(
name, SGVec3d(offset_x, offset_y, offset_z), hd);
99 _ppositions.push_back(pp);
104 _wind_from_east = fps;
108 _wind_from_north = fps;
112 _max_lat = fabs(deg);
116 _min_lat = fabs(deg);
120 _max_lon = fabs(deg);
124 _min_lon = fabs(deg);
129 _deck_altitude_ft = altitude_feet;
137 _TACAN_channel_id = id;
148void FGAICarrier::update(
double dt) {
153 if (_is_user_craft->getBoolValue()) {
154 _latitude_node->setDoubleValue(
pos.getLatitudeDeg());
155 _longitude_node->setDoubleValue(
pos.getLongitudeDeg());
156 _altitude_node->setDoubleValue(
pos.getElevationFt());
157 _heading_node->setDoubleValue(
hdg);
158 _pitch_node->setDoubleValue(
pitch);
159 _roll_node->setDoubleValue(
roll);
164 if (_ai_latch_node->getStringValue() !=
"") {
165 SG_LOG(SG_AI, SG_DEBUG,
"FGAICarrier::update(): not updating because ai-latch=" << _ai_latch_node->getStringValue());
167 else if (!_MPControl && _AIControl){
169 if(_turn_to_launch_hdg){
171 }
else if(_turn_to_recovery_hdg ){
189 SGQuatd ec2hl = SGQuatd::fromLonLat(
pos);
191 SGQuatd hl2body = SGQuatd::fromYawPitchRollDeg(
hdg,
pitch,
roll);
194 SGQuatd ec2body = ec2hl * hl2body;
196 SGVec3d cartPos = SGVec3d::fromGeod(
pos);
202 SGVec3d eyeWrtCarrier = eyePos - cartPos;
204 eyeWrtCarrier = ec2body.transform(eyeWrtCarrier);
206 SGVec3d eyeWrtFlols = eyeWrtCarrier - _flolsPosOffset;
211 _flols_dist = norm(eyeWrtFlols);
214 double lineup_hdg, lineup_az2, lineup_s;
216 SGGeod g_eyePos = SGGeod::fromCart(eyePos);
217 SGGeod g_carrier = SGGeod::fromCart(cartPos);
222 switch (_view_index) {
225 viewPosition = SGGeod::fromCart(
getCartPosAt(_towerPosition));
229 viewPosition = SGGeod::fromCart(
getCartPosAt(_flolsTouchdownPosition));
233 viewPosition = SGGeod::fromCart(
getCartPosAt(_lsoPosition));
236 _view_position_lat_deg_node->setDoubleValue(viewPosition.getLatitudeDeg());
237 _view_position_lon_deg_node->setDoubleValue(viewPosition.getLongitudeDeg());
238 _view_position_alt_ft_node->setDoubleValue(viewPosition.getElevationFt());
240 SGGeodesy::inverse(g_carrier, g_eyePos, lineup_hdg, lineup_az2, lineup_s);
242 double target_lineup =
_getHeading() + _angled_deck_degrees + 180.0;
243 SG_NORMALIZE_RANGE(target_lineup, 0.0, 360.0);
245 _lineup = lineup_hdg - target_lineup;
247 if (fabs(_flols_dist) < SGLimits<double>::min()) {
250 double sAngle = -eyeWrtFlols(2) / _flols_dist;
251 sAngle = SGMiscd::min(1, SGMiscd::max(-1, sAngle));
252 _flols_angle = SGMiscd::rad2deg(asin(sAngle));
254 if (_flols_dist < 8000){
255 SGVec3d eyeWrtFlols_tdp = eyeWrtCarrier - _flolsTouchdownPosition;
258 double dist_tdp = norm(eyeWrtFlols_tdp);
262 if (fabs(dist_tdp) < SGLimits<double>::min()) {
266 double sAngle = -eyeWrtFlols_tdp(2) / dist_tdp;
267 sAngle = SGMiscd::min(1, SGMiscd::max(-1, sAngle));
275 if ( _flols_angle <= 4.35 && _flols_angle > 4.01 )
276 _flols_visible_light = 1;
277 else if ( _flols_angle <= 4.01 && _flols_angle > 3.670 )
278 _flols_visible_light = 2;
279 else if ( _flols_angle <= 3.670 && _flols_angle > 3.330 )
280 _flols_visible_light = 3;
281 else if ( _flols_angle <= 3.330 && _flols_angle > 2.990 )
282 _flols_visible_light = 4;
283 else if ( _flols_angle <= 2.990 && _flols_angle > 2.650 )
284 _flols_visible_light = 5;
285 else if ( _flols_angle <= 2.650 )
286 _flols_visible_light = 6;
288 _flols_visible_light = 0;
293 if (_flols_dist < 3200) {
294 if (_flols_dist > 100) {
295 bool new_wave_off_lights_demand = (_flols_angle <= 3.0);
297 if (new_wave_off_lights_demand != _wave_off_lights_demand) {
299 _wave_off_lights_demand = new_wave_off_lights_demand;
303 if (_flols_angle < 2 && _flols_dist < 800) {
304 _wave_off_lights_demand =
true;
309 _wave_off_lights_demand =
true;
317 _longitude_node =
fgGetNode(
"/position/longitude-deg",
true);
318 _latitude_node =
fgGetNode(
"/position/latitude-deg",
true);
319 _altitude_node =
fgGetNode(
"/position/altitude-ft",
true);
320 _heading_node =
fgGetNode(
"/orientation/true-heading-deg",
true);
321 _pitch_node =
fgGetNode(
"/orientation/pitch-deg",
true);
322 _roll_node =
fgGetNode(
"/orientation/roll-deg",
true);
324 _launchbar_state_node =
fgGetNode(
"/gear/launchbar/state",
true);
326 _surface_wind_from_deg_node =
fgGetNode(
"/environment/config/boundary/entry[0]/wind-from-heading-deg",
true);
327 _surface_wind_speed_node =
fgGetNode(
"/environment/config/boundary/entry[0]/wind-speed-kt",
true);
330 int dmd_course =
fgGetInt(
"/sim/presets/carrier-course");
331 if (dmd_course == 2) {
333 _turn_to_launch_hdg =
true;
334 _turn_to_recovery_hdg =
false;
335 _turn_to_base_course =
false;
336 }
else if (dmd_course == 3) {
338 _turn_to_launch_hdg =
false;
339 _turn_to_recovery_hdg =
true;
340 _turn_to_base_course =
false;
343 _turn_to_launch_hdg =
false;
344 _turn_to_recovery_hdg =
false;
345 _turn_to_base_course =
true;
355 _elevator_pos_norm = 0;
356 _elevator_pos_norm_raw = 0;
358 _elevator_transition_time = 150;
359 _elevator_time_constant = 0.005;
360 _jbd_elevator_pos_norm = 0;
361 _jbd_elevator_pos_norm_raw = 0;
363 _jbd_transition_time = 3;
364 _jbd_time_constant = 0.1;
370 _is_user_craft =
props->getNode(
"is-user-craft",
true );
371 _ai_latch_node =
props->getNode(
"ai-latch",
true );
373 props->untie(
"velocities/true-airspeed-kt");
375 props->getNode(
"position/deck-altitude-feet",
true)->setDoubleValue(_deck_altitude_ft);
377 tie(
"controls/flols/source-lights",
378 SGRawValuePointer<int>(&_flols_visible_light));
379 tie(
"controls/flols/distance-m",
380 SGRawValuePointer<double>(&_flols_dist));
381 tie(
"controls/flols/angle-degs",
382 SGRawValuePointer<double>(&_flols_angle));
383 tie(
"controls/flols/lineup-degs",
384 SGRawValuePointer<double>(&_lineup));
385 tie(
"controls/turn-to-launch-hdg",
386 SGRawValuePointer<bool>(&_turn_to_launch_hdg));
387 tie(
"controls/in-to-wind",
388 SGRawValuePointer<bool>(&_turn_to_launch_hdg));
389 tie(
"controls/base-course-deg",
390 SGRawValuePointer<double>(&_base_course));
391 tie(
"controls/base-speed-kts",
392 SGRawValuePointer<double>(&_base_speed));
393 tie(
"controls/start-pos-lat-deg",
394 SGRawValueMethods<SGGeod,double>(
pos, &SGGeod::getLatitudeDeg));
395 tie(
"controls/start-pos-long-deg",
396 SGRawValueMethods<SGGeod,double>(
pos, &SGGeod::getLongitudeDeg));
397 tie(
"controls/mp-control",
398 SGRawValuePointer<bool>(&_MPControl));
399 tie(
"controls/ai-control",
400 SGRawValuePointer<bool>(&_AIControl));
401 tie(
"environment/surface-wind-speed-true-kts",
402 SGRawValuePointer<double>(&_wind_speed_kts));
403 tie(
"environment/surface-wind-from-true-degs",
404 SGRawValuePointer<double>(&_wind_from_deg));
405 tie(
"environment/rel-wind-from-degs",
406 SGRawValuePointer<double>(&_rel_wind_from_deg));
407 tie(
"environment/rel-wind-from-carrier-hdg-degs",
408 SGRawValuePointer<double>(&_rel_wind));
409 tie(
"environment/rel-wind-speed-kts",
410 SGRawValuePointer<double>(&_rel_wind_speed_kts));
411 tie(
"environment/in-to-wind",
412 SGRawValuePointer<bool>(&_in_to_wind));
413 tie(
"controls/flols/wave-off-lights-demand",
414 SGRawValuePointer<bool>(&_wave_off_lights_demand));
415 tie(
"controls/elevators",
416 SGRawValuePointer<bool>(&_elevators));
417 tie(
"surface-positions/elevators-pos-norm",
418 SGRawValuePointer<double>(&_elevator_pos_norm));
419 tie(
"controls/constants/elevators/trans-time-s",
420 SGRawValuePointer<double>(&_elevator_transition_time));
421 tie(
"controls/constants/elevators/time-constant",
422 SGRawValuePointer<double>(&_elevator_time_constant));
424 SGRawValuePointer<bool>(&_jbd));
425 tie(
"surface-positions/jbd-pos-norm",
426 SGRawValuePointer<double>(&_jbd_elevator_pos_norm));
427 tie(
"controls/constants/jbd/trans-time-s",
428 SGRawValuePointer<double>(&_jbd_transition_time));
429 tie(
"controls/constants/jbd/time-constant",
430 SGRawValuePointer<double>(&_jbd_time_constant));
431 tie(
"controls/turn-to-recovery-hdg",
432 SGRawValuePointer<bool>(&_turn_to_recovery_hdg));
433 tie(
"controls/turn-to-base-course",
434 SGRawValuePointer<bool>(&_turn_to_base_course));
436 tie(
"controls/view-index", SGRawValuePointer<int>(&_view_index));
438 props->setBoolValue(
"controls/flols/cut-lights",
false);
439 props->setBoolValue(
"controls/flols/wave-off-lights",
false);
440 props->setBoolValue(
"controls/flols/wave-off-lights-emergency",
false);
441 props->setBoolValue(
"controls/flols/cond-datum-lights",
true);
442 props->setBoolValue(
"controls/crew",
false);
443 props->setStringValue(
"navaids/tacan/channel-ID", _TACAN_channel_id.c_str());
444 props->setStringValue(
"sign", _sign.c_str());
445 std::string island_texture =
"island_" + _sign +
".jpg";
446 props->setStringValue(
"island_texture", island_texture.c_str());
447 props->setBoolValue(
"controls/lighting/deck-lights",
false);
448 props->setDoubleValue(
"controls/lighting/flood-lights-red-norm", 0);
450 _flols_x_node =
props->getNode(
"position/flols-x",
true);
451 _flols_y_node =
props->getNode(
"position/flols-y",
true);
452 _flols_z_node =
props->getNode(
"position/flols-z",
true);
454 _view_position_lat_deg_node =
props->getNode(
"position/view-position-lat",
true);
455 _view_position_lon_deg_node =
props->getNode(
"position/view-position-lon",
true);
456 _view_position_alt_ft_node =
props->getNode(
"position/view-position-alt",
true);
460 for (
const auto& ppos : _ppositions) {
461 if (ppos.name !=
"")
props->addChild(
"parking-pos")->setStringValue(
"name", ppos.name);
466 double& hdng, SGVec3d& uvw)
470 for (
const auto& ppos : _ppositions) {
472 if (ppos.name ==
id ||
id.empty()) {
474 geodPos = SGGeod::fromCart(cartPos);
475 hdng =
hdg + ppos.heading_deg;
476 double shdng = sin(ppos.heading_deg * SGD_DEGREES_TO_RADIANS);
477 double chdng = cos(ppos.heading_deg * SGD_DEGREES_TO_RADIANS);
490 geodPos = SGGeod::fromCart(cartPos);
494 heading =
hdg + _flolsHeadingOffsetDeg;
501 return _flolsApproachAngle;
508 _wind_from_deg = _surface_wind_from_deg_node->getDoubleValue();
509 _wind_speed_kts = _surface_wind_speed_node->getDoubleValue();
512 double wind_speed_from_north_kts = cos( _wind_from_deg / SGD_RADIANS_TO_DEGREES )* _wind_speed_kts ;
513 double wind_speed_from_east_kts = sin( _wind_from_deg / SGD_RADIANS_TO_DEGREES )* _wind_speed_kts ;
516 double speed_north_kts = cos(
hdg / SGD_RADIANS_TO_DEGREES )*
speed ;
517 double speed_east_kts = sin(
hdg / SGD_RADIANS_TO_DEGREES )*
speed ;
520 double rel_wind_speed_from_east_kts = wind_speed_from_east_kts + speed_east_kts;
521 double rel_wind_speed_from_north_kts = wind_speed_from_north_kts + speed_north_kts;
524 _rel_wind_speed_kts = sqrt((rel_wind_speed_from_east_kts * rel_wind_speed_from_east_kts)
525 + (rel_wind_speed_from_north_kts * rel_wind_speed_from_north_kts));
528 _rel_wind_from_deg = SGMiscd::rad2deg(atan2(rel_wind_speed_from_east_kts, rel_wind_speed_from_north_kts));
531 _rel_wind = _rel_wind_from_deg -
hdg;
532 SG_NORMALIZE_RANGE(_rel_wind, -180.0, 180.0);
551 if (_wind_speed_kts < 3){
573 if (_wind_speed_kts < 3){
575 }
else if (_rel_wind < -9 && _rel_wind >= -180){
577 }
else if (_rel_wind > -7 && _rel_wind < 45){
579 }
else if (_rel_wind >=45 && _rel_wind < 180){
606 double course, distance, az2;
609 geo_inverse_wgs_84(
pos, _mOpBoxPos, &course, &az2, &distance);
611 distance *= SG_METER_TO_NM;
628 if ( _max_lat == 0 && _min_lat == 0 && _max_lon == 0 && _min_lon == 0) {
629 SG_LOG(SG_AI, SG_DEBUG,
"AICarrier: No Operating Box defined" );
633 if (_mOpBoxPos.getLatitudeDeg() >= 0) {
634 if (
pos.getLatitudeDeg() >= _mOpBoxPos.getLatitudeDeg() + _max_lat)
637 if (
pos.getLatitudeDeg() <= _mOpBoxPos.getLatitudeDeg() - _min_lat)
641 if (
pos.getLatitudeDeg() <= _mOpBoxPos.getLatitudeDeg() - _max_lat)
644 if (
pos.getLatitudeDeg() >= _mOpBoxPos.getLatitudeDeg() + _min_lat)
648 if (_mOpBoxPos.getLongitudeDeg() >=0) {
649 if (
pos.getLongitudeDeg() >= _mOpBoxPos.getLongitudeDeg() + _max_lon)
652 if (
pos.getLongitudeDeg() <= _mOpBoxPos.getLongitudeDeg() - _min_lon)
656 if (
pos.getLongitudeDeg() <= _mOpBoxPos.getLongitudeDeg() - _max_lon)
659 if (
pos.getLongitudeDeg() >= _mOpBoxPos.getLongitudeDeg() + _min_lon)
668bool FGAICarrier::InToWind() {
671 if ( fabs(_rel_wind) < 10 ){
679void FGAICarrier::UpdateElevator(
double dt) {
683 if ((_elevators && _elevator_pos_norm >= 1 ) || (!_elevators && _elevator_pos_norm <= 0 ))
688 step = dt / _elevator_transition_time;
692 step = -dt / _elevator_transition_time;
697 _elevator_pos_norm_raw += step;
700 _elevator_pos_norm = (_elevator_pos_norm_raw * _elevator_time_constant) + (_elevator_pos_norm * (1 - _elevator_time_constant));
703 if (_elevator_pos_norm_raw >= 1.0) {
704 _elevator_pos_norm_raw = 1.0;
705 }
else if (_elevator_pos_norm_raw <= 0.0) {
706 _elevator_pos_norm_raw = 0.0;
713void FGAICarrier::UpdateJBD(
double dt) {
715 const std::string launchbar_state = _launchbar_state_node->getStringValue();
718 if (launchbar_state ==
"Engaged"){
724 if ((_jbd && _jbd_elevator_pos_norm >= 1 ) || ( !_jbd && _jbd_elevator_pos_norm <= 0 )){
730 step = dt / _jbd_transition_time;
734 step = -dt / _jbd_transition_time;
740 _jbd_elevator_pos_norm_raw += step;
743 _jbd_elevator_pos_norm = (_jbd_elevator_pos_norm_raw * _jbd_time_constant) + (_jbd_elevator_pos_norm * (1 - _jbd_time_constant));
746 if (_jbd_elevator_pos_norm >= 1.0) {
747 _jbd_elevator_pos_norm = 1.0;
748 }
else if (_jbd_elevator_pos_norm <= 0.0) {
749 _jbd_elevator_pos_norm = 0.0;
763 for (
auto s :
fgGetNode(
"/sim/ai/scenarios")->getChildren(
"scenario")) {
764 auto carriers = s->getChildren(
"carrier");
765 auto it = std::find_if(carriers.begin(), carriers.end(),
766 [namePennant] (
const SGPropertyNode* n)
769 for (auto nameChild : n->getChildren(
"name")) {
770 if (nameChild->getStringValue() == namePennant) return true;
774 if (it == carriers.end()) {
779 fgGetNode(
"/sim/ai/")->addChild(
"scenario")->setStringValue(s->getStringValue(
"id"));
782 SGGeod geod = SGGeod::fromDeg((*it)->getDoubleValue(
"longitude"),
783 (*it)->getDoubleValue(
"latitude"));
784 return std::make_pair(
true, geod);
787 return std::make_pair(
false, SGGeod());
797 for (
const auto& aiObject : aiManager->get_ai_list()) {
799 SGSharedPtr<FGAICarrier> c =
static_cast<FGAICarrier*
>(aiObject.get());
800 if ((c->_sign == namePennant) || (c->_getName() == namePennant)) {
811 for (
auto c : xmlNode->getChildren(
"entry")) {
812 if (c->getStringValue(
"type") != std::string(
"carrier"))
815 const std::string
name = c->getStringValue(
"name");
816 const std::string pennant = c->getStringValue(
"pennant-number");
817 if (
name.empty() && pennant.empty()) {
821 SGPropertyNode_ptr carrierNode = scenario->addChild(
"carrier");
824 carrierNode->setDoubleValue(
"longitude", c->getDoubleValue(
"longitude"));
825 carrierNode->setDoubleValue(
"latitude", c->getDoubleValue(
"latitude"));
828 carrierNode->setStringValue(
"description", c->getStringValue(
"description"));
832 if (!
name.empty()) carrierNode->addChild(
"name")->setStringValue(
name);
833 if (!pennant.empty()) {
834 carrierNode->addChild(
"name")->setStringValue(pennant);
835 carrierNode->addChild(
"pennant-number")->setStringValue(pennant);
839 for (
auto p : c->getChildren(
"parking-pos")) {
840 carrierNode->addChild(
"parking-pos")->setStringValue(
p->getStringValue(
"name"));
847 auto nctn = dynamic_pointer_cast<NearestCarrierToNotification>(n);
850 if (!nctn->GetCarrier() || nctn->GetDistanceMeters() > nctn->GetDistanceToMeters(
pos)) {
851 nctn->SetCarrier(
this, &
pos);
852 nctn->SetViewPositionLatNode(_view_position_lat_deg_node);
853 nctn->SetViewPositionLonNode(_view_position_lon_deg_node);
854 nctn->SetViewPositionAltNode(_view_position_alt_ft_node);
855 nctn->SetDeckheight(_deck_altitude_ft);
856 nctn->SetHeading(
hdg);
857 nctn->SetVckts(
speed);
858 nctn->SetCarrierIdent(this->
_getName());
860 return simgear::Emesary::ReceiptStatus::OK;
862 return simgear::Emesary::ReceiptStatus::NotProcessed;
SGPropertyNode * getPositionFromNode(SGPropertyNode *scFileNode, const std::string &key, SGVec3d &position)
void setCallSign(const std::string &)
SGVec3d getCartPosAt(const SGVec3d &off) const
double _getHeading() const
void setRadius(double radius)
const char * _getName() const
void tie(const char *aRelPath, const SGRawValue< T > &aRawValue)
Tied-properties helper, record nodes which are tied for easy un-tie-ing.
void setMaxLat(double deg)
static std::pair< bool, SGGeod > initialPositionForCarrier(const std::string &namePennant)
void setAIControl(bool c)
static SGSharedPtr< FGAICarrier > findCarrierByNameOrPennant(const std::string &namePennant)
type-safe wrapper around AIManager::getObjectFromProperty
void setTACANChannelID(const std::string &)
void setMinLong(double deg)
void setWind_from_east(double fps)
bool getParkPosition(const std::string &id, SGGeod &geodPos, double &hdng, SGVec3d &uvw)
void setMinLat(double deg)
bool init(ModelSearchOrder searchOrder) override
void setSign(const std::string &)
double getFLOLFSGlidepathAngleDeg() const
void setMaxLong(double deg)
void setDeckAltitudeFt(const double altitude_feet)
void setMPControl(bool c)
void setWind_from_north(double fps)
void UpdateWind(double dt)
bool getFLOLSPositionHeading(SGGeod &pos, double &heading) const
virtual simgear::Emesary::ReceiptStatus Receive(simgear::Emesary::INotificationPtr n) override
void readFromScenario(SGPropertyNode *scFileNode) override
static void extractCarriersFromScenario(SGPropertyNode_ptr xmlNode, SGPropertyNode_ptr scenario)
for a given scenario node, check for carriers within, and write nodes with names, pennants and initia...
static void registerScenarios(SGPropertyNode_ptr root={})
Static helper to register scenarios.
void readFromScenario(SGPropertyNode *scFileNode) override
void update(double dt) override
void TurnTo(double heading)
void AccelTo(double speed)
FGAIShip(object_type ot=object_type::otShip)
bool init(ModelSearchOrder searchOrder) override
SGVec3d get_ownship_reference_position_cart() const
int fgGetInt(const char *name, int defaultValue)
Get an int value for a property.
SGPropertyNode * fgGetNode(const char *path, bool create)
Get a property node.