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