]> git.mxchange.org Git - flightgear.git/blob - src/Airports/dynamics.cxx
Fix file access mode for newnavradio.[ch]xx
[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
27 #include <simgear/compiler.h>
28
29 #include <Environment/environment_mgr.hxx>
30 #include <Environment/environment.hxx>
31 #include <simgear/misc/sg_path.hxx>
32 #include <simgear/props/props.hxx>
33 #include <simgear/structure/subsystem_mgr.hxx>
34 #include <simgear/debug/logstream.hxx>
35 #include <simgear/route/waypoint.hxx>
36 #include <Main/globals.hxx>
37 #include <Main/fg_props.hxx>
38 #include <Airports/runways.hxx>
39 #include <ATCDCL/ATCutils.hxx>
40
41 #include <string>
42 #include <vector>
43
44 using std::string;
45 using std::vector;
46 using std::sort;
47 using std::random_shuffle;
48
49 #include "simple.hxx"
50 #include "dynamics.hxx"
51
52 FGAirportDynamics::FGAirportDynamics(FGAirport * ap):
53     _ap(ap), rwyPrefs(ap), SIDs(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 bool FGAirportDynamics::getAvailableParking(double *lat, double *lon,
90                                             double *heading, int *gateId,
91                                             double rad,
92                                             const string & flType,
93                                             const string & acType,
94                                             const string & airline)
95 {
96     bool found = false;
97     bool available = false;
98
99
100     FGParkingVecIterator i;
101     if (parkings.begin() == parkings.end()) {
102         //cerr << "Could not find parking spot at " << _ap->getId() << endl;
103         *lat = _ap->getLatitude();
104         *lon = _ap->getLongitude();
105         * gateId = -1;
106         *heading = 0;
107         found = true;
108     } else {
109         // First try finding a parking with a designated airline code
110         for (i = parkings.begin(); !(i == parkings.end() || found); i++) {
111             available = true;
112             // Taken by another aircraft
113             if (!(i->isAvailable())) {
114                 available = false;
115                 continue;
116             }
117             // No airline codes, so skip
118             if (i->getCodes().empty()) {
119                 available = false;
120                 continue;
121             } else {             // Airline code doesn't match
122                 //cerr << "Code = " << airline << ": Codes " << i->getCodes();
123                 if (i->getCodes().find(airline, 0) == string::npos) {
124                     available = false;
125                     //cerr << "Unavailable" << endl;
126                     continue;
127                 } else {
128                     //cerr << "Available" << endl;
129                 }
130             }
131             // Type doesn't match
132             if (i->getType() != flType) {
133                 available = false;
134                 continue;
135             }
136             // too small
137             if (i->getRadius() < rad) {
138                 available = false;
139                 continue;
140             }
141
142             if (available) {
143                 *lat = i->getLatitude();
144                 *lon = i->getLongitude();
145                 *heading = i->getHeading();
146                 *gateId = i->getIndex();
147                 i->setAvailable(false);
148                 found = true;
149             }
150         }
151         // then try again for those without codes. 
152         for (i = parkings.begin(); !(i == parkings.end() || found); i++) {
153             available = true;
154             if (!(i->isAvailable())) {
155                 available = false;
156                 continue;
157             }
158             if (!(i->getCodes().empty())) {
159                 if ((i->getCodes().find(airline, 0) == string::npos)) {
160                     available = false;
161                     continue;
162                 }
163             }
164             if (i->getType() != flType) {
165                 available = false;
166                 continue;
167             }
168
169             if (i->getRadius() < rad) {
170                 available = false;
171                 continue;
172             }
173
174             if (available) {
175                 *lat = i->getLatitude();
176                 *lon = i->getLongitude();
177                 *heading = i->getHeading();
178                 *gateId = i->getIndex();
179                 i->setAvailable(false);
180                 found = true;
181             }
182         }
183         // And finally once more if that didn't work. Now ignore the airline codes, as a last resort
184         for (i = parkings.begin(); !(i == parkings.end() || found); i++) {
185             available = true;
186             if (!(i->isAvailable())) {
187                 available = false;
188                 continue;
189             }
190             if (i->getType() != flType) {
191                 available = false;
192                 continue;
193             }
194
195             if (i->getRadius() < rad) {
196                 available = false;
197                 continue;
198             }
199
200             if (available) {
201                 *lat = i->getLatitude();
202                 *lon = i->getLongitude();
203                 *heading = i->getHeading();
204                 *gateId = i->getIndex();
205                 i->setAvailable(false);
206                 found = true;
207             }
208         }
209     }
210     if (!found) {
211         //cerr << "Traffic overflow at" << _ap->getId() 
212         //           << ". flType = " << flType 
213         //           << ". airline = " << airline 
214         //           << " Radius = " <<rad
215         //           << endl;
216         *lat = _ap->getLatitude();
217         *lon = _ap->getLongitude();
218         *heading = 0;
219         *gateId = -1;
220         //exit(1);
221     }
222     return found;
223 }
224
225 void FGAirportDynamics::getParking(int id, double *lat, double *lon,
226                                    double *heading)
227 {
228     if (id < 0) {
229         *lat = _ap->getLatitude();
230         *lon = _ap->getLongitude();
231         *heading = 0;
232     } else {
233         FGParkingVecIterator i = parkings.begin();
234         for (i = parkings.begin(); i != parkings.end(); i++) {
235             if (id == i->getIndex()) {
236                 *lat = i->getLatitude();
237                 *lon = i->getLongitude();
238                 *heading = i->getHeading();
239             }
240         }
241     }
242 }
243
244 FGParking *FGAirportDynamics::getParking(int id)
245 {
246     FGParkingVecIterator i = parkings.begin();
247     for (i = parkings.begin(); i != parkings.end(); i++) {
248         if (id == i->getIndex()) {
249             return &(*i);
250         }
251     }
252     return 0;
253 }
254
255 string FGAirportDynamics::getParkingName(int id)
256 {
257     FGParkingVecIterator i = parkings.begin();
258     for (i = parkings.begin(); i != parkings.end(); i++) {
259         if (id == i->getIndex()) {
260             return i->getName();
261         }
262     }
263
264     return string("overflow");
265 }
266
267 void FGAirportDynamics::releaseParking(int id)
268 {
269     if (id >= 0) {
270
271         FGParkingVecIterator i = parkings.begin();
272         for (i = parkings.begin(); i != parkings.end(); i++) {
273             if (id == i->getIndex()) {
274                 i->setAvailable(true);
275             }
276         }
277     }
278 }
279
280 void FGAirportDynamics::setRwyUse(const FGRunwayPreference & ref)
281 {
282     rwyPrefs = ref;
283     //cerr << "Exiting due to not implemented yet" << endl;
284     //exit(1);
285 }
286
287 bool FGAirportDynamics::innerGetActiveRunway(const string & trafficType,
288                                              int action, string & runway,
289                                              double heading)
290 {
291     double windSpeed;
292     double windHeading;
293     double maxTail;
294     double maxCross;
295     string name;
296     string type;
297
298     if (!rwyPrefs.available()) {
299         return false;
300     }
301
302     RunwayGroup *currRunwayGroup = 0;
303     int nrActiveRunways = 0;
304     time_t dayStart = fgGetLong("/sim/time/utc/day-seconds");
305     if ((abs((long) (dayStart - lastUpdate)) > 600)
306         || trafficType != prevTrafficType) {
307         landing.clear();
308         takeoff.clear();
309         lastUpdate = dayStart;
310         prevTrafficType = trafficType;
311         /*
312         FGEnvironment
313             stationweather =
314             ((FGEnvironmentMgr *) globals->get_subsystem("environment"))
315             ->getEnvironment(getLatitude(), getLongitude(),
316                              getElevation());
317         */
318         windSpeed   = fgGetInt("/environment/metar/base-wind-speed-kt"); //stationweather.get_wind_speed_kt();
319         windHeading = fgGetInt("/environment/metar/base-wind-dir-deg");
320         //stationweather.get_wind_from_heading_deg();
321         string scheduleName;
322         //cerr << "finding active Runway for : " << _ap->getId() << endl;
323         //cerr << "Wind Heading              : " << windHeading << endl;
324         //cerr << "Wind Speed                : " << windSpeed << endl;
325
326         //cerr << "Nr of seconds since day start << " << dayStart << endl;
327
328         ScheduleTime *currSched;
329         //cerr << "A"<< endl;
330         currSched = rwyPrefs.getSchedule(trafficType.c_str());
331         if (!(currSched))
332             return false;
333         //cerr << "B"<< endl;
334         scheduleName = currSched->getName(dayStart);
335         maxTail = currSched->getTailWind();
336         maxCross = currSched->getCrossWind();
337         //cerr << "Current Schedule =        : " << scheduleName << endl;
338         if (scheduleName.empty())
339             return false;
340         //cerr << "C"<< endl;
341         currRunwayGroup = rwyPrefs.getGroup(scheduleName);
342         //cerr << "D"<< endl;
343         if (!(currRunwayGroup))
344             return false;
345         nrActiveRunways = currRunwayGroup->getNrActiveRunways();
346
347         // Keep a history of the currently active runways, to ensure
348         // that an already established selection of runways will not
349         // be overridden once a more preferred selection becomes 
350         // available as that can lead to random runway swapping.
351         if (trafficType == "com") {
352             currentlyActive = &comActive;
353         } else if (trafficType == "gen") {
354             currentlyActive = &genActive;
355         } else if (trafficType == "mil") {
356             currentlyActive = &milActive;
357         } else if (trafficType == "ul") {
358             currentlyActive = &ulActive;
359         }
360
361         //cerr << "Durrently active selection for " << trafficType << ": ";
362         for (stringVecIterator it = currentlyActive->begin();
363              it != currentlyActive->end(); it++) {
364              //cerr << (*it) << " ";
365          }
366          //cerr << endl;
367
368         currRunwayGroup->setActive(_ap,
369                                    windSpeed,
370                                    windHeading,
371                                    maxTail, maxCross, currentlyActive);
372
373         // Note that I SHOULD keep multiple lists in memory, one for 
374         // general aviation, one for commercial and one for military
375         // traffic.
376         currentlyActive->clear();
377         nrActiveRunways = currRunwayGroup->getNrActiveRunways();
378         //cerr << "Choosing runway for " << trafficType << endl;
379         for (int i = 0; i < nrActiveRunways; i++) {
380             type = "unknown";   // initialize to something other than landing or takeoff
381             currRunwayGroup->getActive(i, name, type);
382             if (type == "landing") {
383                 landing.push_back(name);
384                 currentlyActive->push_back(name);
385                 //cerr << "Landing " << name << endl; 
386             }
387             if (type == "takeoff") {
388                 takeoff.push_back(name);
389                 currentlyActive->push_back(name);
390                 //cerr << "takeoff " << name << endl;
391             }
392         }
393         //cerr << endl;
394     }
395
396     if (action == 1)            // takeoff 
397     {
398         int nr = takeoff.size();
399         if (nr) {
400             // Note that the randomization below, is just a placeholder to choose between
401             // multiple active runways for this action. This should be
402             // under ATC control.
403             runway = chooseRwyByHeading(takeoff, heading);
404         } else {                // Fallback
405             runway = chooseRunwayFallback();
406         }
407     }
408
409     if (action == 2)            // landing
410     {
411         int nr = landing.size();
412         if (nr) {
413             runway = chooseRwyByHeading(landing, heading);
414         } else {                //fallback
415             runway = chooseRunwayFallback();
416         }
417     }
418
419     return true;
420 }
421
422 string FGAirportDynamics::chooseRwyByHeading(stringVec rwys,
423                                              double heading)
424 {
425     double bestError = 360.0;
426     double rwyHeading, headingError;
427     string runway;
428     for (stringVecIterator i = rwys.begin(); i != rwys.end(); i++) {
429         if (!_ap->hasRunwayWithIdent(*i)) {
430           SG_LOG(SG_ATC, SG_WARN, "chooseRwyByHeading: runway " << *i <<
431             " not found at " << _ap->ident());
432           continue;
433         }
434         
435         FGRunway *rwy = _ap->getRunwayByIdent((*i));
436         rwyHeading = rwy->headingDeg();
437         headingError = fabs(heading - rwyHeading);
438         if (headingError > 180)
439             headingError = fabs(headingError - 360);
440         if (headingError < bestError) {
441             runway = (*i);
442             bestError = headingError;
443         }
444     }
445     //cerr << "Using active runway " << runway << " for heading " << heading << endl;
446     return runway;
447 }
448
449 void FGAirportDynamics::getActiveRunway(const string & trafficType,
450                                         int action, string & runway,
451                                         double heading)
452 {
453     bool ok = innerGetActiveRunway(trafficType, action, runway, heading);
454     if (!ok) {
455         runway = chooseRunwayFallback();
456     }
457 }
458
459 string FGAirportDynamics::chooseRunwayFallback()
460 {
461     FGRunway *rwy = _ap->getActiveRunwayForUsage();
462     return rwy->ident();
463 }
464
465 void FGAirportDynamics::addParking(FGParking & park)
466 {
467     parkings.push_back(park);
468 }
469
470 double FGAirportDynamics::getLatitude() const
471 {
472     return _ap->getLatitude();
473 }
474
475 double FGAirportDynamics::getLongitude() const
476 {
477     return _ap->getLongitude();
478 }
479
480 double FGAirportDynamics::getElevation() const
481 {
482     return _ap->getElevation();
483 }
484
485 const string & FGAirportDynamics::getId() const
486 {
487     return _ap->getId();
488 }
489
490 // Experimental: Return a different ground frequency depending on the leg of the
491 // Flight. Leg should always have a minimum value of two when this function is called. 
492 // Note that in this scheme, the assignment of various frequencies to various ground 
493 // operations is completely arbitrary. As such, is a short cut I need to take now,
494 // so that at least I can start working on assigning different frequencies to different
495 // operations.
496
497 int FGAirportDynamics::getGroundFrequency(unsigned leg)
498 {
499     //return freqGround.size() ? freqGround[0] : 0; };
500     //cerr << "Getting frequency for : " << leg << endl;
501     int groundFreq = 0;
502     if (leg < 1) {
503         SG_LOG(SG_ATC, SG_ALERT,
504                "Leg value is smaller than one at " << SG_ORIGIN);
505     }
506     if (freqGround.size() == 0) {
507         return 0;
508     }
509
510     if ((freqGround.size() < leg) && (leg > 0)) {
511         groundFreq =
512             (freqGround.size() <=
513              (leg - 1)) ? freqGround[freqGround.size() -
514                                      1] : freqGround[leg - 1];
515     }
516     if ((freqGround.size() >= leg) && (leg > 0)) {
517         groundFreq = freqGround[leg - 1];
518     }
519     return groundFreq;
520 }
521
522 int FGAirportDynamics::getTowerFrequency(unsigned nr)
523 {
524     int towerFreq = 0;
525     if (nr < 2) {
526         SG_LOG(SG_ATC, SG_ALERT,
527                "Leg value is smaller than two at " << SG_ORIGIN);
528     }
529     if (freqTower.size() == 0) {
530         return 0;
531     }
532     if ((freqTower.size() > nr - 1) && (nr > 1)) {
533         towerFreq = freqTower[nr - 1];
534     }
535     if ((freqTower.size() < nr - 1) && (nr > 1)) {
536         towerFreq =
537             (freqTower.size() <
538              (nr - 1)) ? freqTower[freqTower.size() -
539                                      1] : freqTower[nr - 2];
540     }
541     if ((freqTower.size() >= nr - 1) && (nr > 1)) {
542         towerFreq = freqTower[nr - 2];
543     }
544     return towerFreq;
545 }
546
547
548 FGAIFlightPlan *FGAirportDynamics::getSID(string activeRunway,
549                                           double heading)
550 {
551     return SIDs.getBest(activeRunway, heading);
552 }
553
554 const std::string FGAirportDynamics::getAtisSequence()
555 {
556    if (atisSequenceIndex == -1) {
557        updateAtisSequence(1, false);
558    }
559    
560    return GetPhoneticLetter(atisSequenceIndex);
561 }
562
563 int FGAirportDynamics::updateAtisSequence(int interval, bool forceUpdate)
564 {
565     double now = globals->get_sim_time_sec();
566     if (atisSequenceIndex == -1) {
567         // first computation
568         atisSequenceTimeStamp = now;
569         atisSequenceIndex = rand() % LTRS; // random initial sequence letters
570         return atisSequenceIndex;
571     }
572
573     int steps = static_cast<int>((now - atisSequenceTimeStamp) / interval);
574     atisSequenceTimeStamp += (interval * steps);
575     if (forceUpdate && (steps == 0)) {
576         ++steps; // a "special" ATIS update is required
577     } 
578     
579     atisSequenceIndex = (atisSequenceIndex + steps) % LTRS;
580     // return a huge value if no update occurred
581     return (atisSequenceIndex + (steps ? 0 : LTRS*1000));
582 }