17#include <simgear/io/iostreams/sgstream.hxx>
18#include <simgear/structure/exception.hxx>
19#include <simgear/timing/sg_time.hxx>
44 _performance(nullptr),
45 csvFile{std::make_unique<sg_ofstream>()}
49 groundOffset = trafficRef->getGroundOffset();
58 prevController =
nullptr;
59 towerController =
nullptr;
67 groundTargetSpeed = 0;
74 headingChangeRate = 0.0;
79 prev_dist_to_go = 0.0;
84 _performance = perfDB->getDefaultPerformance();
86 SG_LOG(SG_AI, SG_DEV_ALERT,
"no performance DB loaded");
92 trackCache.remainingLength = 0;
93 trackCache.startWptName =
"-";
95 tcasThreatNode =
props->getNode(
"tcas/threat-level",
true);
96 tcasRANode =
props->getNode(
"tcas/ra-sense",
true);
100void FGAIAircraft::lazyInitControlsNodes()
102 _controlsLateralModeNode =
props->getNode(
"controls/flight/lateral-mode",
true);
103 _controlsVerticalModeNode =
props->getNode(
"controls/flight/vertical-mode",
true);
104 _controlsTargetHeadingNode =
props->getNode(
"controls/flight/target-hdg",
true);
105 _controlsTargetRollNode =
props->getNode(
"controls/flight/target-roll",
true);
106 _controlsTargetAltitude =
props->getNode(
"controls/flight/target-alt",
true);
107 _controlsTargetPitch =
props->getNode(
"controls/flight/target-pitch",
true);
108 _controlsTargetSpeed =
props->getNode(
"controls/flight/target-spd",
true);
118 SG_LOG(SG_AI, SG_ALERT,
"Destruction of AIAircraft which was not unbound");
130 setPerformance(
"", scFileNode->getStringValue(
"class",
"jet_transport"));
132 scFileNode->getBoolValue(
"repeat",
false));
133 setCallSign(scFileNode->getStringValue(
"callsign"));
142 tie(
"transponder-id",
143 SGRawValueMethods<FGAIAircraft, const char*>(*
this,
144 &FGAIAircraft::_getTransponderCode));
152 if (tracked && !csvFile->is_open()) {
155 snprintf(fname,
sizeof(fname),
"%s_%ld.csv",
getCallSign().c_str(), t);
156 SGPath
p =
globals->get_download_dir() / fname;
160 if (tracked && csvFile->is_open()) {
175 _performance = perfDB->getDataFor(acType, acClass);
179 SG_LOG(SG_AI, SG_DEV_ALERT,
"no AI performance data found for: " << acType <<
"/" << acClass);
190 const bool isUserAircraft = (
manager ==
nullptr);
192 bool flightplanActive =
true;
196 if (!isUserAircraft) {
197 bool outOfSight =
false;
198 updatePrimaryTargetValues(dt, flightplanActive, outOfSight);
203 updateUserFlightPlan(dt);
206 if (!flightplanActive) {
207 groundTargetSpeed = 0;
210 handleATCRequests(dt);
211 updateSecondaryTargetValues(dt);
212 updateActualState(dt);
214 updateModelProperties(dt);
217 if (!isUserAircraft) {
227 if (!isStationary()) {
228 _needsGroundElevation =
true;
248void FGAIAircraft::YawTo(
double angle) {
263 const double headingDiff = SGMiscd::normalizePeriodic(-180, 180, heading -
tgt_heading);
265 if (fabs(heading) < 0.1 && fabs(headingDiff) > 1) {
274double FGAIAircraft::sign(
double x)
285 if (flightplan.empty()) {
290 std::unique_ptr<FGAIFlightPlan> plan(
new FGAIFlightPlan(flightplan));
291 if (plan->isValidPlan()) {
292 plan->setRepeat(repeat);
295 SG_LOG(SG_AI, SG_WARN,
"setFlightPlan: invalid flightplan specified:" << flightplan);
308 int nextTurnAngle = 0;
310 prev =
fp->getPreviousWaypoint();
311 curr =
fp->getCurrentWaypoint();
312 next =
fp->getNextWaypoint();
313 nextTurnAngle =
fp->getNextTurnAngle();
320 if (!prev || repositioned) {
322 handleFirstWaypoint();
324 SG_LOG(SG_AI, SG_WARN,
getCallSign() <<
" didn't have a valid flightplan and was killed");
329 if (!fpExecutable(now)) {
334 double distanceToDescent;
336 if (
fp->getLastWaypoint() ==
fp->getNextWaypoint() &&
337 reachedEndOfCruise(distanceToDescent)) {
342 fp->IncrementWaypoint(
false);
343 fp->IncrementWaypoint(
false);
344 prev =
fp->getPreviousWaypoint();
346 curr =
fp->getCurrentWaypoint();
348 next =
fp->getNextWaypoint();
362 if (!leadPointReached(curr, next, nextTurnAngle)) {
363 controlHeading(curr,
nullptr);
364 controlSpeed(curr, next);
367 SG_LOG(SG_AI, SG_BULK,
getCallSign() <<
"(" <<
getID() <<
") Flightplan ended");
368 if (
fp->getRepeat()) {
380 SG_LOG(SG_AI, SG_BULK,
"Set tgt_heading to " <<
tgt_heading);
384 fp->IncrementWaypoint(trafficRef != 0);
385 if (((!(
fp->getNextWaypoint()))) && (trafficRef != 0)) {
392 prev =
fp->getPreviousWaypoint();
396 curr =
fp->getCurrentWaypoint();
400 next =
fp->getNextWaypoint();
408 if (!aiTrafficVisible()) {
413 if (!handleAirportEndPoints(prev, now)) {
420 props->getChild(
"arrival-time-sec", 0,
true)->setIntValue(
fp->getArrivalTime());
424 if (next && !curr->
contains(
"END") && !curr->
contains(
"PushBackPointlegend")) {
425 SG_LOG(SG_AI, SG_BULK,
getCallSign() <<
"(" <<
getID() <<
") Setting Leaddistance");
435 double dist_m =
fp->getDistanceToGo(
pos.getLatitudeDeg(),
pos.getLongitudeDeg(), curr);
450 double dist_m =
fp->getDistanceToGo(
pos.getLatitudeDeg(),
pos.getLongitudeDeg(), curr);
465 double dist_m =
fp->getDistanceToGo(
pos.getLatitudeDeg(),
pos.getLongitudeDeg(), curr);
475 hdg_lock = alt_lock =
true;
483 double vert_m = vert_ft * SG_FEET_TO_METER;
484 double speedMs = (
speed * SG_NM_TO_METER) / 3600;
487 vs = ((vert_m) / dist_m) * speedMs;
490 vs *= (SG_METER_TO_FEET * 60);
496 if (controller &&
getDie()) {
498 controller->signOff(
getID());
501 controller =
nullptr;
502 prevController =
nullptr;
503 towerController =
nullptr;
512 const double headingDiff = SGMiscd::normalizePeriodic(-180, 180,
hdg - azimuth);
514 SG_LOG(SG_AI, SG_BULK,
getCallSign() <<
" blocked by " << other->
getCallSign() << azimuth <<
" Heading " <<
hdg <<
" Diff" << headingDiff);
519void FGAIAircraft::assertSpeed(
double speed)
523 <<
"Previous waypoint " <<
fp->getPreviousWaypoint()->getName() <<
" "
525 <<
"Leg " <<
fp->getLeg() <<
" "
526 <<
"target_speed << " <<
tgt_speed <<
" "
527 <<
"speedFraction << " << speedFraction <<
" "
528 <<
"Current speed << " <<
speed <<
" ");
536 if (tcasThreatNode && tcasThreatNode->getIntValue() == 3) {
537 const int RASense = tcasRANode->getIntValue();
538 if ((RASense > 0) && (
tgt_vs < 4000)) {
541 }
else if (RASense < 0) {
556void FGAIAircraft::initializeFlightPlan() {
560const char* FGAIAircraft::_getTransponderCode()
const
562 return transponderCode.c_str();
570 const int leg =
fp->getLeg();
572 FGAirport* oldArr = trafficRef->getArrivalAirport();
573 if (!trafficRef->next()) {
574 SG_LOG(SG_AI, SG_BULK,
getCallSign() <<
"(" <<
getID() <<
") No following flight killing");
579 FGAirport* dep = trafficRef->getDepartureAirport();
584 repositioned =
false;
591 FGAirport* dep = trafficRef->getDepartureAirport();
592 FGAirport* arr = trafficRef->getArrivalAirport();
594 SG_LOG(SG_AI, SG_WARN,
getCallSign() <<
"(" <<
getID() <<
") No dep/arr for fp ");
597 double cruiseAlt = trafficRef->getCruiseAlt() * 100;
599 int nextLeg = determineNextLeg(leg);
601 SG_LOG(SG_AI, SG_DEBUG,
getCallSign() <<
"(" <<
getID() <<
") |Loading Leg:" << leg <<
" Next: " << nextLeg);
602 bool ok =
fp->create(
this,
607 trafficRef->getSpeed(),
611 trafficRef->getRadius(),
612 trafficRef->getFlightType(),
618 SG_LOG(SG_AI, SG_WARN,
getCallSign() <<
"(" <<
getID() <<
") Failed to create waypoints for leg:" << leg + 1);
634 if (!needGroundElevation())
638 if (dt_elev_count < (3.0) + (rand() % 10))
645 double visibility_meters =
fgGetDouble(
"/environment/visibility-m");
646 if (SGGeodesy::distanceM(
globals->get_view_position(),
pos) > visibility_meters) {
650 double range = 500.0;
655 if (isStationary()) {
657 _needsGroundElevation =
false;
684 const bool isUserAircraft = (
manager ==
nullptr);
686 if ( isUserAircraft &&
globals->get_subsystem<
FDMShell>()->is_suspended()) {
690 int leg =
fp->getLeg();
691 if (!
fp->getCurrentWaypoint()) {
694 throw sg_exception(
"bad AI flight plan. No current WP");
705 if (trafficRef->getDepartureAirport()->getDynamics())
706 controller = trafficRef->getDepartureAirport()->getDynamics()->getStartupController();
709 if (trafficRef->getDepartureAirport()->getDynamics()->getGroundController()->exists())
710 controller = trafficRef->getDepartureAirport()->getDynamics()->getGroundController();
713 if (trafficRef->getDepartureAirport()->getDynamics()) {
714 controller = trafficRef->getDepartureAirport()->getDynamics()->getTowerController();
715 if (towerController) {
716 SG_LOG(SG_AI, SG_BULK,
" : " << controller->getName() <<
"#" << towerController->getName() << (controller != towerController));
718 towerController =
nullptr;
720 SG_LOG(SG_AI, SG_BULK,
"Error: Could not find Dynamics at airport : " << trafficRef->getDepartureAirport()->getId());
725 if (mgr && mgr->getEnRouteController()) {
726 controller = mgr->getEnRouteController();
731 if (trafficRef->getArrivalAirport()->getDynamics()) {
732 controller = trafficRef->getArrivalAirport()->getDynamics()->getApproachController();
736 if (trafficRef->getArrivalAirport()->getDynamics()) {
737 controller = trafficRef->getArrivalAirport()->getDynamics()->getTowerController();
741 if (trafficRef->getArrivalAirport()->getDynamics()->getGroundController()->exists()) {
742 controller = trafficRef->getArrivalAirport()->getDynamics()->getGroundController();
744 SG_LOG(SG_ATC, SG_ALERT, trafficRef->getArrivalAirport()->getId() <<
" doesn't have a groundcontroller" );
749 SG_LOG(SG_AI, SG_BULK,
"Will be signing off from " << controller->getName());
750 controller->signOff(
getID());
752 SG_LOG(SG_AI, SG_BULK,
"Controller was null");
754 controller =
nullptr;
757 SG_LOG(SG_AI, SG_ALERT,
"AILeg " << leg <<
" not covered by a controller type");
758 if (prevController) {
759 SG_LOG(SG_AI, SG_BULK,
"Will be signing off from " << prevController->getName());
761 controller =
nullptr;
765 if ((controller != prevController) && prevController && !
getDie()) {
767 prevController->announcePosition(
getID(),
fp.get(),
fp->getCurrentWaypoint()->getRouteIndex(),
769 trafficRef->getRadius(), leg,
this);
770 if (controller!=
nullptr) {
771 SG_LOG(SG_AI, SG_DEBUG,
"Handing over " << this->
getCallSign() <<
"(" << this->
getID() <<
") to " << controller->getName());
772 controller->handover(prevController->getRecord(
getID()), leg);
775 prevController->signOff(
getID());
777 prevController = controller;
779 controller->announcePosition(
getID(),
fp.get(),
fp->getCurrentWaypoint()->getRouteIndex(),
781 trafficRef->getRadius(), leg,
this);
785 SG_LOG(SG_AI, SG_ALERT,
"Can't announcePosition " << this->
getCallSign() <<
" no controller on Leg " <<
fp->getLeg());
822 groundTargetSpeed = 0.0;
834 AccelTo(
fp->getPreviousWaypoint()->getSpeed());
850void FGAIAircraft::handleFirstWaypoint()
853 bool eraseWaypoints = trafficRef ? true :
false;
862 fp->IncrementWaypoint(eraseWaypoints);
863 if (!(
fp->getNextWaypoint()) && trafficRef) {
870 prev =
fp->getPreviousWaypoint();
872 curr =
fp->getCurrentWaypoint();
874 next =
fp->getNextWaypoint();
881 if (
fp->getLeg() == 1) {
899 if (next && !curr->
contains(
"END") && !curr->
contains(
"PushBackPointlegend")) {
908 double dist_m =
fp->getDistanceToGo(
pos.getLatitudeDeg(),
pos.getLongitudeDeg(), curr);
916 alt_lock = hdg_lock =
true;
922 _needsGroundElevation =
true;
937bool FGAIAircraft::fpExecutable(time_t now)
939 double rand_exec_time = (rand() % 100) / 100;
940 return (dt_count > (0.1 + rand_exec_time)) && (
fp->isActive(now));
954 double dist_to_go_m =
fp->getDistanceToGo(
pos.getLatitudeDeg(),
pos.getLongitudeDeg(), curr);
956 double lead_distance_m =
fp->getLeadDistance() * SG_FEET_TO_METER;
957 const double arrivalDist = fabs(10.0 *
fp->getCurrentWaypoint()->getSpeed());
959 if ((dist_to_go_m < arrivalDist) && (
speed < 0) && (
tgt_speed < 0) &&
fp->getCurrentWaypoint()->contains(
"PushBackPoint")) {
961 tgt_speed = -std::sqrt((pow(arrivalDist, 2) - pow(arrivalDist - dist_to_go_m, 2)));
968 if (
fp->getPreviousWaypoint()->getSpeed() <
tgt_speed) {
969 SG_LOG(SG_AI, SG_BULK,
getCallSign() <<
"(" <<
getID() <<
") Set speed of WP from " <<
fp->getPreviousWaypoint()->getSpeed() <<
" to " <<
tgt_speed);
974 if ((dist_to_go_m < arrivalDist) && (
speed > 0) && (
tgt_speed > 0) &&
fp->getCurrentWaypoint()->contains(
"END")) {
981 if (
fp->getPreviousWaypoint()->getSpeed() <
tgt_speed) {
982 SG_LOG(SG_AI, SG_BULK,
getCallSign() <<
"(" <<
getID() <<
") Set speed of WP from " <<
fp->getPreviousWaypoint()->getSpeed() <<
" to " <<
tgt_speed);
987 if (lead_distance_m < fabs(2 *
speed) * SG_FEET_TO_METER) {
989 SG_LOG(SG_AI, SG_BULK,
getCallSign() <<
"(" <<
getID() <<
") Set lead_distance_m due to speed " << lead_distance_m <<
" to " << fabs(2 *
speed) * SG_FEET_TO_METER);
990 lead_distance_m = fabs(2 *
speed) * SG_FEET_TO_METER;
991 fp->setLeadDistance(lead_distance_m * SG_METER_TO_FEET);
1001 if (
onGround() && fabs(nextTurnAngle) > 50) {
1003 const int multiplicator = 4;
1004 SG_LOG(SG_AI, SG_BULK,
getCallSign() <<
"(" <<
getID() <<
") Set lead_distance_m due to next turn angle " << lead_distance_m <<
" to " << fabs(multiplicator *
speed) * SG_FEET_TO_METER <<
" dist_to_go_m " << dist_to_go_m <<
" Next turn angle : " << fabs(nextTurnAngle));
1005 lead_distance_m = fabs(multiplicator *
speed) * SG_FEET_TO_METER;
1006 fp->setLeadDistance(lead_distance_m * SG_METER_TO_FEET);
1010 if (minBearing < 10) {
1013 if ((minBearing < 360.0) && (minBearing > 10.0)) {
1014 speedFraction = 0.5 + (cos(minBearing * SG_DEGREES_TO_RADIANS) * 0.5);
1016 speedFraction = 1.0;
1021 if (next &&
abs(nextTurnAngle) < 5) {
1022 double bearingTowardsCurrent =
fp->getBearing(this->
getGeodPos(), curr);
1023 double headingDiffCurrent = SGMiscd::normalizePeriodic(-180, 180,
hdg - bearingTowardsCurrent);
1024 double bearingTowardsNext =
fp->getBearing(this->
getGeodPos(), next);
1025 double headingDiffNext = SGMiscd::normalizePeriodic(-180, 180,
hdg - bearingTowardsNext);
1026 if (
abs(headingDiffCurrent) > 80 &&
speed > 0) {
1027 if (trafficRef !=
nullptr) {
1029 SG_LOG(SG_AI, SG_WARN,
getCallSign() <<
"(" <<
getID() <<
") possible missed WP at " << trafficRef->getDepartureAirport()->getId() <<
" " << curr->
getName());
1031 SG_LOG(SG_AI, SG_WARN,
getCallSign() <<
"(" <<
getID() <<
") possible missed WP at " << trafficRef->getArrivalAirport()->getId() <<
" " << curr->
getName() <<
" Leg " <<
fp->getLeg() <<
" Speed " <<
getSpeed());
1036 SG_LOG(SG_AI, SG_BULK,
getCallSign() <<
"(" <<
getID() <<
") headingDiffCurrent " << headingDiffCurrent <<
" headingDiffNext " << headingDiffNext);
1039 if ((dist_to_go_m < lead_distance_m) ||
1040 ((dist_to_go_m > prev_dist_to_go) && (
bearing > (minBearing * 1.1)))) {
1041 SG_LOG(SG_AI, SG_BULK,
getCallSign() <<
"(" <<
getID() <<
") Leadpoint reached Bearing : " <<
bearing <<
"\tNext Bearing : " << nextBearing <<
" Next Turn Angle : " << fabs(nextTurnAngle));
1043 speedFraction = 1.0;
1044 prev_dist_to_go = HUGE_VAL;
1047 if (prev_dist_to_go == dist_to_go_m
1048 && fabs(groundTargetSpeed) > 0
1049 && this->
atGate().empty()) {
1053 if (stuckCounter > AI_STUCK_LIMIT) {
1054 SG_LOG(SG_AI, SG_WARN,
getCallSign() <<
"(" <<
getID() <<
") Stuck flight killed on leg " <<
fp->getLeg());
1060 prev_dist_to_go = dist_to_go_m;
1066bool FGAIAircraft::aiTrafficVisible()
1068 SGVec3d cartPos = SGVec3d::fromGeod(
pos);
1084bool FGAIAircraft::handleAirportEndPoints(
FGAIWaypoint* prev, time_t now)
1086 using namespace std::string_literals;
1089 FGAirport* dep = trafficRef->getDepartureAirport();
1090 FGAirport* arr = trafficRef->getArrivalAirport();
1099 if (prev->
contains(
"PushBackPoint"s)) {
1101 fp->setGate(ParkingAssignment());
1105 int nextLeg = determineNextLeg(
fp->getLeg());
1106 while (nextLeg !=
fp->getLeg()) {
1110 if (prev->
contains(
"DepartureHold"s)) {
1121 time_t nextDeparture = trafficRef->getDepartureTime();
1123 if (nextDeparture < (now + 1200)) {
1124 nextDeparture = now + 1200;
1126 fp->setTime(nextDeparture);
1141 const double calc_bearing =
speed < 0 ? SGMiscd::normalizePeriodic(0, 360,
fp->getBearing(
pos, curr) + 180.0) :
fp->
getBearing(
pos, curr);
1144 if (next &&
speed > 0) {
1145 const double calcNextBearing =
fp->getBearing(
pos, next);
1147 double averageHeading = calc_bearing + (calcNextBearing - calc_bearing) / 2;
1148 averageHeading = SGMiscd::normalizePeriodic(0, 360, averageHeading);
1150 if (fabs(hdg_error) > 0.01) {
1154 SG_LOG(SG_AI, SG_WARN,
"calc_bearing is not a finite number : "
1156 SG_LOG(SG_AI, SG_WARN,
"waypoint name: '" << curr->
getName() <<
"'");
1162 if (fabs(hdg_error) > 0.01) {
1166 SG_LOG(SG_AI, SG_WARN,
"calc_bearing is not a finite number : "
1168 SG_LOG(SG_AI, SG_WARN,
"waypoint name: '" << curr->
getName() <<
"'");
1184 double speed_diff =
speed - prevSpeed;
1186 if (fabs(speed_diff) > 10) {
1203void FGAIAircraft::updatePrimaryTargetValues(
double dt,
bool& flightplanActive,
bool& aiOutOfSight)
1205 if (
fp &&
fp->isValidPlan())
1225 aiOutOfSight = !aiTrafficVisible();
1228 aiOutOfSight =
true;
1232 timeElapsed = now -
fp->getStartTime();
1233 flightplanActive =
fp->isActive(now);
1240 if (!_controlsLateralModeNode) {
1241 lazyInitControlsNodes();
1245 const std::string& lat_mode = _controlsLateralModeNode->getStringValue();
1246 if (lat_mode ==
"roll") {
1247 const double angle = _controlsTargetRollNode->getDoubleValue();
1250 const double angle = _controlsTargetHeadingNode->getDoubleValue();
1254 std::string vert_mode = _controlsVerticalModeNode->getStringValue();
1255 if (vert_mode ==
"alt") {
1256 const double alt = _controlsTargetAltitude->getDoubleValue();
1259 const double angle = _controlsTargetPitch->getDoubleValue();
1263 AccelTo(_controlsTargetSpeed->getDoubleValue());
1267void FGAIAircraft::updateHeading(
double dt)
1276 const double headingDiff = SGMiscd::normalizePeriodic(-180, 180,
hdg -
tgt_heading);
1279 groundTargetSpeed =
tgt_speed * cos(headingDiff * SG_DEGREES_TO_RADIANS);
1285 SG_LOG(SG_AI, SG_BULK,
"Oh dear " <<
_callsign <<
" might get stuck aka next point is behind us. Speed is " <<
speed);
1287 if (stuckCounter > AI_STUCK_LIMIT) {
1288 SG_LOG(SG_AI, SG_WARN,
"Stuck flight " <<
_callsign <<
" killed on leg " <<
fp->getLeg() <<
" because point behind");
1293 groundTargetSpeed = 0.21 * sign(
tgt_speed);
1298 if (fabs(headingDiff) > 30.0) {
1300 if (sign(headingChangeRate) == sign(headingDiff)) {
1302 headingChangeRate = 10.0 * dt * sign(headingDiff) * -1;
1304 headingChangeRate -= 10.0 * dt * sign(headingDiff);
1310 if ((headingDiff < headingError)) {
1311 if (headingChangeRate > 30)
1312 headingChangeRate = 30;
1313 else if (headingChangeRate < -30)
1314 headingChangeRate = -30;
1317 if (sign(headingChangeRate) == sign(headingDiff)) {
1319 headingChangeRate = 3 * dt * sign(headingDiff) * -1;
1321 headingChangeRate -= 3 * dt * sign(headingDiff);
1335 hdg += headingChangeRate * dt * sqrt(fabs(
speed) / 15);
1337 headingError = headingDiff;
1339 if (fabs(headingError) < 1.0) {
1343 if (fabs(
speed) > 1.0) {
1350 double dist_covered_ft =
speed * 1.686 * dt;
1351 double alpha = dist_covered_ft / turn_circum_ft * 360.0;
1354 while (
hdg > 360.0) {
1366void FGAIAircraft::updateBankAngleTarget()
1370 double bank_sense = 0.0;
1373 diff = fabs(diff - 360);
1375 double sum =
hdg + diff;
1383 if (diff < _performance->maximumBankAngle()) {
1386 tgt_roll = _performance->maximumBankAngle() * bank_sense;
1388 if ((fabs((
double)spinCounter) > 1) && (diff > _performance->maximumBankAngle())) {
1395void FGAIAircraft::updateVerticalSpeedTarget(
double dt)
1401 }
else if (alt_lock) {
1409 }
else if (
fp->getCurrentWaypoint()) {
1410 double vert_dist_ft =
fp->getCurrentWaypoint()->getCrossat() -
altitude_ft;
1411 double err_dist = 0;
1412 double dist_m =
fp->getDistanceToGo(
pos.getLatitudeDeg(),
pos.getLongitudeDeg(),
fp->getCurrentWaypoint());
1424void FGAIAircraft::updatePitchAngleTarget()
1442 if ((
fp->getLeg() < 3) && trafficRef) {
1443 if (
fp->getParkingGate()) {
1444 return fp->getParkingGate()->ident();
1448 static const std::string empty{};
1452int FGAIAircraft::determineNextLeg(
int leg)
1455 time_t now =
globals->get_time_params()->get_cur_time();
1457 SG_LOG(SG_AI, SG_DEBUG,
getCallSign() <<
"(" <<
getID() <<
") Entering holding pattern " << leg );
1468void FGAIAircraft::handleATCRequests(
double dt)
1476 controller->updateAircraftInformation(
getID(),
1484 if (towerController) {
1485 towerController->updateAircraftInformation(
getID(),
1494void FGAIAircraft::updateUserFlightPlan(
double dt)
1500 switch (
fp->getLeg()) {
1502 auto current =
fp->getCurrentWaypoint();
1503 auto last =
fp->getLastWaypoint();
1504 int legDistance = SGGeodesy::distanceM(current->getPos(), last->getPos());
1505 int currDist = SGGeodesy::distanceM(
getGeodPos(), current->getPos());
1506 int lastDist = SGGeodesy::distanceM(
getGeodPos(), last->getPos());
1507 if (currDist > legDistance) {
1508 SG_LOG(SG_ATC, SG_BULK,
"Signing off from Tower "
1509 <<
"\t currDist\t" << currDist <<
"\t legDistance\t" << legDistance <<
"\t" << lastDist <<
"\t" <<
getGeodPos().getLatitudeDeg() <<
"\t" <<
getGeodPos().getLongitudeDeg() <<
"\t" << current->getPos().getLatitudeDeg() <<
"\t" << current->getPos().getLongitudeDeg());
1521void FGAIAircraft::updateActualState(
double dt)
1525 double distance =
speed * SG_KT_TO_MPS * dt;
1526 pos = SGGeodesy::direct(
pos,
hdg, distance);
1529 speed = _performance->actualSpeed(
this, groundTargetSpeed, dt, holdPos);
1531 speed = _performance->actualSpeed(
this, (
tgt_speed * speedFraction), dt,
false);
1534 roll = _performance->actualBankAngle(
this,
tgt_roll, dt);
1540 vs_fps = _performance->actualVerticalSpeed(
this,
tgt_vs, dt) / 60;
1544void FGAIAircraft::updateSecondaryTargetValues(
double dt)
1547 updateBankAngleTarget();
1548 updateVerticalSpeedTarget(dt);
1549 updatePitchAngleTarget();
1554bool FGAIAircraft::reachedEndOfCruise(
double& distance)
1556 FGAIWaypoint* curr =
fp->getCurrentWaypoint();
1558 SG_LOG(SG_AI, SG_WARN,
"FGAIAircraft::reachedEndOfCruise: no current waypoint");
1564 if (curr->
getName() == std::string(
"BOD")) {
1566 if (!trafficRef->getArrivalAirport()) {
1567 SG_LOG(SG_AI, SG_WARN, trafficRef->getCallSign() <<
"FGAIAircraft::reachedEndOfCruise: no arrival airport");
1573 double dist =
fp->getDistanceToGo(
pos.getLatitudeDeg(),
pos.getLongitudeDeg(), curr);
1577 double verticalDistance = ((
altitude_ft - 2000.0) - trafficRef->getArrivalAirport()->getElevation()) * SG_FEET_TO_METER;
1578 double descentTimeNeeded = verticalDistance / descentRate;
1579 double distanceCoveredByDescent = descentSpeed * descentTimeNeeded;
1582 SG_LOG(SG_AI, SG_BULK,
"Checking for end of cruise stage for :" << trafficRef->getCallSign());
1583 SG_LOG(SG_AI, SG_BULK,
"Descent rate : " << descentRate);
1584 SG_LOG(SG_AI, SG_BULK,
"Descent speed : " << descentSpeed);
1585 SG_LOG(SG_AI, SG_BULK,
"VerticalDistance : " << verticalDistance <<
". Altitude : " <<
altitude_ft <<
". Elevation " << trafficRef->getArrivalAirport()->getElevation());
1586 SG_LOG(SG_AI, SG_BULK,
"DecentTimeNeeded : " << descentTimeNeeded);
1587 SG_LOG(SG_AI, SG_BULK,
"DistanceCovered : " << distanceCoveredByDescent);
1590 distance = distanceCoveredByDescent;
1591 if (dist < distanceCoveredByDescent) {
1592 SG_LOG(SG_AI, SG_BULK,
getCallSign() <<
"(" <<
getID() <<
") End Of Cruise");
1614 prev =
fp->getPreviousWaypoint();
1615 curr =
fp->getCurrentWaypoint();
1616 next =
fp->getNextWaypoint();
1618 if (curr ==
nullptr || next ==
nullptr) {
1619 SG_LOG(SG_AI, SG_WARN,
getCallSign() <<
"(" <<
getID() <<
") Repositioned without curr/next");
1636 double hdgDiff = fabs(
hdg - crse);
1638 hdgDiff = fabs(hdgDiff - 360);
1645 curr =
fp->getCurrentWaypoint();
1649 ((curr->
getName() != trackCache.startWptName) ||
1650 (wptName != trackCache.finalWptName))) {
1651 trackCache.remainingLength =
fp->checkTrackLength(wptName);
1652 trackCache.startWptName = curr->
getName();
1653 trackCache.finalWptName = wptName;
1655 double tracklength = trackCache.remainingLength;
1656 if (tracklength > 0.1) {
1657 tracklength +=
fp->getDistanceToGo(
pos.getLatitudeDeg(),
pos.getLongitudeDeg(), curr);
1661 time_t now =
globals->get_time_params()->get_cur_time();
1662 time_t arrivalTime =
fp->getArrivalTime();
1664 time_t ete = tracklength / ((
speed * SG_NM_TO_METER) / 3600.0);
1665 time_t secondsToGo = arrivalTime - now;
1667 SG_LOG(SG_AI, SG_BULK,
"Checking arrival time: ete " << ete <<
". Time to go : " << secondsToGo <<
". Track length = " << tracklength);
1669 return (ete - secondsToGo);
1676 departure =
globals->get_time_params()->get_cur_time();
1683 double delta = target - cur;
1684 double maxDelta = maxDeltaSec * dt;
1687 return (fabs(delta) < maxDelta) ? delta : copysign(maxDelta, delta);
1695void FGAIAircraft::updateModelProperties(
double dt)
1697 if ((!
fp) || (!
fp->getPreviousWaypoint())) {
1701 double targetGearPos =
fp->getPreviousWaypoint()->getGear_down() ? 1.0 : 0.0;
1704 if (gearPos != targetGearPos) {
1706 if (gearPos < 0.001) {
1708 }
else if (gearPos > 0.999) {
1714 double targetFlapsPos =
fp->getPreviousWaypoint()->getFlaps();
1717 if (flapsPos != targetFlapsPos) {
1719 if (flapsPos < 0.001) {
1721 }
else if (flapsPos > 0.999) {
1729 CabinLight(
fp->getPreviousWaypoint()->getCabinLight());
1732 NavLight(
fp->getPreviousWaypoint()->getNavLight());
1734 TaxiLight(
fp->getPreviousWaypoint()->getTaxiLight());
1743 (*o) <<
"Callsign\t";
1744 (*o) <<
"headingDiff\t";
1745 (*o) <<
"headingChangeRate\t";
1746 (*o) <<
"headingError\t";
1748 (*o) <<
"tgt_heading\t";
1749 (*o) <<
"tgt_speed\t";
1750 (*o) <<
"minBearing\t";
1751 (*o) <<
"speedFraction\t";
1752 (*o) <<
"groundOffset\t";
1754 (*o) <<
"groundTargetSpeed\t";
1755 (*o) <<
"getVerticalSpeedFPM\t";
1756 (*o) <<
"getTrueHeadingDeg\t";
1757 (*o) <<
"bearingToWP\t";
1763 (*o) <<
"Next Lat\t";
1764 (*o) <<
"Next Lon\t";
1765 (*o) <<
"Departuretime\t";
1767 (*o) <<
"Startup diff\t";
1768 (*o) <<
"Departure\t";
1769 (*o) <<
"Arrival\t";
1770 (*o) <<
"dist_to_go_m\t";
1771 (*o) <<
"leadInAngle\t";
1772 (*o) <<
"Leaddistance\t";
1775 (*o) <<
"no_roll\t";
1777 (*o) <<
"repositioned\t";
1778 (*o) <<
"stuckCounter\t";
1779 (*o) <<
"blockerId\t";
1780 (*o) <<
"holdPos\t";
1786 const double headingDiff = SGMiscd::normalizePeriodic(-180, 180,
hdg -
tgt_heading);
1788 (*o) << lineIndex <<
"\t";
1789 (*o) << std::setprecision(12);
1790 (*o) << this->
getGeodPos().getLatitudeDeg() <<
"\t";
1791 (*o) << this->
getGeodPos().getLongitudeDeg() <<
"\t";
1792 (*o) << this->
getGeodPos().getElevationFt() <<
"\t";
1794 (*o) << headingDiff <<
"\t";
1795 (*o) << headingChangeRate <<
"\t";
1796 (*o) << headingError <<
"\t";
1797 (*o) <<
hdg <<
"\t";
1800 (*o) << minBearing <<
"\t";
1801 (*o) << speedFraction <<
"\t";
1802 (*o) << groundOffset <<
"\t";
1804 (*o) << round(this->
getSpeed()) <<
"\t";
1805 (*o) << groundTargetSpeed <<
"\t";
1813 (*o) <<
fp->getBearing(this->
getGeodPos(), currentWP) <<
"\t";
1814 (*o) << currentWP->
getName() <<
"\t";
1815 (*o) << currentWP->
getPos().getLatitudeDeg() <<
"\t";
1816 (*o) << currentWP->
getPos().getLongitudeDeg() <<
"\t";
1819 (*o) << nextWP->
getPos().getLatitudeDeg() <<
"\t";
1820 (*o) << nextWP->
getPos().getLongitudeDeg() <<
"\t";
1825 (*o) << SGGeodesy::distanceM(this->
getGeodPos(), currentWP->
getPos()) <<
"\t";
1826 (*o) <<
fp->getStartTime() <<
"\t";
1827 (*o) <<
globals->get_time_params()->get_cur_time() <<
"\t";
1828 (*o) <<
fp->getStartTime() -
globals->get_time_params()->get_cur_time() <<
"\t";
1829 (*o) << (
fp->departureAirport().get() ?
fp->departureAirport().get()->getId() :
"") <<
"\t";
1830 (*o) << (
fp->arrivalAirport().get() ?
fp->arrivalAirport().get()->getId() :
"") <<
"\t";
1832 double dist_to_go_m =
fp->getDistanceToGo(
pos.getLatitudeDeg(),
pos.getLongitudeDeg(), currentWP);
1833 (*o) << dist_to_go_m <<
"\t";
1835 double outbound =
fp->getBearing(currentWP, nextWP);
1836 double leadInAngle = fabs(inbound - outbound);
1837 if (leadInAngle > 180.0) leadInAngle = 360.0 - leadInAngle;
1838 (*o) << leadInAngle <<
"\t";
1841 (*o) <<
"No WP\t\t\t\t\t\t\t\t";
1843 if (
fp->isValidPlan()) {
1844 (*o) <<
fp->getLeadDistance() * SG_FEET_TO_METER <<
"\t";
1845 (*o) <<
fp->getLeg() <<
"\t";
1846 (*o) <<
fp->getNrOfWayPoints() <<
"\t";
1848 (*o) <<
"FP NotValid\t\t";
1852 (*o) <<
roll <<
"\t";
1853 (*o) << repositioned <<
"\t";
1854 (*o) << stuckCounter <<
"\t";
1855 (*o) << waitsForId <<
"\t";
1856 (*o) << holdPos <<
"\t";
1861std::string FGAIAircraft::getTimeString(
int timeOffset)
1866 rawtime = rawtime + timeOffset;
1867 tm* timeinfo = gmtime(&rawtime);
1868 strftime(ret, 11,
"%w/%H:%M:%S", timeinfo);
double limitRateOfChange(double cur, double target, double maxDeltaSec, double dt)
double fgIsFinite(double x)
constexpr double TRAFFIC_TO_AI_DIST_TO_DIE
Wrap an FDM implementation in a subsystem with standard semantics Notably, deal with the various case...
void dumpCSVHeader(const std::unique_ptr< sg_ofstream > &o)
void clearATCController()
void setPerformance(const std::string &acType, const std::string &perfString)
double getVerticalSpeedFPM() const
double getBearing(double crse)
Returns a normalised bearing.
PerformanceData * getPerformance()
FGAIAircraft(FGAISchedule *ref=0)
void dumpCSV(const std::unique_ptr< sg_ofstream > &o, int lineIndex)
void getGroundElev(double dt)
time_t checkForArrivalTime(const std::string &wptName)
FGAIFlightPlan * GetFlightPlan() const
bool loadNextLeg(double dist=0)
void TurnTo(double heading)
void ProcessFlightPlan(double dt, time_t now)
void processATC(const FGATCInstruction &instruction)
Process ATC instructions and report back.
void PitchTo(double angle)
void resetPositionFromFlightPlan()
void AccelTo(double speed)
FGAISchedule * getTrafficRef()
void RollTo(double angle)
void update(double dt) override
double calcVerticalSpeed(double vert_ft, double dist_m, double speed, double error)
bool isBlockedBy(FGAIAircraft *other)
void ClimbTo(double altitude)
void readFromScenario(SGPropertyNode *scFileNode) override
void announcePositionToController()
const std::string & atGate()
void setFlightPlan(const std::string &fp, bool repat=false)
void scheduleForATCTowerRunwayControl()
bool LandingLight() const
double SpeedBrakePos() const
FGAIBaseAircraft(object_type otype=object_type::otAircraft)
double SpoilerPos() const
void setSpeed(double speed_KTAS)
void setLatitude(double latitude)
virtual void readFromScenario(SGPropertyNode *scFileNode)
double getTrueHeadingDeg() const
const std::string & getCallSign() const
void setCallSign(const std::string &)
double _getLatitude() const
void setFlightPlan(std::unique_ptr< FGAIFlightPlan > f)
bool getGroundElevationM(const SGGeod &pos, double &elev, const simgear::BVHMaterial **material) const
SGGeod getGeodPos() const
void setAltitude(double altitude_ft)
virtual void update(double dt)
std::unique_ptr< FGAIFlightPlan > fp
double UpdateRadar(FGAIManager *manager)
double _getLongitude() const
void setLongitude(double longitude)
void setHeading(double heading)
ModelSearchOrder _searchOrder
void tie(const char *aRelPath, const SGRawValue< T > &aRawValue)
Tied-properties helper, record nodes which are tied for easy un-tie-ing.
FGAirport * getDepartureAirport()
bool contains(const std::string &name)
double getLatitude() const
const std::string & getName()
double getLongitude() const
double getAltitude() const
FGATCInstruction getInstruction(int id)
bool getChangeHeading() const
bool getChangeAltitude() const
bool getHoldPosition() const
int getWaitsForId() const
bool getChangeSpeed() const
double getHeading() const
bool getCheckForCircularWait() const
bool getHoldPattern() const
SGTime * get_time_params() const
SGVec3d get_aircraft_position_cart() const
std::string fgGetString(const char *name, const char *defaultValue)
Get a string value for a property.
double fgGetDouble(const char *name, double defaultValue)
Get a double value for a property.