15#include <simgear/constants.h>
16#include <simgear/debug/logstream.hxx>
17#include <simgear/io/iostreams/sgstream.hxx>
18#include <simgear/math/sg_geodesy.hxx>
19#include <simgear/misc/sg_path.hxx>
20#include <simgear/props/props.hxx>
21#include <simgear/props/props_io.hxx>
22#include <simgear/structure/exception.hxx>
23#include <simgear/timing/sg_time.hxx>
58 size_t found = name.find(target);
59 if (found == string::npos)
67 return pos.getLatitudeDeg();
72 return pos.getLongitudeDeg();
77 return pos.getElevationFt();
82 pos.setLatitudeDeg(lat);
87 pos.setLongitudeDeg(lon);
92 pos.setElevationFt(alt);
106 wpt_iterator = waypoints.begin();
118 isValid(parseProperties(filename))
132 const std::string&
p,
135 time_t remainingTime,
144 const string& fltType,
145 const string& acType,
146 const string& airline) : sid(NULL),
159 if (parseProperties(
p)) {
162 createWaypoints(ac, course, start, remainingTime, dep, arr, firstLeg, radius,
163 alt, lat, lon, speed, fltType, acType, airline);
176 time_t remainingTime,
185 const string& fltType,
186 const string& acType,
187 const string& airline)
189 time_t now =
globals->get_time_params()->get_cur_time();
190 time_t timeDiff = now - start;
193 if ((timeDiff > 60) && (timeDiff < 500))
195 else if ((timeDiff >= 500) && (timeDiff < 600))
197 else if ((timeDiff >= 600) && (timeDiff < 1000))
199 else if (timeDiff >= 1000) {
200 if (remainingTime > 2000) {
207 SG_LOG(SG_AI, SG_DEBUG, ac->
getTrafficRef()->
getCallSign() <<
"|Route from " << dep->
getId() <<
" to " << arr->
getId() <<
". Set leg to : " << leg <<
" " << remainingTime);
209 wpt_iterator = waypoints.begin();
211 isValid =
create(ac, dep, arr, leg, alt, speed, lat, lon,
212 firstLeg, radius, fltType, acType, airline, dist);
213 wpt_iterator = waypoints.begin();
216bool FGAIFlightPlan::parseProperties(
const std::string& filename)
236 readProperties(stream, &root);
237 }
catch (
const sg_exception& e) {
238 SG_LOG(SG_AI, SG_ALERT,
"Error reading AI flight plan: " << loc.asString() <<
" message:" << e.getFormattedMessage());
242 SGPropertyNode* node = root.getNode(
"flightplan");
244 SG_LOG(SG_AI, SG_ALERT,
"Error reading AI flight plan: " << loc.asString() <<
": no <flightplan> root element");
248 for (
int i = 0;
i < node->nChildren();
i++) {
250 SGPropertyNode* wpt_node = node->getChild(
i);
255 if (wpt_node->getDoubleValue(
"ktas", 0) < 1.0f) {
260 }
else if (wpt_node->getDoubleValue(
"alt", 0) > 10000.0f) {
265 }
else if (wpt_node->getBoolValue(
"on-ground",
false)) {
270 }
else if (wpt_node->getDoubleValue(
"alt", 0) < 3000.0f) {
282 wpt->
setName(wpt_node->getStringValue(
"name",
"END"));
283 wpt->
setLatitude(wpt_node->getDoubleValue(
"lat", 0));
285 wpt->
setAltitude(wpt_node->getDoubleValue(
"alt", 0));
286 wpt->
setSpeed(wpt_node->getDoubleValue(
"ktas", 0));
287 wpt->
setCrossat(wpt_node->getDoubleValue(
"crossat", -10000));
288 wpt->
setGear_down(wpt_node->getBoolValue(
"gear-down", gear));
289 wpt->
setFlaps(wpt_node->getBoolValue(
"flaps-down", flaps) ? 1.0 : 0.0);
290 wpt->
setSpoilers(wpt_node->getBoolValue(
"spoilers",
false) ? 1.0 : 0.0);
291 wpt->
setSpeedBrakes(wpt_node->getBoolValue(
"speedbrakes",
false) ? 1.0 : 0.0);
292 wpt->
setOn_ground(wpt_node->getBoolValue(
"on-ground",
false));
293 wpt->
setTime_sec(wpt_node->getDoubleValue(
"time-sec", 0));
294 wpt->
setTime(wpt_node->getStringValue(
"time",
""));
296 pushBackWaypoint(wpt);
300 if (!lastWp || lastWp->getName().compare(
"END") != 0) {
301 SG_LOG(SG_AI, SG_ALERT,
"FGAIFlightPlan::Flightplan (" + loc.asString() +
") missing END node");
306 wpt_iterator = waypoints.begin();
312 if (waypoints.empty())
315 return waypoints.back();
323 if (wpt_iterator == waypoints.begin()) {
326 wpt_vector_iterator prev = wpt_iterator;
333 if (wpt_iterator == waypoints.end())
335 return *wpt_iterator;
340 if (wpt_iterator == waypoints.end())
343 wpt_vector_iterator last = waypoints.end() - 1;
344 if (wpt_iterator == last) {
347 return *(wpt_iterator + 1);
353 return nextTurnAngle;
362 if (eraseWaypoints) {
363 if (wpt_iterator == waypoints.begin())
365 else if (!waypoints.empty()) {
366 delete *(waypoints.begin());
367 waypoints.erase(waypoints.begin());
368 wpt_iterator = waypoints.begin();
375 if (wpt_iterator == waypoints.end())
377 if (wpt_iterator == waypoints.begin())
379 if (wpt_iterator + 1 == waypoints.end())
381 if (waypoints.size() < 3)
386 int currentBearing = this->
getBearing(previousWP, currentWP);
387 int nextBearing = this->
getBearing(currentWP, nextWP);
389 nextTurnAngle = SGMiscd::normalizePeriodic(-180, 180, nextBearing - currentBearing);
392 nextTurnAngle += 180;
393 SG_LOG(SG_AI, SG_BULK,
"Add 180 to turn angle pushback end");
395 SG_LOG(SG_AI, SG_BULK,
"Calculated next turn angle " << nextTurnAngle <<
" " << previousWP->
getName() <<
" " << currentWP->
getName() <<
" Previous Speed " << previousWP->
getSpeed() <<
" Next Speed " << nextWP->
getSpeed());
406void FGAIFlightPlan::eraseLastWaypoint()
411 delete (waypoints.back());
412 waypoints.pop_back();
413 wpt_iterator = waypoints.begin();
420 return SGGeodesy::distanceM(SGGeod::fromDeg(lon, lat), wp->
getPos());
432 double turn_radius_m;
442 if (speed > 0 && speed < 0.5) {
444 SG_LOG(SG_AI, SG_BULK,
"Setting Leaddistance fixed " << (lead_distance_ft * SG_FEET_TO_METER));
448 double speed_mps = speed * SG_KT_TO_MPS;
450 turn_radius_m = ((360 / 30) * fabs(speed_mps)) / (2 *
M_PI);
452 turn_radius_m = 0.1911 * speed * speed;
455 double inbound = bearing;
457 leadInAngle = fabs(inbound - outbound);
458 if (leadInAngle > 180.0) leadInAngle = 360.0 - leadInAngle;
464 if ((
int)leadInAngle == 0) {
465 double lead_distance_m = fabs(2 * speed) * SG_FEET_TO_METER;
467 if (lead_distance_ft > 1000) {
468 SG_LOG(SG_AI, SG_BULK,
"Excessive leaddistance leadin 0 " << lead_distance_ft <<
" leadInAngle " << leadInAngle <<
" inbound " << inbound <<
" outbound " << outbound);
471 double lead_distance_m = turn_radius_m * tan((leadInAngle * SG_DEGREES_TO_RADIANS) / 2);
473 SG_LOG(SG_AI, SG_BULK,
"Setting Leaddistance " << (lead_distance_ft * SG_FEET_TO_METER) <<
" Turnradius " << turn_radius_m <<
" Speed " << speed_mps <<
" Half turn Angle " << (leadInAngle) / 2);
474 if (lead_distance_ft > 1000) {
475 SG_LOG(SG_AI, SG_BULK,
"Excessive leaddistance possible direction change " << lead_distance_ft <<
" leadInAngle " << leadInAngle <<
" inbound " << inbound <<
" outbound " << outbound <<
" at " << current->
getName());
494 lead_distance_ft = distance_ft;
495 if (lead_distance_ft > 10000) {
496 SG_LOG(SG_AI, SG_BULK,
"Excessive Leaddistance " << distance_ft);
503 return SGGeodesy::courseDeg(first->
getPos(), second->
getPos());
508 return SGGeodesy::courseDeg(aPos, wp->
getPos());
511void FGAIFlightPlan::deleteWaypoints()
513 for (wpt_vector_iterator
i = waypoints.begin();
i != waypoints.end(); ++
i)
516 wpt_iterator = waypoints.begin();
521void FGAIFlightPlan::resetWaypoints()
523 if (waypoints.begin() == waypoints.end())
527 FGAIWaypoint* wpt =
new FGAIWaypoint;
528 wpt_vector_iterator
i = waypoints.end();
531 wpt->
setPos((*i)->getPos());
544 SG_LOG(SG_AI, SG_DEBUG,
"Recycling waypoint " << wpt->
getName());
546 pushBackWaypoint(wpt);
551 pushBackWaypoint(wpt);
557 SG_LOG(SG_AI, SG_WARN,
"Null WPT added");
559 size_t pos = wpt_iterator - waypoints.begin();
560 if (waypoints.size() > 0) {
561 double dist = SGGeodesy::distanceM(waypoints.back()->getPos(), wpt->
getPos());
563 SG_LOG(SG_AI, SG_DEBUG,
"Double WP : \t" << wpt->
getName() <<
" not added ");
565 waypoints.push_back(wpt);
566 SG_LOG(SG_AI, SG_BULK,
"Added WP : \t" << std::setprecision(12) << wpt->
getName() <<
"\t" << wpt->
getPos() <<
"\t" << wpt->
getSpeed());
569 waypoints.push_back(wpt);
570 SG_LOG(SG_AI, SG_BULK,
"Added WP : \t" << std::setprecision(12) << wpt->
getName() <<
"\t" << wpt->
getPos() <<
"\t" << wpt->
getSpeed());
575 wpt_iterator = waypoints.begin() + pos;
581 wpt_iterator = waypoints.begin();
586 if ((
i > 0) && (
i < (
int)waypoints.size())) {
587 return waypoints[
i]->getRouteIndex();
595 double trackDistance = 0;
596 wpt_vector_iterator wptvec = waypoints.begin();
599 while ((wptvec != waypoints.end())) {
600 if (*wptvec !=
nullptr && (!((*wptvec)->contains(wptName)))) {
603 trackDistance += (*wptvec)->getTrackLength();
606 if (wptvec == waypoints.end()) {
609 return trackDistance;
614 while (waypoints.size() > number + 3) {
617 (waypoints.back())->setName((waypoints.back())->getName() + name);
627 return gate.parking();
649 return waypoints.
empty();
657time_t FGAIFlightPlan::calcArrivalTimes()
const
661 if (waypoints.cbegin() == waypoints.cend()) {
664 auto previousWP = (*waypoints.cbegin());
666 for(
const auto wp : waypoints) {
667 SGGeod lastPos = previousWP->getPos();
668 SGGeod currentPos = wp->getPos();
669 double dist_m = SGGeodesy::distanceM(lastPos, currentPos);
670 double speed_mps = wp->getSpeed() * SG_KT_TO_MPS;
671 double time_s = dist_m / speed_mps;
675 SG_LOG(SG_AI, SG_DEBUG,
"Runtime : " << runtime <<
" " << dist_m );
678 SG_LOG(SG_AI, SG_DEBUG,
"Runtime : " << runtime <<
" " <<
getLeg() );
SGSharedPtr< FGAirport > FGAirportRef
FGAISchedule * getTrafficRef()
void setGate(const ParkingAssignment &pka)
void IncrementWaypoint(bool erase)
void setLeadDistance(double speed, double bearing, FGAIWaypoint *current, FGAIWaypoint *next)
Set lead_distance_ft.
int getRouteIndex(int i) const
FGAIWaypoint * getNextWaypoint(void) const
FGParking * getParkingGate() const
FGAirportRef departureAirport() const
double getDistanceToGo(double lat, double lon, FGAIWaypoint *wp) const
static FGAIFlightPlan * createDummyUserPlan()
create a nearly empty FlightPlan for the user aircraft, based on the current position and route-manag...
FGAirportRef arrivalAirport() const
double checkTrackLength(const std::string &wptName) const
void shortenToFirst(unsigned int number, const std::string &name)
bool readFlightplan(const SGPath &file)
read a flight-plan from a file.
virtual ~FGAIFlightPlan()
bool create(FGAIAircraft *, FGAirport *dep, FGAirport *arr, int leg, double alt, double speed, double lat, double lon, bool firstLeg, double radius, const std::string &fltType, const std::string &aircraftType, const std::string &airline, double distance)
int getNextTurnAngle(void) const
FGAIWaypoint * getPreviousWaypoint(void) const
FGAIWaypoint * getCurrentWaypoint(void) const
FGAIWaypoint * getLastWaypoint() const
void addWaypoint(FGAIWaypoint *wpt)
double getBearing(FGAIWaypoint *previous, FGAIWaypoint *next) const
std::string getCallSign()
void setGear_down(bool grd)
void setAltitude(double alt)
void setCrossat(double val)
bool contains(const std::string &name)
void setName(const std::string &nam)
double getLatitude() const
void setStrobeLight(bool strobe)
void setOn_ground(bool grn)
const std::string & getName()
void setSpoilers(double val)
void setTime_sec(double ts)
void setFinished(bool fin)
void setLongitude(double lon)
void setTime(const std::string &tme)
void setLatitude(double lat)
void setSpeed(double spd)
void setBeaconLight(bool beacon)
void setLandingLight(bool ldg)
void setPos(const SGGeod &aPos)
void setPowerDownLights()
double getLongitude() const
void setFlaps(double val)
void setTaxiLight(bool taxi)
double getAltitude() const
void setSpeedBrakes(double val)
void setNavLight(bool nav)
const std::string & getId() const
SGPath findDataPath(const std::string &pathSuffix) const
Given a path suffix (eg 'Textures' or 'AI/Traffic'), find the first data directory which defines it.