14#include <simgear/compiler.h>
15#include <simgear/structure/SGWeakPtr.hxx>
27#include <simgear/debug/logstream.hxx>
28#include <simgear/misc/sg_path.hxx>
29#include <simgear/props/props.hxx>
30#include <simgear/structure/subsystem_mgr.hxx>
57 strongRef->releaseParking(
parking);
88 _sharedData->release();
102 _sharedData->retain();
108 if (_sharedData == aOther._sharedData) {
113 _sharedData->release();
116 _sharedData = aOther._sharedData;
118 _sharedData->retain();
125 _sharedData->release();
132 return (_sharedData != NULL);
137 return _sharedData ? _sharedData->parking.ptr() : NULL;
162 for (
auto c : m_cache) {
163 const double d = dist(cart, c);
175 SGVec3d cartAirportPos = m_airport->cart();
177 for (
auto ai : aiManager->get_ai_list()) {
178 const auto cart = ai->getCartPos();
181 if (dist(cartAirportPos, cart) > 20000) {
185 m_cache.push_back(cart);
190 bool m_populated =
false;
192 std::vector<SGVec3d> m_cache;
198 startupController(this),
199 towerController(this),
200 approachController(this),
201 groundController(this),
203 atisSequenceIndex(-1),
204 atisSequenceTimeStamp(0.0)
221 startupController.setAirportGroundRadar(groundRadar);
222 towerController.setAirportGroundRadar(groundRadar);
223 approachController.setAirportGroundRadar(groundRadar);
224 groundController.setAirportGroundRadar(groundRadar);
227FGParking* FGAirportDynamics::innerGetAvailableParking(
double radius,
const std::string& flType,
228 const std::string& airline,
229 bool skipEmptyAirlineCode)
234 for (
auto parking : parkings) {
239 if (parking->getRadius() < radius) {
243 if (!flType.empty() && (parking->getType() != flType)) {
247 if (skipEmptyAirlineCode && parking->getCodes().empty()) {
251 if (!airline.empty() && !parking->getCodes().empty()) {
252 if (parking->getCodes().find(airline, 0) == std::string::npos) {
257 if (nearCache.isAnythingNear(parking->cart(), parking->getRadius())) {
261 candidates.push_back(parking);
264 if (candidates.empty()) {
270 std::sort(candidates.begin(), candidates.end(),
272 return a->getRadius() < b->getRadius();
276 return candidates.front();
281 return std::find(
parent()->groundNetwork()->allParkings().begin(),
282 parent()->groundNetwork()->allParkings().end(),
292 const std::string& flType,
293 const std::string& acType,
294 const std::string& airline)
299 FGParking* result = innerGetAvailableParking(radius, flType, airline,
true);
305 result = innerGetAvailableParking(radius, flType, airline,
false);
311 result = innerGetAvailableParking(radius, flType, std::string(),
false);
318 FGParkingList::const_iterator it;
319 for (it = parkings.begin(); it != parkings.end(); ++it) {
320 if ((*it)->name() ==
name) {
331 auto it = std::find_if(parkings.begin(), parkings.end(), [
this,
name](
FGParkingRef parking) {
332 if (parking->name() != name)
335 return this->isParkingAvailable(parking);
338 if (it == parkings.end())
346 auto it = std::find_if(occupiedParkings.begin(), occupiedParkings.end(),
348 if (it != occupiedParkings.end()) {
360 occupiedParkings.insert(park);
366 return (occupiedParkings.find(parking) == occupiedParkings.end());
371 ParkingSet::iterator it = occupiedParkings.find(
id);
372 if (it == occupiedParkings.end()) {
376 occupiedParkings.erase(it);
381 bool mustBeAvailable;
394 if (!type.empty() && (park->getType() != type))
397 if (mustBeAvailable && !dynamics->isParkingAvailable(park)) {
410 auto it = std::remove_if(result.begin(), result.end(), pred);
411 result.erase(it, result.end());
422 double hdgDiff = (b->headingDeg() - a->headingDeg());
423 SG_NORMALIZE_RANGE(hdgDiff, -180.0, 180.0);
424 return (fabs(hdgDiff) < 5.0);
429 return rwy->lengthM() + rwy->widthM();
435 double hdgDiff = fabs(windHeading - runway->headingDeg()) * SG_DEGREES_TO_RADIANS;
436 SGMiscd::normalizeAngle(hdgDiff);
438 double crossWind = windSpeedKts * sin(hdgDiff);
439 double tailWind = -windSpeedKts * cos(hdgDiff);
441 return -(crossWind + tailWind);
452 runways.push_back(rwy);
454 _groupScore += _leadRunwayScore;
470 if (score < (0.5 * _leadRunwayScore)) {
476 runways.push_back(rwy);
477 _groupScore += score;
482 _basicScore = _groupScore;
483 RunwayVec::iterator it;
484 for (it = runways.begin(); it != runways.end(); ++it) {
497 if (runways.size() == 1) {
498 arrivals.push_back(runways.front());
499 departures.push_back(runways.front());
506 for (
unsigned int r = 0; r < runways.size(); ++r) {
508 arrivals.push_back(runways[r]);
510 departures.push_back(runways[r]);
517 std::ostringstream os;
518 os << runways.front()->ident();
519 for (
unsigned int r = 1; r < runways.size(); ++r) {
520 os <<
", " << runways[r]->ident();
523 os <<
" (score=" << _basicScore <<
", wind score=" << _groupScore <<
")";
531 double _basicScore{0.0};
538 _windHeading(windHeading)
548 double _windSpeedKts,
570std::string FGAirportDynamics::fallbackGetActiveRunway(
int action,
double heading)
572 bool updateNeeded =
false;
573 if (_lastFallbackUpdate == SGTimeStamp()) {
576 updateNeeded = (_lastFallbackUpdate.elapsedMSec() > (1000 * 60 * 15));
580 double windSpeed =
fgGetInt(
"/environment/metar/base-wind-speed-kt");
581 double windHeading =
fgGetInt(
"/environment/metar/base-wind-dir-deg");
584 WindExclusionCheck windCheck(windHeading, windSpeed);
586 auto it = std::remove_if(runways.begin(), runways.end(), windCheck);
587 runways.erase(it, runways.end());
590 std::sort(runways.begin(), runways.end(), SortByScore());
592 std::vector<FallbackRunwayGroup> groups;
593 std::vector<FallbackRunwayGroup>::iterator git;
595 for (it = runways.begin(); it != runways.end(); ++it) {
596 bool existingGroupDidAccept =
false;
597 for (git = groups.begin(); git != groups.end(); ++git) {
598 if (git->canAccept(*it)) {
599 existingGroupDidAccept =
true;
605 if (!existingGroupDidAccept) {
607 groups.push_back(FallbackRunwayGroup(*it));
611 if (groups.empty()) {
612 SG_LOG(SG_AI, SG_DEV_WARN,
"fallbackGetActiveRunway: airport " <<
parent()->ident() <<
" has no runways");
617 for (git = groups.begin(); git != groups.end(); ++git) {
618 git->adjustScoreForWind(windHeading, windSpeed);
621 std::sort(groups.begin(), groups.end(), GroupSortByScore());
624#if defined(RUNWAY_FALLBACK_DEBUG)
629 for (git = groups.begin(); git != groups.end(); ++git) {
630 os <<
"\n\t" << git->dump();
633 std::string s = os.str();
634 SG_LOG(SG_AI, SG_INFO, s);
639 FallbackRunwayGroup bestGroup = groups.front();
641 _lastFallbackUpdate.stamp();
642 _fallbackRunwayCounter = 0;
643 _fallbackDepartureRunways.clear();
644 _fallbackArrivalRunways.clear();
645 bestGroup.
getRunways(_fallbackArrivalRunways, _fallbackDepartureRunways);
647#if defined(RUNWAY_FALLBACK_DEBUG)
649 os <<
"\tArrival:" << _fallbackArrivalRunways.front()->ident();
650 for (
unsigned int r = 1; r < _fallbackArrivalRunways.size(); ++r) {
651 os <<
", " << _fallbackArrivalRunways[r]->ident();
653 os <<
"\n\tDeparture:" << _fallbackDepartureRunways.front()->ident();
654 for (
unsigned int r = 1; r < _fallbackDepartureRunways.size(); ++r) {
655 os <<
", " << _fallbackDepartureRunways[r]->ident();
658 std::string s = os.str();
659 SG_LOG(SG_AI, SG_INFO,
parent()->ident() <<
" fallback runways assignments for " <<
static_cast<int>(windHeading) <<
"@" <<
static_cast<int>(windSpeed) <<
"\n"
664 _fallbackRunwayCounter++;
668 r = _fallbackDepartureRunways[_fallbackRunwayCounter % _fallbackDepartureRunways.size()];
670 r = _fallbackArrivalRunways[_fallbackRunwayCounter % _fallbackArrivalRunways.size()];
676bool FGAirportDynamics::innerGetActiveRunway(
const std::string& trafficType,
677 int action, std::string& runway,
680 if (!rwyPrefs.available()) {
681 runway = fallbackGetActiveRunway(action, heading);
682 return !runway.empty();
685 time_t dayStart =
fgGetLong(
"/sim/time/utc/day-seconds");
686 if ((std::abs((
long)(dayStart - lastUpdate)) > 600) || trafficType != prevTrafficType) {
689 lastUpdate = dayStart;
690 prevTrafficType = trafficType;
698 double windSpeed =
fgGetInt(
"/environment/metar/base-wind-speed-kt");
699 double windHeading =
fgGetInt(
"/environment/metar/base-wind-dir-deg");
701 std::string scheduleName;
708 ScheduleTime* currSched;
710 currSched = rwyPrefs.getSchedule(trafficType.c_str());
714 scheduleName = currSched->
getName(dayStart);
718 if (scheduleName.empty())
721 RunwayGroup* currRunwayGroup = rwyPrefs.getGroup(scheduleName);
723 if (!(currRunwayGroup))
730 if (trafficType ==
"com") {
731 currentlyActive = &comActive;
732 }
else if (trafficType ==
"gen") {
733 currentlyActive = &genActive;
734 }
else if (trafficType ==
"mil") {
735 currentlyActive = &milActive;
736 }
else if (trafficType ==
"ul") {
737 currentlyActive = &ulActive;
742 it != currentlyActive->end(); ++it) {
750 maxTail, maxCross, currentlyActive);
755 currentlyActive->clear();
758 for (
int i = 0;
i < nrActiveRunways;
i++) {
760 std::string type =
"unknown";
763 if (type ==
"landing") {
764 landing.push_back(
name);
765 currentlyActive->push_back(
name);
768 if (type ==
"takeoff") {
769 takeoff.push_back(
name);
770 currentlyActive->push_back(
name);
779 int nr = takeoff.size();
784 runway = chooseRwyByHeading(takeoff, heading);
786 runway = chooseRunwayFallback();
792 if (!landing.empty()) {
793 runway = chooseRwyByHeading(landing, heading);
796 if (runway.empty()) {
797 runway = chooseRunwayFallback();
804std::string FGAirportDynamics::chooseRwyByHeading(
stringVec rwys,
807 double bestError = 360.0;
808 double rwyHeading, headingError;
811 if (!_ap->hasRunwayWithIdent(*
i)) {
812 SG_LOG(SG_ATC, SG_WARN,
"chooseRwyByHeading: runway " << *
i <<
" not found at " << _ap->ident());
816 FGRunway* rwy = _ap->getRunwayByIdent((*
i));
818 headingError = fabs(heading - rwyHeading);
819 if (headingError > 180)
820 headingError = fabs(headingError - 360);
821 if (headingError < bestError) {
823 bestError = headingError;
831 int action, std::string& runway,
836 bool ok = innerGetActiveRunway(trafficType, action, runway, heading);
837 if (!ok || runway.empty()) {
838 runway = chooseRunwayFallback();
845 if (activeRunways.size()) {
846 while (rwy != activeRunways.end()) {
847 if (rwy->getRunwayName().find(
name) != std::string::npos) {
853 if (rwy == activeRunways.end()) {
855 activeRunways.push_back(aRwy);
856 rwy = activeRunways.end() - 1;
861std::string FGAirportDynamics::chooseRunwayFallback()
865 SG_LOG(SG_AI, SG_WARN,
"FGAirportDynamics::chooseRunwayFallback failed at " << _ap->
ident());
875 return _ap->getElevation();
892 int approachFreq = 0;
894 SG_LOG(SG_ATC, SG_ALERT,
895 "Leg value is smaller than two at " << SG_ORIGIN);
898 const intVec& freqApproach(
parent()->groundNetwork()->getApproachFrequencies());
900 if (freqApproach.size() == 0) {
903 if ((freqApproach.size() > nr - 1) && (nr > 1)) {
904 approachFreq = freqApproach[nr - 1];
906 if ((freqApproach.size() < nr - 1) && (nr > 1)) {
908 (freqApproach.size() <
910 ? freqApproach[freqApproach.size() -
912 : freqApproach[nr - 2];
914 if ((freqApproach.size() >= nr - 1) && (nr > 1)) {
915 approachFreq = freqApproach[nr - 2];
926 SG_LOG(SG_ATC, SG_ALERT,
927 "Leg value is smaller than one at " << SG_ORIGIN);
930 const intVec& freqGround(
parent()->groundNetwork()->getGroundFrequencies());
932 if (freqGround.size() == 0) {
936 if ((freqGround.size() < leg) && (leg > 0)) {
938 (freqGround.size() <=
940 ? freqGround[freqGround.size() -
942 : freqGround[leg - 1];
944 if ((freqGround.size() >= leg) && (leg > 0)) {
945 groundFreq = freqGround[leg - 1];
954 SG_LOG(SG_ATC, SG_ALERT,
955 "Leg value is smaller than two at " << SG_ORIGIN);
958 const intVec& freqTower(
parent()->groundNetwork()->getTowerFrequencies());
960 if (freqTower.size() == 0) {
963 if ((freqTower.size() > nr - 1) && (nr > 1)) {
964 towerFreq = freqTower[nr - 1];
966 if ((freqTower.size() < nr - 1) && (nr > 1)) {
970 ? freqTower[freqTower.size() -
974 if ((freqTower.size() >= nr - 1) && (nr > 1)) {
975 towerFreq = freqTower[nr - 2];
982 if (atisSequenceIndex == -1) {
986 char atisSequenceString[2];
987 atisSequenceString[0] =
'a' + atisSequenceIndex;
988 atisSequenceString[1] = 0;
990 return globals->get_locale()->getLocalizedString(atisSequenceString,
"atc",
"unknown");
995 double now =
globals->get_sim_time_sec();
996 if (atisSequenceIndex == -1) {
998 atisSequenceTimeStamp = now;
999 atisSequenceIndex = rand() % 26;
1000 return atisSequenceIndex;
1003 int steps =
static_cast<int>((now - atisSequenceTimeStamp) / interval);
1004 atisSequenceTimeStamp += (interval * steps);
1005 if (forceUpdate && (steps == 0)) {
1009 atisSequenceIndex = (atisSequenceIndex + steps) % 26;
1011 return (atisSequenceIndex + (steps ? 0 : 26 * 1000));
SGSharedPtr< FGAirportDynamics > FGAirportDynamicsRef
std::vector< FGRunwayRef > FGRunwayList
std::vector< FGParkingRef > FGParkingList
SGSharedPtr< FGAirport > FGAirportRef
SGSharedPtr< FGRunway > FGRunwayRef
SGSharedPtr< FGParking > FGParkingRef
Class representing a kind of ground radar.
FGParkingList getParkings(bool onlyAvailable, const std::string &type) const
double getElevation() const
ParkingAssignment getParkingByName(const std::string &name) const
Find a parking gate index by name.
bool hasParking(FGParking *parking) const
ActiveRunwayQueue * getRunwayQueue(const std::string &name)
const std::string getAtisSequence()
get current ATIS sequence letter
int updateAtisSequence(int interval, bool forceUpdate)
get the current ATIS sequence number, updating it if necessary
int getApproachFrequency(unsigned nr)
void releaseParking(FGParking *id)
FGAirportDynamics(FGAirport *ap)
void init()
Initialization required after XMLRead.
FGParkingRef getOccupiedParkingByName(const std::string &name) const
ParkingAssignment getAvailableParking(double radius, const std::string &fltype, const std::string &acType, const std::string &airline)
retrieve an available parking by GateID, or -1 if no suitable parking location could be found.
FGAirport * parent() const
virtual ~FGAirportDynamics()
int getGroundFrequency(unsigned leg)
void getActiveRunway(const std::string &trafficType, int action, std::string &runway, double heading)
ParkingAssignment getAvailableParkingByName(const std::string &name)
find a parking by name, if available.
void setParkingAvailable(FGParking *park, bool available)
const std::string getId() const
int getTowerFrequency(unsigned nr)
void setRwyUse(const FGRunwayPreference &ref)
bool isParkingAvailable(FGParking *parking) const
FGRunwayRef getRunwayByIndex(unsigned int aIndex) const
FGRunwayRef getActiveRunwayForUsage() const
FGGroundNetwork * groundNetwork() const
const FGParkingList & allParkings() const
const std::string & ident() const
double headingDeg() const
Runway heading in degrees.
SGGeod begin() const
Get the runway beginning point - this is syntatic sugar, equivalent to calling pointOnCenterline(0....
FallbackRunwayGroup(const FGRunwayRef &rwy)
void getRunways(FGRunwayList &arrivals, FGRunwayList &departures)
void addRunway(const FGRunwayRef &rwy)
void adjustScoreForWind(double windHeading, double windSpeedKts)
double groupScore() const
bool canAccept(const FGRunwayRef &rwy) const
bool operator()(const FGParkingRef &park) const
GetParkingsPredicate(bool b, const std::string &ty, const FGAirportDynamics *dyn)
bool operator()(const FallbackRunwayGroup &a, const FallbackRunwayGroup &b) const
Helper to cache all AIObject positions near the airport when searching for available parkings.
bool isAnythingNear(const SGVec3d &cart, double radiusM)
NearbyAIObjectCache(FGAirportRef apt)
ParkingAssignmentPrivate(FGParking *pk, FGAirportDynamics *dyn)
SGWeakPtr< FGAirportDynamics > dynamics
~ParkingAssignmentPrivate()
FGParking * parking() const
void operator=(const ParkingAssignment &aOther)
void getActive(int i, std::string &name, std::string &type)
void setActive(const FGAirport *airport, double windSpeed, double windHeading, double maxTail, double maxCross, stringVec *curr)
std::string getName(time_t dayStart)
bool operator()(const FGRunwayRef &a, const FGRunwayRef &b) const
WindExclusionCheck(double windHeading, double windSpeedKts)
bool operator()(const FGRunwayRef &rwy) const
bool areRunwaysParallel(const FGRunwayRef &a, const FGRunwayRef &b)
double runwayScore(const FGRunwayRef &rwy)
std::vector< FGRunwayRef > RunwayVec
double runwayWindScore(const FGRunwayRef &runway, double windHeading, double windSpeedKts)
long fgGetLong(const char *name, long defaultValue)
Get a long value for a property.
int fgGetInt(const char *name, int defaultValue)
Get an int value for a property.
std::vector< int > intVec
FlightGear Localization Support.
std::vector< std::string >::iterator stringVecIterator
std::vector< std::string > stringVec
std::vector< ActiveRunwayQueue >::iterator ActiveRunwayVecIterator