FlightGear next
dynamics.cxx
Go to the documentation of this file.
1/*
2 * SPDX-FileName: dynamics.cxx
3 * SPDX-FileComment: Code to manage the higher order airport ground activities
4 * SPDX-FileCopyrightText: Written by Durk Talsma, started December 2004
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8#include <config.h>
9
10#include <algorithm>
11#include <string>
12#include <vector>
13
14#include <simgear/compiler.h>
15#include <simgear/structure/SGWeakPtr.hxx>
16
17#include <AIModel/AIBase.hxx>
18#include <AIModel/AIManager.hxx>
20#include <Airports/runways.hxx>
23#include <Main/fg_props.hxx>
24#include <Main/globals.hxx>
25#include <Main/locale.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>
31
32#include "airport.hxx"
33#include "dynamics.hxx"
34
35using std::sort;
36using std::string;
37using std::vector;
38
39// #define RUNWAY_FALLBACK_DEBUG
40
42{
43public:
45 parking(pk),
46 dynamics(dyn)
47 {
48 assert(pk);
49 assert(dyn);
50 retain(); // initial count of 1
51 }
52
54 {
55 FGAirportDynamicsRef strongRef = dynamics.lock();
56 if (strongRef) {
57 strongRef->releaseParking(parking);
58 }
59 }
60
61 void release()
62 {
63 if ((--refCount) == 0) {
64 delete this;
65 }
66 }
67
68 void retain()
69 {
70 ++refCount;
71 }
72
73 unsigned int refCount;
75
76 // we don't want an owning ref here, otherwise we
77 // have a circular ownership from AirportDynamics -> us
78 SGWeakPtr<FGAirportDynamics> dynamics;
79};
80
82{
83}
84
86{
87 if (_sharedData) {
88 _sharedData->release();
89 }
90}
91
93{
94 if (pk) {
95 _sharedData = new ParkingAssignmentPrivate(pk, dyn);
96 }
97}
98
99ParkingAssignment::ParkingAssignment(const ParkingAssignment& aOther) : _sharedData(aOther._sharedData)
100{
101 if (_sharedData) {
102 _sharedData->retain();
103 }
104}
105
107{
108 if (_sharedData == aOther._sharedData) {
109 return; // self-assignment, special case
110 }
111
112 if (_sharedData) {
113 _sharedData->release();
114 }
115
116 _sharedData = aOther._sharedData;
117 if (_sharedData) {
118 _sharedData->retain();
119 }
120}
121
123{
124 if (_sharedData) {
125 _sharedData->release();
126 _sharedData = NULL;
127 }
128}
129
131{
132 return (_sharedData != NULL);
133}
134
136{
137 return _sharedData ? _sharedData->parking.ptr() : NULL;
138}
139
141
150{
151public:
152 explicit NearbyAIObjectCache(FGAirportRef apt) : m_airport(apt)
153 {
154 }
155
156 bool isAnythingNear(const SGVec3d& cart, double radiusM)
157 {
158 if (!m_populated) {
159 populate();
160 }
161
162 for (auto c : m_cache) {
163 const double d = dist(cart, c);
164 if (d < radiusM) {
165 return true;
166 }
167 }
168
169 return false;
170 }
171
172private:
173 void populate()
174 {
175 SGVec3d cartAirportPos = m_airport->cart();
176 auto aiManager = globals->get_subsystem<FGAIManager>();
177 for (auto ai : aiManager->get_ai_list()) {
178 const auto cart = ai->getCartPos();
179
180 // 20km cutoff from airport centre
181 if (dist(cartAirportPos, cart) > 20000) {
182 continue;
183 }
184
185 m_cache.push_back(cart);
186 }
187 m_populated = true;
188 }
189
190 bool m_populated = false;
191 FGAirportRef m_airport;
192 std::vector<SGVec3d> m_cache;
193};
194
196
198 startupController(this),
199 towerController(this),
200 approachController(this),
201 groundController(this),
202 lastUpdate{0},
203 atisSequenceIndex(-1),
204 atisSequenceTimeStamp(0.0)
205{
206}
207
208// Destructor
212
213
218{
219 groundRadar = new AirportGroundRadar(_ap);
220
221 startupController.setAirportGroundRadar(groundRadar);
222 towerController.setAirportGroundRadar(groundRadar);
223 approachController.setAirportGroundRadar(groundRadar);
224 groundController.setAirportGroundRadar(groundRadar);
225}
226
227FGParking* FGAirportDynamics::innerGetAvailableParking(double radius, const std::string& flType,
228 const std::string& airline,
229 bool skipEmptyAirlineCode)
230{
231 NearbyAIObjectCache nearCache(parent());
232 const FGParkingList& parkings(parent()->groundNetwork()->allParkings());
233 FGParkingList candidates;
234 for (auto parking : parkings) {
235 if (!isParkingAvailable(parking)) {
236 continue;
237 }
238
239 if (parking->getRadius() < radius) {
240 continue;
241 }
242
243 if (!flType.empty() && (parking->getType() != flType)) {
244 continue;
245 }
246
247 if (skipEmptyAirlineCode && parking->getCodes().empty()) {
248 continue;
249 }
250
251 if (!airline.empty() && !parking->getCodes().empty()) {
252 if (parking->getCodes().find(airline, 0) == std::string::npos) {
253 continue;
254 }
255 }
256
257 if (nearCache.isAnythingNear(parking->cart(), parking->getRadius())) {
258 continue;
259 }
260
261 candidates.push_back(parking);
262 }
263
264 if (candidates.empty()) {
265 return nullptr;
266 }
267
268 // sort by increasing radius, so we return the smallest radius candidate
269 // this avoids large spaces (A380 sized) being given to Fokkers/ATR-72s
270 std::sort(candidates.begin(), candidates.end(),
271 [](const FGParkingRef& a, const FGParkingRef& b) {
272 return a->getRadius() < b->getRadius();
273 });
274
275 setParkingAvailable(candidates.front(), false);
276 return candidates.front();
277}
278
280{
281 return std::find(parent()->groundNetwork()->allParkings().begin(),
282 parent()->groundNetwork()->allParkings().end(),
283 parking) != parent()->groundNetwork()->allParkings().end();
284}
285
287{
288 return !parent()->groundNetwork()->allParkings().empty();
289}
290
292 const std::string& flType,
293 const std::string& acType,
294 const std::string& airline)
295{
296 SG_UNUSED(acType); // sadly not used at the moment
297
298 // most exact seach - airline codes must be present and match
299 FGParking* result = innerGetAvailableParking(radius, flType, airline, true);
300 if (result) {
301 return ParkingAssignment(result, this);
302 }
303
304 // more tolerant - gates with empty airline codes are permitted
305 result = innerGetAvailableParking(radius, flType, airline, false);
306 if (result) {
307 return ParkingAssignment(result, this);
308 }
309
310 // fallback - ignore the airline code entirely
311 result = innerGetAvailableParking(radius, flType, std::string(), false);
312 return result ? ParkingAssignment(result, this) : ParkingAssignment();
313}
314
316{
317 const FGParkingList& parkings(parent()->groundNetwork()->allParkings());
318 FGParkingList::const_iterator it;
319 for (it = parkings.begin(); it != parkings.end(); ++it) {
320 if ((*it)->name() == name) {
321 return ParkingAssignment(*it, const_cast<FGAirportDynamics*>(this));
322 }
323 }
324
325 return ParkingAssignment();
326}
327
329{
330 const FGParkingList& parkings(parent()->groundNetwork()->allParkings());
331 auto it = std::find_if(parkings.begin(), parkings.end(), [this, name](FGParkingRef parking) {
332 if (parking->name() != name)
333 return false;
334
335 return this->isParkingAvailable(parking);
336 });
337
338 if (it == parkings.end())
339 return {}; // no assignment possible
340
341 return {*it, this};
342}
343
345{
346 auto it = std::find_if(occupiedParkings.begin(), occupiedParkings.end(),
347 [name](const FGParkingRef& pk) { return pk->name() == name; });
348 if (it != occupiedParkings.end()) {
349 return *it;
350 }
351
352 return {};
353}
354
356{
357 if (available) {
358 releaseParking(park);
359 } else {
360 occupiedParkings.insert(park);
361 }
362}
363
365{
366 return (occupiedParkings.find(parking) == occupiedParkings.end());
367}
368
370{
371 ParkingSet::iterator it = occupiedParkings.find(id);
372 if (it == occupiedParkings.end()) {
373 return;
374 }
375
376 occupiedParkings.erase(it);
377}
378
380{
381 bool mustBeAvailable;
382 std::string type;
383 const FGAirportDynamics* dynamics;
384
385public:
386 GetParkingsPredicate(bool b, const std::string& ty, const FGAirportDynamics* dyn) : mustBeAvailable(b),
387 type(ty),
388 dynamics(dyn)
389 {
390 }
391
392 bool operator()(const FGParkingRef& park) const
393 {
394 if (!type.empty() && (park->getType() != type))
395 return true;
396
397 if (mustBeAvailable && !dynamics->isParkingAvailable(park)) {
398 return true;
399 }
400
401 return false;
402 }
403};
404
405FGParkingList FGAirportDynamics::getParkings(bool onlyAvailable, const std::string& type) const
406{
407 FGParkingList result(parent()->groundNetwork()->allParkings());
408
409 GetParkingsPredicate pred(onlyAvailable, type, this);
410 auto it = std::remove_if(result.begin(), result.end(), pred);
411 result.erase(it, result.end());
412 return result;
413}
414
416{
417 rwyPrefs = ref;
418}
419
421{
422 double hdgDiff = (b->headingDeg() - a->headingDeg());
423 SG_NORMALIZE_RANGE(hdgDiff, -180.0, 180.0);
424 return (fabs(hdgDiff) < 5.0);
425}
426
427double runwayScore(const FGRunwayRef& rwy)
428{
429 return rwy->lengthM() + rwy->widthM();
430}
431
432double runwayWindScore(const FGRunwayRef& runway, double windHeading,
433 double windSpeedKts)
434{
435 double hdgDiff = fabs(windHeading - runway->headingDeg()) * SG_DEGREES_TO_RADIANS;
436 SGMiscd::normalizeAngle(hdgDiff);
437
438 double crossWind = windSpeedKts * sin(hdgDiff);
439 double tailWind = -windSpeedKts * cos(hdgDiff);
440
441 return -(crossWind + tailWind);
442}
443
444typedef std::vector<FGRunwayRef> RunwayVec;
445
446
448{
449public:
450 explicit FallbackRunwayGroup(const FGRunwayRef& rwy) : _groupScore(0.0)
451 {
452 runways.push_back(rwy);
453 _leadRunwayScore = runwayScore(rwy);
454 _groupScore += _leadRunwayScore;
455 }
456
457 bool canAccept(const FGRunwayRef& rwy) const
458 {
459 if (!areRunwaysParallel(runways.front(), rwy)) {
460 return false;
461 }
462
463 return true;
464 }
465
466 void addRunway(const FGRunwayRef& rwy)
467 {
468 assert(areRunwaysParallel(runways.front(), rwy));
469 double score = runwayScore(rwy);
470 if (score < (0.5 * _leadRunwayScore)) {
471 // drop the runway completely. this is to drop short parallel
472 // runways from being active
473 return;
474 }
475
476 runways.push_back(rwy);
477 _groupScore += score;
478 }
479
480 void adjustScoreForWind(double windHeading, double windSpeedKts)
481 {
482 _basicScore = _groupScore;
483 RunwayVec::iterator it;
484 for (it = runways.begin(); it != runways.end(); ++it) {
485 _groupScore += runwayWindScore(*it, windHeading, windSpeedKts);
486 }
487 }
488
489 double groupScore() const
490 {
491 return _groupScore;
492 }
493
494 void getRunways(FGRunwayList& arrivals, FGRunwayList& departures)
495 {
496 // make the common cases very obvious
497 if (runways.size() == 1) {
498 arrivals.push_back(runways.front());
499 departures.push_back(runways.front());
500 return;
501 }
502
503
504 // because runways were sorted by score when building, they were added
505 // by score also, so we can use a simple algorithm to assign
506 for (unsigned int r = 0; r < runways.size(); ++r) {
507 if ((r % 2) == 0) {
508 arrivals.push_back(runways[r]);
509 } else {
510 departures.push_back(runways[r]);
511 }
512 }
513 }
514
515 std::string dump()
516 {
517 std::ostringstream os;
518 os << runways.front()->ident();
519 for (unsigned int r = 1; r < runways.size(); ++r) {
520 os << ", " << runways[r]->ident();
521 }
522
523 os << " (score=" << _basicScore << ", wind score=" << _groupScore << ")";
524 return os.str();
525 }
526
527private:
528 RunwayVec runways;
529 double _groupScore,
530 _leadRunwayScore;
531 double _basicScore{0.0};
532};
533
535{
536public:
537 WindExclusionCheck(double windHeading, double windSpeedKts) : _windSpeedKts(windSpeedKts),
538 _windHeading(windHeading)
539 {
540 }
541
542 bool operator()(const FGRunwayRef& rwy) const
543 {
544 return (runwayWindScore(rwy, _windHeading, _windSpeedKts) > 30);
545 }
546
547private:
548 double _windSpeedKts,
549 _windHeading;
550};
551
553{
554public:
555 bool operator()(const FGRunwayRef& a, const FGRunwayRef& b) const
556 {
557 return runwayScore(a) > runwayScore(b);
558 }
559};
560
562{
563public:
565 {
566 return a.groupScore() > b.groupScore();
567 }
568};
569
570std::string FGAirportDynamics::fallbackGetActiveRunway(int action, double heading)
571{
572 bool updateNeeded = false;
573 if (_lastFallbackUpdate == SGTimeStamp()) {
574 updateNeeded = true;
575 } else {
576 updateNeeded = (_lastFallbackUpdate.elapsedMSec() > (1000 * 60 * 15));
577 }
578
579 if (updateNeeded) {
580 double windSpeed = fgGetInt("/environment/metar/base-wind-speed-kt");
581 double windHeading = fgGetInt("/environment/metar/base-wind-dir-deg");
582
583 // discount runways based on cross / tail-wind
584 WindExclusionCheck windCheck(windHeading, windSpeed);
585 RunwayVec runways(parent()->getRunways());
586 auto it = std::remove_if(runways.begin(), runways.end(), windCheck);
587 runways.erase(it, runways.end());
588
589 // sort highest scored to lowest scored
590 std::sort(runways.begin(), runways.end(), SortByScore());
591
592 std::vector<FallbackRunwayGroup> groups;
593 std::vector<FallbackRunwayGroup>::iterator git;
594
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;
600 git->addRunway(*it);
601 break;
602 }
603 } // of existing groups iteration
604
605 if (!existingGroupDidAccept) {
606 // create a new group
607 groups.push_back(FallbackRunwayGroup(*it));
608 }
609 } // of group building phase
610
611 if (groups.empty()) {
612 SG_LOG(SG_AI, SG_DEV_WARN, "fallbackGetActiveRunway: airport " << parent()->ident() << " has no runways");
613 return {};
614 }
615
616 // select highest scored group based on cross/tail wind
617 for (git = groups.begin(); git != groups.end(); ++git) {
618 git->adjustScoreForWind(windHeading, windSpeed);
619 }
620
621 std::sort(groups.begin(), groups.end(), GroupSortByScore());
622
623 // debugging fallback runway assignments
624#if defined(RUNWAY_FALLBACK_DEBUG)
625 {
626 ostringstream os;
627 os << parent()->ident() << " groups:";
628
629 for (git = groups.begin(); git != groups.end(); ++git) {
630 os << "\n\t" << git->dump();
631 }
632
633 std::string s = os.str();
634 SG_LOG(SG_AI, SG_INFO, s);
635 }
636#endif
637
638 // assign takeoff and landing runways
639 FallbackRunwayGroup bestGroup = groups.front();
640
641 _lastFallbackUpdate.stamp();
642 _fallbackRunwayCounter = 0;
643 _fallbackDepartureRunways.clear();
644 _fallbackArrivalRunways.clear();
645 bestGroup.getRunways(_fallbackArrivalRunways, _fallbackDepartureRunways);
646
647#if defined(RUNWAY_FALLBACK_DEBUG)
648 ostringstream os;
649 os << "\tArrival:" << _fallbackArrivalRunways.front()->ident();
650 for (unsigned int r = 1; r < _fallbackArrivalRunways.size(); ++r) {
651 os << ", " << _fallbackArrivalRunways[r]->ident();
652 }
653 os << "\n\tDeparture:" << _fallbackDepartureRunways.front()->ident();
654 for (unsigned int r = 1; r < _fallbackDepartureRunways.size(); ++r) {
655 os << ", " << _fallbackDepartureRunways[r]->ident();
656 }
657
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"
660 << s);
661#endif
662 }
663
664 _fallbackRunwayCounter++;
665 FGRunwayRef r;
666 // ensure we cycle through possible runways where they exist
667 if (action == 1) {
668 r = _fallbackDepartureRunways[_fallbackRunwayCounter % _fallbackDepartureRunways.size()];
669 } else {
670 r = _fallbackArrivalRunways[_fallbackRunwayCounter % _fallbackArrivalRunways.size()];
671 }
672
673 return r->ident();
674}
675
676bool FGAirportDynamics::innerGetActiveRunway(const std::string& trafficType,
677 int action, std::string& runway,
678 double heading)
679{
680 if (!rwyPrefs.available()) {
681 runway = fallbackGetActiveRunway(action, heading);
682 return !runway.empty();
683 }
684
685 time_t dayStart = fgGetLong("/sim/time/utc/day-seconds");
686 if ((std::abs((long)(dayStart - lastUpdate)) > 600) || trafficType != prevTrafficType) {
687 landing.clear();
688 takeoff.clear();
689 lastUpdate = dayStart;
690 prevTrafficType = trafficType;
691 /*
692 FGEnvironment
693 stationweather =
694 globals->get_subsystem<FGEnvironmentMgr>()
695 ->getEnvironment(getLatitude(), getLongitude(),
696 getElevation());
697 */
698 double windSpeed = fgGetInt("/environment/metar/base-wind-speed-kt"); //stationweather.get_wind_speed_kt();
699 double windHeading = fgGetInt("/environment/metar/base-wind-dir-deg");
700 //stationweather.get_wind_from_heading_deg();
701 std::string scheduleName;
702 //cerr << "finding active Runway for : " << _ap->getId() << endl;
703 //cerr << "Wind Heading : " << windHeading << endl;
704 //cerr << "Wind Speed : " << windSpeed << endl;
705
706 //cerr << "Nr of seconds since day start << " << dayStart << endl;
707
708 ScheduleTime* currSched;
709 //cerr << "A"<< endl;
710 currSched = rwyPrefs.getSchedule(trafficType.c_str());
711 if (!(currSched))
712 return false;
713 //cerr << "B"<< endl;
714 scheduleName = currSched->getName(dayStart);
715 double maxTail = currSched->getTailWind();
716 double maxCross = currSched->getCrossWind();
717 //cerr << "Current Schedule = : " << scheduleName << endl;
718 if (scheduleName.empty())
719 return false;
720 //cerr << "C"<< endl;
721 RunwayGroup* currRunwayGroup = rwyPrefs.getGroup(scheduleName);
722 //cerr << "D"<< endl;
723 if (!(currRunwayGroup))
724 return false;
725
726 // Keep a history of the currently active runways, to ensure
727 // that an already established selection of runways will not
728 // be overridden once a more preferred selection becomes
729 // available as that can lead to random runway swapping.
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;
738 }
739
740 //cerr << "Currently active selection for " << trafficType << ": ";
741 for (stringVecIterator it = currentlyActive->begin();
742 it != currentlyActive->end(); ++it) {
743 //cerr << (*it) << " ";
744 }
745 //cerr << endl;
746
747 currRunwayGroup->setActive(_ap,
748 windSpeed,
749 windHeading,
750 maxTail, maxCross, currentlyActive);
751
752 // Note that I SHOULD keep multiple lists in memory, one for
753 // general aviation, one for commercial and one for military
754 // traffic.
755 currentlyActive->clear();
756 int nrActiveRunways = currRunwayGroup->getNrActiveRunways();
757 //cerr << "Choosing runway for " << trafficType << endl;
758 for (int i = 0; i < nrActiveRunways; i++) {
759 std::string name;
760 std::string type = "unknown"; // initialize to something other than landing or takeoff
761
762 currRunwayGroup->getActive(i, name, type);
763 if (type == "landing") {
764 landing.push_back(name);
765 currentlyActive->push_back(name);
766 //cerr << "Landing " << name << endl;
767 }
768 if (type == "takeoff") {
769 takeoff.push_back(name);
770 currentlyActive->push_back(name);
771 //cerr << "takeoff " << name << endl;
772 }
773 }
774 //cerr << endl;
775 }
776
777 if (action == 1) // takeoff
778 {
779 int nr = takeoff.size();
780 if (nr) {
781 // Note that the randomization below, is just a placeholder to choose between
782 // multiple active runways for this action. This should be
783 // under ATC control.
784 runway = chooseRwyByHeading(takeoff, heading);
785 } else { // Fallback
786 runway = chooseRunwayFallback();
787 }
788 }
789
790 if (action == 2) // landing
791 {
792 if (!landing.empty()) {
793 runway = chooseRwyByHeading(landing, heading);
794 }
795
796 if (runway.empty()) { //fallback
797 runway = chooseRunwayFallback();
798 }
799 }
800
801 return true;
802}
803
804std::string FGAirportDynamics::chooseRwyByHeading(stringVec rwys,
805 double heading)
806{
807 double bestError = 360.0;
808 double rwyHeading, headingError;
809 std::string runway;
810 for (stringVecIterator i = rwys.begin(); i != rwys.end(); ++i) {
811 if (!_ap->hasRunwayWithIdent(*i)) {
812 SG_LOG(SG_ATC, SG_WARN, "chooseRwyByHeading: runway " << *i << " not found at " << _ap->ident());
813 continue;
814 }
815
816 FGRunway* rwy = _ap->getRunwayByIdent((*i));
817 rwyHeading = rwy->headingDeg();
818 headingError = fabs(heading - rwyHeading);
819 if (headingError > 180)
820 headingError = fabs(headingError - 360);
821 if (headingError < bestError) {
822 runway = (*i);
823 bestError = headingError;
824 }
825 }
826 //cerr << "Using active runway " << runway << " for heading " << heading << endl;
827 return runway;
828}
829
830void FGAirportDynamics::getActiveRunway(const std::string& trafficType,
831 int action, std::string& runway,
832 double heading)
833{
834 //FIXME must allign with FGAirport::findBestRunwayForHeading
835
836 bool ok = innerGetActiveRunway(trafficType, action, runway, heading);
837 if (!ok || runway.empty()) {
838 runway = chooseRunwayFallback();
839 }
840}
841
843{
844 ActiveRunwayVecIterator rwy = activeRunways.begin();
845 if (activeRunways.size()) {
846 while (rwy != activeRunways.end()) {
847 if (rwy->getRunwayName().find(name) != std::string::npos) {
848 break;
849 }
850 ++rwy;
851 }
852 }
853 if (rwy == activeRunways.end()) {
854 ActiveRunwayQueue aRwy(_ap->getId(), name, 0);
855 activeRunways.push_back(aRwy);
856 rwy = activeRunways.end() - 1;
857 }
858 return &(*rwy);
859}
860
861std::string FGAirportDynamics::chooseRunwayFallback()
862{
863 FGRunway* rwy = _ap->getActiveRunwayForUsage();
864 if (!rwy) {
865 SG_LOG(SG_AI, SG_WARN, "FGAirportDynamics::chooseRunwayFallback failed at " << _ap->ident());
866
867 // let's use runway 0
868 return _ap->getRunwayByIndex(0)->ident();
869 }
870 return rwy->ident();
871}
872
874{
875 return _ap->getElevation();
876}
877
878const std::string FGAirportDynamics::getId() const
879{
880 return _ap->getId();
881}
882
883// Experimental: Return a different ground frequency depending on the leg of the
884// Flight. Leg should always have a minimum value of two when this function is called.
885// Note that in this scheme, the assignment of various frequencies to various ground
886// operations is completely arbitrary. As such, is a short cut I need to take now,
887// so that at least I can start working on assigning different frequencies to different
888// operations.
889
891{
892 int approachFreq = 0;
893 if (nr < 2) {
894 SG_LOG(SG_ATC, SG_ALERT,
895 "Leg value is smaller than two at " << SG_ORIGIN);
896 }
897
898 const intVec& freqApproach(parent()->groundNetwork()->getApproachFrequencies());
899
900 if (freqApproach.size() == 0) {
901 return 0;
902 }
903 if ((freqApproach.size() > nr - 1) && (nr > 1)) {
904 approachFreq = freqApproach[nr - 1];
905 }
906 if ((freqApproach.size() < nr - 1) && (nr > 1)) {
907 approachFreq =
908 (freqApproach.size() <
909 (nr - 1))
910 ? freqApproach[freqApproach.size() -
911 1]
912 : freqApproach[nr - 2];
913 }
914 if ((freqApproach.size() >= nr - 1) && (nr > 1)) {
915 approachFreq = freqApproach[nr - 2];
916 }
917 return approachFreq;
918}
919
921{
922 //return freqGround.size() ? freqGround[0] : 0; };
923 //cerr << "Getting frequency for : " << leg << endl;
924 int groundFreq = 0;
925 if (leg < 1) {
926 SG_LOG(SG_ATC, SG_ALERT,
927 "Leg value is smaller than one at " << SG_ORIGIN);
928 }
929
930 const intVec& freqGround(parent()->groundNetwork()->getGroundFrequencies());
931
932 if (freqGround.size() == 0) {
933 return 0;
934 }
935
936 if ((freqGround.size() < leg) && (leg > 0)) {
937 groundFreq =
938 (freqGround.size() <=
939 (leg - 1))
940 ? freqGround[freqGround.size() -
941 1]
942 : freqGround[leg - 1];
943 }
944 if ((freqGround.size() >= leg) && (leg > 0)) {
945 groundFreq = freqGround[leg - 1];
946 }
947 return groundFreq;
948}
949
951{
952 int towerFreq = 0;
953 if (nr < 2) {
954 SG_LOG(SG_ATC, SG_ALERT,
955 "Leg value is smaller than two at " << SG_ORIGIN);
956 }
957
958 const intVec& freqTower(parent()->groundNetwork()->getTowerFrequencies());
959
960 if (freqTower.size() == 0) {
961 return 0;
962 }
963 if ((freqTower.size() > nr - 1) && (nr > 1)) {
964 towerFreq = freqTower[nr - 1];
965 }
966 if ((freqTower.size() < nr - 1) && (nr > 1)) {
967 towerFreq =
968 (freqTower.size() <
969 (nr - 1))
970 ? freqTower[freqTower.size() -
971 1]
972 : freqTower[nr - 2];
973 }
974 if ((freqTower.size() >= nr - 1) && (nr > 1)) {
975 towerFreq = freqTower[nr - 2];
976 }
977 return towerFreq;
978}
979
981{
982 if (atisSequenceIndex == -1) {
983 updateAtisSequence(1, false);
984 }
985
986 char atisSequenceString[2];
987 atisSequenceString[0] = 'a' + atisSequenceIndex;
988 atisSequenceString[1] = 0;
989
990 return globals->get_locale()->getLocalizedString(atisSequenceString, "atc", "unknown");
991}
992
993int FGAirportDynamics::updateAtisSequence(int interval, bool forceUpdate)
994{
995 double now = globals->get_sim_time_sec();
996 if (atisSequenceIndex == -1) {
997 // first computation
998 atisSequenceTimeStamp = now;
999 atisSequenceIndex = rand() % 26; // random initial sequence letters
1000 return atisSequenceIndex;
1001 }
1002
1003 int steps = static_cast<int>((now - atisSequenceTimeStamp) / interval);
1004 atisSequenceTimeStamp += (interval * steps);
1005 if (forceUpdate && (steps == 0)) {
1006 ++steps; // a "special" ATIS update is required
1007 }
1008
1009 atisSequenceIndex = (atisSequenceIndex + steps) % 26;
1010 // return a huge value if no update occurred
1011 return (atisSequenceIndex + (steps ? 0 : 26 * 1000));
1012}
#define i(x)
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
Definition dynamics.cxx:405
double getElevation() const
Definition dynamics.cxx:873
ParkingAssignment getParkingByName(const std::string &name) const
Find a parking gate index by name.
Definition dynamics.cxx:315
bool hasParking(FGParking *parking) const
Definition dynamics.cxx:279
ActiveRunwayQueue * getRunwayQueue(const std::string &name)
Definition dynamics.cxx:842
const std::string getAtisSequence()
get current ATIS sequence letter
Definition dynamics.cxx:980
int updateAtisSequence(int interval, bool forceUpdate)
get the current ATIS sequence number, updating it if necessary
Definition dynamics.cxx:993
int getApproachFrequency(unsigned nr)
Definition dynamics.cxx:890
void releaseParking(FGParking *id)
Definition dynamics.cxx:369
FGAirportDynamics(FGAirport *ap)
Definition dynamics.cxx:197
void init()
Initialization required after XMLRead.
Definition dynamics.cxx:217
FGParkingRef getOccupiedParkingByName(const std::string &name) const
Definition dynamics.cxx:344
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.
Definition dynamics.cxx:291
bool hasParkings() const
Definition dynamics.cxx:286
FGAirport * parent() const
Definition dynamics.hxx:116
virtual ~FGAirportDynamics()
Definition dynamics.cxx:209
int getGroundFrequency(unsigned leg)
Definition dynamics.cxx:920
void getActiveRunway(const std::string &trafficType, int action, std::string &runway, double heading)
Definition dynamics.cxx:830
ParkingAssignment getAvailableParkingByName(const std::string &name)
find a parking by name, if available.
Definition dynamics.cxx:328
void setParkingAvailable(FGParking *park, bool available)
Definition dynamics.cxx:355
const std::string getId() const
Definition dynamics.cxx:878
int getTowerFrequency(unsigned nr)
Definition dynamics.cxx:950
void setRwyUse(const FGRunwayPreference &ref)
Definition dynamics.cxx:415
bool isParkingAvailable(FGParking *parking) const
Definition dynamics.cxx:364
FGRunwayRef getRunwayByIndex(unsigned int aIndex) const
Definition airport.cxx:116
FGRunwayRef getActiveRunwayForUsage() const
Definition airport.cxx:402
FGGroundNetwork * groundNetwork() const
Definition airport.cxx:1053
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....
Definition runways.cxx:89
FallbackRunwayGroup(const FGRunwayRef &rwy)
Definition dynamics.cxx:450
void getRunways(FGRunwayList &arrivals, FGRunwayList &departures)
Definition dynamics.cxx:494
void addRunway(const FGRunwayRef &rwy)
Definition dynamics.cxx:466
void adjustScoreForWind(double windHeading, double windSpeedKts)
Definition dynamics.cxx:480
double groupScore() const
Definition dynamics.cxx:489
std::string dump()
Definition dynamics.cxx:515
bool canAccept(const FGRunwayRef &rwy) const
Definition dynamics.cxx:457
bool operator()(const FGParkingRef &park) const
Definition dynamics.cxx:392
GetParkingsPredicate(bool b, const std::string &ty, const FGAirportDynamics *dyn)
Definition dynamics.cxx:386
bool operator()(const FallbackRunwayGroup &a, const FallbackRunwayGroup &b) const
Definition dynamics.cxx:564
Helper to cache all AIObject positions near the airport when searching for available parkings.
Definition dynamics.cxx:150
bool isAnythingNear(const SGVec3d &cart, double radiusM)
Definition dynamics.cxx:156
NearbyAIObjectCache(FGAirportRef apt)
Definition dynamics.cxx:152
ParkingAssignmentPrivate(FGParking *pk, FGAirportDynamics *dyn)
Definition dynamics.cxx:44
SGWeakPtr< FGAirportDynamics > dynamics
Definition dynamics.cxx:78
FGParking * parking() const
Definition dynamics.cxx:135
bool isValid() const
Definition dynamics.cxx:130
void operator=(const ParkingAssignment &aOther)
Definition dynamics.cxx:106
void getActive(int i, std::string &name, std::string &type)
int getNrActiveRunways()
void setActive(const FGAirport *airport, double windSpeed, double windHeading, double maxTail, double maxCross, stringVec *curr)
double getTailWind()
std::string getName(time_t dayStart)
double getCrossWind()
bool operator()(const FGRunwayRef &a, const FGRunwayRef &b) const
Definition dynamics.cxx:555
WindExclusionCheck(double windHeading, double windSpeedKts)
Definition dynamics.cxx:537
bool operator()(const FGRunwayRef &rwy) const
Definition dynamics.cxx:542
bool areRunwaysParallel(const FGRunwayRef &a, const FGRunwayRef &b)
Definition dynamics.cxx:420
double runwayScore(const FGRunwayRef &rwy)
Definition dynamics.cxx:427
std::vector< FGRunwayRef > RunwayVec
Definition dynamics.cxx:444
double runwayWindScore(const FGRunwayRef &runway, double windHeading, double windSpeedKts)
Definition dynamics.cxx:432
const char * name
long fgGetLong(const char *name, long defaultValue)
Get a long value for a property.
Definition fg_props.cxx:538
int fgGetInt(const char *name, int defaultValue)
Get an int value for a property.
Definition fg_props.cxx:532
FGGlobals * globals
Definition globals.cxx:142
std::vector< int > intVec
FlightGear Localization Support.
std::vector< std::string >::iterator stringVecIterator
std::vector< std::string > stringVec
std::vector< ActiveRunwayQueue >::iterator ActiveRunwayVecIterator