]> git.mxchange.org Git - flightgear.git/blob - src/Airports/dynamics.cxx
Alexis Bory:
[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 <Airports/runways.hxx>
42 #include <ATCDCL/ATCutils.hxx>
43
44 #include "simple.hxx"
45 #include "dynamics.hxx"
46
47 using std::string;
48 using std::vector;
49 using std::sort;
50 using std::random_shuffle;
51
52 FGAirportDynamics::FGAirportDynamics(FGAirport * ap):
53     _ap(ap), rwyPrefs(ap),
54     startupController    (this),
55     towerController      (this),
56     approachController   (this),
57     atisSequenceIndex(-1),
58     atisSequenceTimeStamp(0.0)
59
60 {
61     lastUpdate = 0;
62 }
63
64 // Destructor
65 FGAirportDynamics::~FGAirportDynamics()
66 {
67 }
68
69
70 // Initialization required after XMLRead
71 void FGAirportDynamics::init()
72 {
73     // This may seem a bit weird to first randomly shuffle the parkings
74     // and then sort them again. However, parkings are sorted here by ascending 
75     // radius. Since many parkings have similar radii, with each radius class they will
76     // still be allocated relatively systematically. Randomizing prior to sorting will
77     // prevent any initial orderings to be destroyed, leading (hopefully) to a more 
78     // naturalistic gate assignment. 
79     random_shuffle(parkings.begin(), parkings.end());
80     sort(parkings.begin(), parkings.end());
81     // add the gate positions to the ground network. 
82     groundNetwork.setParent(_ap);
83     groundNetwork.addNodes(&parkings);
84     groundNetwork.init();
85     groundNetwork.setTowerController(&towerController);
86     
87 }
88
89 int FGAirportDynamics::innerGetAvailableParking(double radius, const string & flType,
90                                            const string & acType,
91                                            const string & airline,
92                                            bool skipEmptyAirlineCode)
93 {
94   BOOST_FOREACH(FGParking* i, parkings) {
95     // Taken by another aircraft, or no airline codes
96     if (!i->isAvailable()) {
97       continue;
98     }
99     
100     if (skipEmptyAirlineCode && i->getCodes().empty()) {
101       continue;
102     }
103     
104     // check airline codes match
105     if (!airline.empty() && !i->getCodes().empty()) {
106       if (i->getCodes().find(airline, 0) == string::npos) {
107         continue;
108       }
109     }
110     
111     // Type doesn't match
112     if (i->getType() != flType) {
113       continue;
114     }
115     // too small
116     if (i->getRadius() < radius) {
117       continue;
118     }
119     
120     i->setAvailable(false);
121     return i->getIndex();
122   }
123   
124   return -1;
125 }
126
127 int FGAirportDynamics::getAvailableParking(double radius, const string & flType,
128                                             const string & acType,
129                                             const string & airline)
130 {
131     if (parkings.empty()) {
132       return -1;
133     }
134   
135   // most exact seach - airline codes must be present and match
136   int result = innerGetAvailableParking(radius, flType, acType, airline, true);
137   if (result >= 0) {
138     return result;
139   }
140   
141   // more tolerant - gates with empty airline codes are permitted
142   result = innerGetAvailableParking(radius, flType, acType, airline, false);
143   if (result >= 0) {
144     return result;
145   }
146
147   // fallback - ignore the airline code entirely
148   return innerGetAvailableParking(radius, flType, acType, string(), false);
149 }
150
151 FGParking *FGAirportDynamics::getParking(int id)
152 {
153   BOOST_FOREACH(FGParking* i, parkings) {
154     if (id == i->getIndex()) {
155       return i;
156     }
157   }
158   
159   return NULL;
160 }
161
162 string FGAirportDynamics::getParkingName(int id)
163 {
164   FGParking* p = getParking(id);
165   if (p) {
166     return p->getName();
167   }
168   
169   return string();
170 }
171
172 int FGAirportDynamics::findParkingByName(const std::string& name) const
173 {
174   BOOST_FOREACH(FGParking* i, parkings) {
175     if (name == i->getName()) {
176       return i->getIndex();
177     }
178   }
179
180   return -1;
181 }
182
183 void FGAirportDynamics::releaseParking(int id)
184 {
185     if (id >= 0) {
186       FGParking* parking = getParking(id);
187       if (parking) {
188         parking->setAvailable(true);
189       }
190     }
191 }
192
193 void FGAirportDynamics::setRwyUse(const FGRunwayPreference & ref)
194 {
195     rwyPrefs = ref;
196 }
197
198 bool FGAirportDynamics::innerGetActiveRunway(const string & trafficType,
199                                              int action, string & runway,
200                                              double heading)
201 {
202     double windSpeed;
203     double windHeading;
204     double maxTail;
205     double maxCross;
206     string name;
207     string type;
208
209     if (!rwyPrefs.available()) {
210         return false;
211     }
212
213     RunwayGroup *currRunwayGroup = 0;
214     int nrActiveRunways = 0;
215     time_t dayStart = fgGetLong("/sim/time/utc/day-seconds");
216     if ((abs((long) (dayStart - lastUpdate)) > 600)
217         || trafficType != prevTrafficType) {
218         landing.clear();
219         takeoff.clear();
220         lastUpdate = dayStart;
221         prevTrafficType = trafficType;
222         /*
223         FGEnvironment
224             stationweather =
225             ((FGEnvironmentMgr *) globals->get_subsystem("environment"))
226             ->getEnvironment(getLatitude(), getLongitude(),
227                              getElevation());
228         */
229         windSpeed   = fgGetInt("/environment/metar/base-wind-speed-kt"); //stationweather.get_wind_speed_kt();
230         windHeading = fgGetInt("/environment/metar/base-wind-dir-deg");
231         //stationweather.get_wind_from_heading_deg();
232         string scheduleName;
233         //cerr << "finding active Runway for : " << _ap->getId() << endl;
234         //cerr << "Wind Heading              : " << windHeading << endl;
235         //cerr << "Wind Speed                : " << windSpeed << endl;
236
237         //cerr << "Nr of seconds since day start << " << dayStart << endl;
238
239         ScheduleTime *currSched;
240         //cerr << "A"<< endl;
241         currSched = rwyPrefs.getSchedule(trafficType.c_str());
242         if (!(currSched))
243             return false;
244         //cerr << "B"<< endl;
245         scheduleName = currSched->getName(dayStart);
246         maxTail = currSched->getTailWind();
247         maxCross = currSched->getCrossWind();
248         //cerr << "Current Schedule =        : " << scheduleName << endl;
249         if (scheduleName.empty())
250             return false;
251         //cerr << "C"<< endl;
252         currRunwayGroup = rwyPrefs.getGroup(scheduleName);
253         //cerr << "D"<< endl;
254         if (!(currRunwayGroup))
255             return false;
256         nrActiveRunways = currRunwayGroup->getNrActiveRunways();
257
258         // Keep a history of the currently active runways, to ensure
259         // that an already established selection of runways will not
260         // be overridden once a more preferred selection becomes 
261         // available as that can lead to random runway swapping.
262         if (trafficType == "com") {
263             currentlyActive = &comActive;
264         } else if (trafficType == "gen") {
265             currentlyActive = &genActive;
266         } else if (trafficType == "mil") {
267             currentlyActive = &milActive;
268         } else if (trafficType == "ul") {
269             currentlyActive = &ulActive;
270         }
271
272         //cerr << "Durrently active selection for " << trafficType << ": ";
273         for (stringVecIterator it = currentlyActive->begin();
274              it != currentlyActive->end(); it++) {
275              //cerr << (*it) << " ";
276          }
277          //cerr << endl;
278
279         currRunwayGroup->setActive(_ap,
280                                    windSpeed,
281                                    windHeading,
282                                    maxTail, maxCross, currentlyActive);
283
284         // Note that I SHOULD keep multiple lists in memory, one for 
285         // general aviation, one for commercial and one for military
286         // traffic.
287         currentlyActive->clear();
288         nrActiveRunways = currRunwayGroup->getNrActiveRunways();
289         //cerr << "Choosing runway for " << trafficType << endl;
290         for (int i = 0; i < nrActiveRunways; i++) {
291             type = "unknown";   // initialize to something other than landing or takeoff
292             currRunwayGroup->getActive(i, name, type);
293             if (type == "landing") {
294                 landing.push_back(name);
295                 currentlyActive->push_back(name);
296                 //cerr << "Landing " << name << endl; 
297             }
298             if (type == "takeoff") {
299                 takeoff.push_back(name);
300                 currentlyActive->push_back(name);
301                 //cerr << "takeoff " << name << endl;
302             }
303         }
304         //cerr << endl;
305     }
306
307     if (action == 1)            // takeoff 
308     {
309         int nr = takeoff.size();
310         if (nr) {
311             // Note that the randomization below, is just a placeholder to choose between
312             // multiple active runways for this action. This should be
313             // under ATC control.
314             runway = chooseRwyByHeading(takeoff, heading);
315         } else {                // Fallback
316             runway = chooseRunwayFallback();
317         }
318     }
319
320     if (action == 2)            // landing
321     {
322         int nr = landing.size();
323         if (nr) {
324             runway = chooseRwyByHeading(landing, heading);
325         } else {                //fallback
326             runway = chooseRunwayFallback();
327         }
328     }
329
330     return true;
331 }
332
333 string FGAirportDynamics::chooseRwyByHeading(stringVec rwys,
334                                              double heading)
335 {
336     double bestError = 360.0;
337     double rwyHeading, headingError;
338     string runway;
339     for (stringVecIterator i = rwys.begin(); i != rwys.end(); i++) {
340         if (!_ap->hasRunwayWithIdent(*i)) {
341           SG_LOG(SG_ATC, SG_WARN, "chooseRwyByHeading: runway " << *i <<
342             " not found at " << _ap->ident());
343           continue;
344         }
345         
346         FGRunway *rwy = _ap->getRunwayByIdent((*i));
347         rwyHeading = rwy->headingDeg();
348         headingError = fabs(heading - rwyHeading);
349         if (headingError > 180)
350             headingError = fabs(headingError - 360);
351         if (headingError < bestError) {
352             runway = (*i);
353             bestError = headingError;
354         }
355     }
356     //cerr << "Using active runway " << runway << " for heading " << heading << endl;
357     return runway;
358 }
359
360 void FGAirportDynamics::getActiveRunway(const string & trafficType,
361                                         int action, string & runway,
362                                         double heading)
363 {
364     bool ok = innerGetActiveRunway(trafficType, action, runway, heading);
365     if (!ok) {
366         runway = chooseRunwayFallback();
367     }
368 }
369
370 string FGAirportDynamics::chooseRunwayFallback()
371 {
372     FGRunway *rwy = _ap->getActiveRunwayForUsage();
373     return rwy->ident();
374 }
375
376 void FGAirportDynamics::addParking(FGParking* park)
377 {
378     parkings.push_back(park);
379 }
380
381 double FGAirportDynamics::getElevation() const
382 {
383     return _ap->getElevation();
384 }
385
386 const string FGAirportDynamics::getId() const
387 {
388     return _ap->getId();
389 }
390
391 // Experimental: Return a different ground frequency depending on the leg of the
392 // Flight. Leg should always have a minimum value of two when this function is called. 
393 // Note that in this scheme, the assignment of various frequencies to various ground 
394 // operations is completely arbitrary. As such, is a short cut I need to take now,
395 // so that at least I can start working on assigning different frequencies to different
396 // operations.
397
398 int FGAirportDynamics::getGroundFrequency(unsigned leg)
399 {
400     //return freqGround.size() ? freqGround[0] : 0; };
401     //cerr << "Getting frequency for : " << leg << endl;
402     int groundFreq = 0;
403     if (leg < 1) {
404         SG_LOG(SG_ATC, SG_ALERT,
405                "Leg value is smaller than one at " << SG_ORIGIN);
406     }
407     if (freqGround.size() == 0) {
408         return 0;
409     }
410
411     if ((freqGround.size() < leg) && (leg > 0)) {
412         groundFreq =
413             (freqGround.size() <=
414              (leg - 1)) ? freqGround[freqGround.size() -
415                                      1] : freqGround[leg - 1];
416     }
417     if ((freqGround.size() >= leg) && (leg > 0)) {
418         groundFreq = freqGround[leg - 1];
419     }
420     return groundFreq;
421 }
422
423 int FGAirportDynamics::getTowerFrequency(unsigned nr)
424 {
425     int towerFreq = 0;
426     if (nr < 2) {
427         SG_LOG(SG_ATC, SG_ALERT,
428                "Leg value is smaller than two at " << SG_ORIGIN);
429     }
430     if (freqTower.size() == 0) {
431         return 0;
432     }
433     if ((freqTower.size() > nr - 1) && (nr > 1)) {
434         towerFreq = freqTower[nr - 1];
435     }
436     if ((freqTower.size() < nr - 1) && (nr > 1)) {
437         towerFreq =
438             (freqTower.size() <
439              (nr - 1)) ? freqTower[freqTower.size() -
440                                      1] : freqTower[nr - 2];
441     }
442     if ((freqTower.size() >= nr - 1) && (nr > 1)) {
443         towerFreq = freqTower[nr - 2];
444     }
445     return towerFreq;
446 }
447
448 const std::string FGAirportDynamics::getAtisSequence()
449 {
450    if (atisSequenceIndex == -1) {
451        updateAtisSequence(1, false);
452    }
453    
454    return GetPhoneticLetter(atisSequenceIndex);
455 }
456
457 int FGAirportDynamics::updateAtisSequence(int interval, bool forceUpdate)
458 {
459     double now = globals->get_sim_time_sec();
460     if (atisSequenceIndex == -1) {
461         // first computation
462         atisSequenceTimeStamp = now;
463         atisSequenceIndex = rand() % LTRS; // random initial sequence letters
464         return atisSequenceIndex;
465     }
466
467     int steps = static_cast<int>((now - atisSequenceTimeStamp) / interval);
468     atisSequenceTimeStamp += (interval * steps);
469     if (forceUpdate && (steps == 0)) {
470         ++steps; // a "special" ATIS update is required
471     } 
472     
473     atisSequenceIndex = (atisSequenceIndex + steps) % LTRS;
474     // return a huge value if no update occurred
475     return (atisSequenceIndex + (steps ? 0 : LTRS*1000));
476 }