]> git.mxchange.org Git - flightgear.git/blob - src/Airports/dynamics.cxx
Checkpoint - ground-net skips the cache
[flightgear.git] / src / Airports / dynamics.cxx
1 // dynamics.cxx - Code to manage the higher order airport ground activities
2 // Written by Durk Talsma, started December 2004.
3 //
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License as
7 // published by the Free Software Foundation; either version 2 of the
8 // License, or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful, but
11 // WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 // General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 //
19 // $Id$
20
21 #ifdef HAVE_CONFIG_H
22 #  include <config.h>
23 #endif
24
25 #include <algorithm>
26 #include <string>
27 #include <vector>
28
29 #include <boost/foreach.hpp>
30
31 #include <simgear/compiler.h>
32
33 #include <Environment/environment_mgr.hxx>
34 #include <Environment/environment.hxx>
35 #include <simgear/misc/sg_path.hxx>
36 #include <simgear/props/props.hxx>
37 #include <simgear/structure/subsystem_mgr.hxx>
38 #include <simgear/debug/logstream.hxx>
39 #include <Main/globals.hxx>
40 #include <Main/fg_props.hxx>
41 #include <Main/locale.hxx>
42 #include <Airports/runways.hxx>
43 #include <Navaids/NavDataCache.hxx>
44
45 #include "airport.hxx"
46 #include "dynamics.hxx"
47
48 using std::string;
49 using std::vector;
50 using std::sort;
51 using std::random_shuffle;
52
53 class ParkingAssignment::ParkingAssignmentPrivate
54 {
55 public:
56   ParkingAssignmentPrivate(FGParking* pk, FGAirport* apt) :
57     refCount(0),
58     parking(pk),
59     airport(apt)
60   {
61     assert(pk);
62     assert(apt);
63     retain(); // initial count of 1
64   }
65   
66   ~ParkingAssignmentPrivate()
67   {
68     airport->getDynamics()->releaseParking(parking);
69   }
70   
71   void release()
72   {
73     if ((--refCount) == 0) {
74       delete this;
75     }
76   }
77   
78   void retain()
79   {
80     ++refCount;
81   }
82   
83   unsigned int refCount;
84   FGParkingRef parking;
85   FGAirportRef airport;
86 };
87
88 ParkingAssignment::ParkingAssignment() :
89   _sharedData(NULL)
90 {
91 }
92
93 ParkingAssignment::~ParkingAssignment()
94 {
95   if (_sharedData) {
96     _sharedData->release();
97   }
98 }
99   
100 ParkingAssignment::ParkingAssignment(FGParking* pk, FGAirport* apt) :
101   _sharedData(NULL)
102 {
103   if (pk) {
104     _sharedData = new ParkingAssignmentPrivate(pk, apt);
105   }
106 }
107
108 ParkingAssignment::ParkingAssignment(const ParkingAssignment& aOther) :
109   _sharedData(aOther._sharedData)
110 {
111   if (_sharedData) {
112     _sharedData->retain();
113   }
114 }
115
116 void ParkingAssignment::operator=(const ParkingAssignment& aOther)
117 {
118   if (_sharedData == aOther._sharedData) {
119     return; // self-assignment, special case
120   }
121   
122   if (_sharedData) {
123     _sharedData->release();
124   }
125   
126   _sharedData = aOther._sharedData;
127   if (_sharedData) {
128     _sharedData->retain();
129   }
130 }
131   
132 void ParkingAssignment::release()
133 {
134   if (_sharedData) {
135     _sharedData->release();
136     _sharedData = NULL;
137   }
138 }
139
140 bool ParkingAssignment::isValid() const
141 {
142   return (_sharedData != NULL);
143 }
144
145 FGParking* ParkingAssignment::parking() const
146 {
147   return _sharedData ? _sharedData->parking.ptr() : NULL;
148 }
149
150 ////////////////////////////////////////////////////////////////////////////////
151
152 FGAirportDynamics::FGAirportDynamics(FGAirport * ap):
153     _ap(ap), rwyPrefs(ap),
154     startupController    (this),
155     towerController      (this),
156     approachController   (this),
157     atisSequenceIndex(-1),
158     atisSequenceTimeStamp(0.0)
159
160 {
161     lastUpdate = 0;
162 }
163
164 // Destructor
165 FGAirportDynamics::~FGAirportDynamics()
166 {
167 }
168
169
170 // Initialization required after XMLRead
171 void FGAirportDynamics::init()
172 {
173     groundNetwork.init(_ap);
174     groundNetwork.setTowerController(&towerController);
175     
176 }
177
178 FGParking* FGAirportDynamics::innerGetAvailableParking(double radius, const string & flType,
179                                            const string & airline,
180                                            bool skipEmptyAirlineCode)
181 {
182     const FGParkingList& parkings(groundNetwork.allParkings());
183     FGParkingList::const_iterator it;
184     for (it = parkings.begin(); it != parkings.end(); ++it) {
185         FGParkingRef parking = *it;
186         if (!isParkingAvailable(parking)) {
187           continue;
188         }
189
190         if (skipEmptyAirlineCode && parking->getCodes().empty()) {
191           continue;
192         }
193
194         if (!airline.empty() && !parking->getCodes().empty()) {
195           if (parking->getCodes().find(airline, 0) == string::npos) {
196             continue;
197           }
198         }
199
200         setParkingAvailable(parking, false);
201         return parking;
202     }
203
204     return NULL;
205 }
206
207 ParkingAssignment FGAirportDynamics::getAvailableParking(double radius, const string & flType,
208                                             const string & acType,
209                                             const string & airline)
210 {
211   SG_UNUSED(acType); // sadly not used at the moment
212   
213   // most exact seach - airline codes must be present and match
214   FGParking* result = innerGetAvailableParking(radius, flType, airline, true);
215   if (result) {
216     return ParkingAssignment(result, _ap);
217   }
218   
219   // more tolerant - gates with empty airline codes are permitted
220   result = innerGetAvailableParking(radius, flType, airline, false);
221   if (result) {
222     return ParkingAssignment(result, _ap);
223   }
224
225   // fallback - ignore the airline code entirely
226   result = innerGetAvailableParking(radius, flType, string(), false);
227   return result ? ParkingAssignment(result, _ap) : ParkingAssignment();
228 }
229
230 ParkingAssignment FGAirportDynamics::getParkingByName(const std::string& name) const
231 {
232     const FGParkingList& parkings(groundNetwork.allParkings());
233     FGParkingList::const_iterator it;
234     for (it = parkings.begin(); it != parkings.end(); ++it) {
235         if ((*it)->name() == name) {
236             return ParkingAssignment(*it, _ap);
237         }
238     }
239
240   return ParkingAssignment();
241 }
242
243 void FGAirportDynamics::setParkingAvailable(FGParking* park, bool available)
244 {
245   if (available) {
246     releaseParking(park);
247   } else {
248     occupiedParkings.insert(park);
249   }
250 }
251
252 bool FGAirportDynamics::isParkingAvailable(FGParking* parking) const
253 {
254   return (occupiedParkings.find(parking) == occupiedParkings.end());
255 }
256
257 void FGAirportDynamics::releaseParking(FGParking* id)
258 {
259   ParkingSet::iterator it = occupiedParkings.find(id);
260   if (it == occupiedParkings.end()) {
261     return;
262   }
263   
264   occupiedParkings.erase(it);
265 }
266
267 class GetParkingsPredicate
268 {
269     bool mustBeAvailable;
270     std::string type;
271     const FGAirportDynamics* dynamics;
272 public:
273     GetParkingsPredicate(bool b, const std::string& ty, const FGAirportDynamics* dyn) :
274         mustBeAvailable(b),
275         type(ty),
276         dynamics(dyn)
277     {}
278
279     bool operator()(const FGParkingRef& park) const
280     {
281         if (!type.empty() && (park->getType() != type))
282             return true;
283
284         if (mustBeAvailable && !dynamics->isParkingAvailable(park)) {
285             return true;
286         }
287
288         return false;
289     }
290 };
291
292 FGParkingList FGAirportDynamics::getParkings(bool onlyAvailable, const std::string &type) const
293 {
294     FGParkingList result(groundNetwork.allParkings());
295
296     GetParkingsPredicate pred(onlyAvailable, type, this);
297     FGParkingList::iterator it = std::remove_if(result.begin(), result.end(), pred);
298     result.erase(it, result.end());
299     return result;
300 }
301
302 void FGAirportDynamics::setRwyUse(const FGRunwayPreference & ref)
303 {
304     rwyPrefs = ref;
305 }
306
307 bool FGAirportDynamics::innerGetActiveRunway(const string & trafficType,
308                                              int action, string & runway,
309                                              double heading)
310 {
311     double windSpeed;
312     double windHeading;
313     double maxTail;
314     double maxCross;
315     string name;
316     string type;
317
318     if (!rwyPrefs.available()) {
319         return false;
320     }
321
322     RunwayGroup *currRunwayGroup = 0;
323     int nrActiveRunways = 0;
324     time_t dayStart = fgGetLong("/sim/time/utc/day-seconds");
325     if ((std::abs((long) (dayStart - lastUpdate)) > 600)
326         || trafficType != prevTrafficType) {
327         landing.clear();
328         takeoff.clear();
329         lastUpdate = dayStart;
330         prevTrafficType = trafficType;
331         /*
332         FGEnvironment
333             stationweather =
334             ((FGEnvironmentMgr *) globals->get_subsystem("environment"))
335             ->getEnvironment(getLatitude(), getLongitude(),
336                              getElevation());
337         */
338         windSpeed   = fgGetInt("/environment/metar/base-wind-speed-kt"); //stationweather.get_wind_speed_kt();
339         windHeading = fgGetInt("/environment/metar/base-wind-dir-deg");
340         //stationweather.get_wind_from_heading_deg();
341         string scheduleName;
342         //cerr << "finding active Runway for : " << _ap->getId() << endl;
343         //cerr << "Wind Heading              : " << windHeading << endl;
344         //cerr << "Wind Speed                : " << windSpeed << endl;
345
346         //cerr << "Nr of seconds since day start << " << dayStart << endl;
347
348         ScheduleTime *currSched;
349         //cerr << "A"<< endl;
350         currSched = rwyPrefs.getSchedule(trafficType.c_str());
351         if (!(currSched))
352             return false;
353         //cerr << "B"<< endl;
354         scheduleName = currSched->getName(dayStart);
355         maxTail = currSched->getTailWind();
356         maxCross = currSched->getCrossWind();
357         //cerr << "Current Schedule =        : " << scheduleName << endl;
358         if (scheduleName.empty())
359             return false;
360         //cerr << "C"<< endl;
361         currRunwayGroup = rwyPrefs.getGroup(scheduleName);
362         //cerr << "D"<< endl;
363         if (!(currRunwayGroup))
364             return false;
365         nrActiveRunways = currRunwayGroup->getNrActiveRunways();
366
367         // Keep a history of the currently active runways, to ensure
368         // that an already established selection of runways will not
369         // be overridden once a more preferred selection becomes 
370         // available as that can lead to random runway swapping.
371         if (trafficType == "com") {
372             currentlyActive = &comActive;
373         } else if (trafficType == "gen") {
374             currentlyActive = &genActive;
375         } else if (trafficType == "mil") {
376             currentlyActive = &milActive;
377         } else if (trafficType == "ul") {
378             currentlyActive = &ulActive;
379         }
380
381         //cerr << "Durrently active selection for " << trafficType << ": ";
382         for (stringVecIterator it = currentlyActive->begin();
383              it != currentlyActive->end(); it++) {
384              //cerr << (*it) << " ";
385          }
386          //cerr << endl;
387
388         currRunwayGroup->setActive(_ap,
389                                    windSpeed,
390                                    windHeading,
391                                    maxTail, maxCross, currentlyActive);
392
393         // Note that I SHOULD keep multiple lists in memory, one for 
394         // general aviation, one for commercial and one for military
395         // traffic.
396         currentlyActive->clear();
397         nrActiveRunways = currRunwayGroup->getNrActiveRunways();
398         //cerr << "Choosing runway for " << trafficType << endl;
399         for (int i = 0; i < nrActiveRunways; i++) {
400             type = "unknown";   // initialize to something other than landing or takeoff
401             currRunwayGroup->getActive(i, name, type);
402             if (type == "landing") {
403                 landing.push_back(name);
404                 currentlyActive->push_back(name);
405                 //cerr << "Landing " << name << endl; 
406             }
407             if (type == "takeoff") {
408                 takeoff.push_back(name);
409                 currentlyActive->push_back(name);
410                 //cerr << "takeoff " << name << endl;
411             }
412         }
413         //cerr << endl;
414     }
415
416     if (action == 1)            // takeoff 
417     {
418         int nr = takeoff.size();
419         if (nr) {
420             // Note that the randomization below, is just a placeholder to choose between
421             // multiple active runways for this action. This should be
422             // under ATC control.
423             runway = chooseRwyByHeading(takeoff, heading);
424         } else {                // Fallback
425             runway = chooseRunwayFallback();
426         }
427     }
428
429     if (action == 2)            // landing
430     {
431         int nr = landing.size();
432         if (nr) {
433             runway = chooseRwyByHeading(landing, heading);
434         } else {                //fallback
435             runway = chooseRunwayFallback();
436         }
437     }
438
439     return true;
440 }
441
442 string FGAirportDynamics::chooseRwyByHeading(stringVec rwys,
443                                              double heading)
444 {
445     double bestError = 360.0;
446     double rwyHeading, headingError;
447     string runway;
448     for (stringVecIterator i = rwys.begin(); i != rwys.end(); i++) {
449         if (!_ap->hasRunwayWithIdent(*i)) {
450           SG_LOG(SG_ATC, SG_WARN, "chooseRwyByHeading: runway " << *i <<
451             " not found at " << _ap->ident());
452           continue;
453         }
454         
455         FGRunway *rwy = _ap->getRunwayByIdent((*i));
456         rwyHeading = rwy->headingDeg();
457         headingError = fabs(heading - rwyHeading);
458         if (headingError > 180)
459             headingError = fabs(headingError - 360);
460         if (headingError < bestError) {
461             runway = (*i);
462             bestError = headingError;
463         }
464     }
465     //cerr << "Using active runway " << runway << " for heading " << heading << endl;
466     return runway;
467 }
468
469 void FGAirportDynamics::getActiveRunway(const string & trafficType,
470                                         int action, string & runway,
471                                         double heading)
472 {
473     bool ok = innerGetActiveRunway(trafficType, action, runway, heading);
474     if (!ok) {
475         runway = chooseRunwayFallback();
476     }
477 }
478
479 string FGAirportDynamics::chooseRunwayFallback()
480 {
481     FGRunway *rwy = _ap->getActiveRunwayForUsage();
482     return rwy->ident();
483 }
484
485 double FGAirportDynamics::getElevation() const
486 {
487     return _ap->getElevation();
488 }
489
490 const string FGAirportDynamics::getId() const
491 {
492     return _ap->getId();
493 }
494
495 // Experimental: Return a different ground frequency depending on the leg of the
496 // Flight. Leg should always have a minimum value of two when this function is called. 
497 // Note that in this scheme, the assignment of various frequencies to various ground 
498 // operations is completely arbitrary. As such, is a short cut I need to take now,
499 // so that at least I can start working on assigning different frequencies to different
500 // operations.
501
502 int FGAirportDynamics::getGroundFrequency(unsigned leg)
503 {
504     //return freqGround.size() ? freqGround[0] : 0; };
505     //cerr << "Getting frequency for : " << leg << endl;
506     int groundFreq = 0;
507     if (leg < 1) {
508         SG_LOG(SG_ATC, SG_ALERT,
509                "Leg value is smaller than one at " << SG_ORIGIN);
510     }
511     if (freqGround.size() == 0) {
512         return 0;
513     }
514
515     if ((freqGround.size() < leg) && (leg > 0)) {
516         groundFreq =
517             (freqGround.size() <=
518              (leg - 1)) ? freqGround[freqGround.size() -
519                                      1] : freqGround[leg - 1];
520     }
521     if ((freqGround.size() >= leg) && (leg > 0)) {
522         groundFreq = freqGround[leg - 1];
523     }
524     return groundFreq;
525 }
526
527 int FGAirportDynamics::getTowerFrequency(unsigned nr)
528 {
529     int towerFreq = 0;
530     if (nr < 2) {
531         SG_LOG(SG_ATC, SG_ALERT,
532                "Leg value is smaller than two at " << SG_ORIGIN);
533     }
534     if (freqTower.size() == 0) {
535         return 0;
536     }
537     if ((freqTower.size() > nr - 1) && (nr > 1)) {
538         towerFreq = freqTower[nr - 1];
539     }
540     if ((freqTower.size() < nr - 1) && (nr > 1)) {
541         towerFreq =
542             (freqTower.size() <
543              (nr - 1)) ? freqTower[freqTower.size() -
544                                      1] : freqTower[nr - 2];
545     }
546     if ((freqTower.size() >= nr - 1) && (nr > 1)) {
547         towerFreq = freqTower[nr - 2];
548     }
549     return towerFreq;
550 }
551
552 const std::string FGAirportDynamics::getAtisSequence()
553 {
554    if (atisSequenceIndex == -1) {
555        updateAtisSequence(1, false);
556    }
557
558    char atisSequenceString[2];
559    atisSequenceString[0] = 'a' + atisSequenceIndex;
560    atisSequenceString[1] = 0;
561    
562    return globals->get_locale()->getLocalizedString(atisSequenceString, "atc", "unknown");
563 }
564
565 int FGAirportDynamics::updateAtisSequence(int interval, bool forceUpdate)
566 {
567     double now = globals->get_sim_time_sec();
568     if (atisSequenceIndex == -1) {
569         // first computation
570         atisSequenceTimeStamp = now;
571         atisSequenceIndex = rand() % 26; // random initial sequence letters
572         return atisSequenceIndex;
573     }
574
575     int steps = static_cast<int>((now - atisSequenceTimeStamp) / interval);
576     atisSequenceTimeStamp += (interval * steps);
577     if (forceUpdate && (steps == 0)) {
578         ++steps; // a "special" ATIS update is required
579     } 
580     
581     atisSequenceIndex = (atisSequenceIndex + steps) % 26;
582     // return a huge value if no update occurred
583     return (atisSequenceIndex + (steps ? 0 : 26*1000));
584 }