]> git.mxchange.org Git - flightgear.git/blob - src/Airports/runwayprefs.cxx
Fix for a misplaced boolean assignment that caused erratic runway selection.
[flightgear.git] / src / Airports / runwayprefs.cxx
1 // runwayprefs.cxx - class implementations corresponding to runwayprefs.hxx
2 // assignments by the AI code
3 //
4 // Written by Durk Talsma, started January 2005.
5 //
6 // Copyright (C) 2004 Durk Talsma.
7 //
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License as
10 // published by the Free Software Foundation; either version 2 of the
11 // License, or (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful, but
14 // WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 // General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the Free Software
20 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 //
22 // $Id$
23
24 #ifdef HAVE_CONFIG_H
25 #  include <config.h>
26 #endif
27
28 #include <math.h>
29 //#include <algorithm>
30
31 #include <simgear/compiler.h>
32
33 //#include <plib/sg.h>
34 //#include <plib/ul.h>
35
36 //#include <Environment/environment_mgr.hxx>
37 //#include <Environment/environment.hxx>
38 //#include <simgear/misc/sg_path.hxx>
39 //#include <simgear/props/props.hxx>
40 //#include <simgear/structure/subsystem_mgr.hxx>
41 #include <simgear/debug/logstream.hxx>
42 #include <Main/globals.hxx>
43 //#include <Main/fg_props.hxx>
44 #include <Airports/runways.hxx>
45
46 #include "runwayprefs.hxx"
47
48 /******************************************************************************
49  * ScheduleTime
50  ***************e*************************************************************/
51 void ScheduleTime::clear()
52
53   start.clear();
54   end.clear();
55   scheduleNames.clear();
56 }
57
58
59 ScheduleTime::ScheduleTime(const ScheduleTime &other) 
60 {
61   //timeVec   start;
62   timeVecConstIterator i;
63   for (i = other.start.begin(); i != other.start.end(); i++)
64     start.push_back(*i);
65    for (i = other.end.begin(); i != other.end.end(); i++)
66     end.push_back(*i);
67    stringVecConstIterator k;
68    for (k = other.scheduleNames.begin(); k != other.scheduleNames.end(); k++)
69      scheduleNames.push_back(*k);
70   
71   //timeVec   end;
72   //stringVec scheduleNames;
73   tailWind = other.tailWind;
74   crssWind = other.tailWind;
75 }
76
77
78 ScheduleTime & ScheduleTime::operator= (const ScheduleTime &other) 
79 {
80   //timeVec   start;
81   clear();
82   timeVecConstIterator i;
83   for (i = other.start.begin(); i != other.start.end(); i++)
84     start.push_back(*i);
85    for (i = other.end.begin(); i != other.end.end(); i++)
86     end.push_back(*i);
87    stringVecConstIterator k;
88    for (k = other.scheduleNames.begin(); k != other.scheduleNames.end(); k++)
89      scheduleNames.push_back(*k);
90   
91   //timeVec   end;
92   //stringVec scheduleNames;
93   tailWind = other.tailWind;
94   crssWind = other.tailWind;
95   return *this;
96 }
97 string ScheduleTime::getName(time_t dayStart)
98 {
99   if ((start.size() != end.size()) || (start.size() != scheduleNames.size()))
100     {
101       SG_LOG( SG_GENERAL, SG_INFO, "Unable to parse schedule times" );
102       exit(1);
103     }
104   else
105     {
106       int nrItems = start.size();
107       //cerr << "Nr of items to process: " << nrItems << endl;
108       if (nrItems > 0)
109         {
110           for (unsigned int i = 0; i < start.size(); i++)
111             {
112               //cerr << i << endl;
113               if ((dayStart >= start[i]) && (dayStart <= end[i]))
114                 return scheduleNames[i];
115             }
116         }
117       //couldn't find one so return 0;
118       //cerr << "Returning 0 " << endl;
119     }
120     return string(0);
121 }                             
122 /******************************************************************************
123  * RunwayList
124  *****************************************************************************/
125
126 RunwayList::RunwayList(const RunwayList &other)
127 {
128   type = other.type;
129   stringVecConstIterator i;
130   for (i = other.preferredRunways.begin(); i != other.preferredRunways.end(); i++)
131     preferredRunways.push_back(*i);
132 }
133 RunwayList& RunwayList::operator= (const RunwayList &other)
134 {
135   type = other.type;
136   preferredRunways.clear();
137   stringVecConstIterator i;
138   for (i = other.preferredRunways.begin(); i != other.preferredRunways.end(); i++)
139     preferredRunways.push_back(*i);
140   return *this;
141 }
142 void RunwayList::set(const string &tp, const string &lst)
143 {
144   //weekday          = atoi(timeCopy.substr(0,1).c_str());
145   //    timeOffsetInDays = weekday - currTimeDate->getGmt()->tm_wday;
146   //    timeCopy = timeCopy.substr(2,timeCopy.length());
147   type = tp;
148   string rwys = lst;
149   string rwy;
150   while (rwys.find(",") != string::npos)
151     {
152       rwy = rwys.substr(0, rwys.find(",",0));
153       //cerr << "adding runway [" << rwy << "] to the list " << endl;
154       preferredRunways.push_back(rwy);
155       rwys.erase(0, rwys.find(",",0)+1); // erase until after the first whitspace
156       while (rwys[0] == ' ')
157         rwys.erase(0, 1); // Erase any leading whitespaces.
158       //cerr << "Remaining runway list " << rwys;
159     } 
160   preferredRunways.push_back(rwys);
161   //exit(1);
162 }
163
164 void RunwayList::clear() 
165 {
166   type = "";
167   preferredRunways.clear();
168 }
169 /****************************************************************************
170  *
171  ***************************************************************************/
172
173 RunwayGroup::RunwayGroup(const RunwayGroup &other)
174 {
175   name = other.name; 
176   RunwayListVecConstIterator i;
177   for (i = other.rwyList.begin(); i != other.rwyList.end(); i++)
178     rwyList.push_back(*i);
179   choice[0] = other.choice[0];
180   choice[1] = other.choice[1];
181   nrActive = other.nrActive;
182 }
183 RunwayGroup& RunwayGroup:: operator= (const RunwayGroup &other)
184
185   rwyList.clear();
186   name = other.name; 
187   RunwayListVecConstIterator i;
188   for (i = other.rwyList.begin(); i != other.rwyList.end(); i++)
189     rwyList.push_back(*i); 
190   choice[0] = other.choice[0];
191   choice[1] = other.choice[1];
192   nrActive = other.nrActive;
193   return *this;
194 }
195
196 void RunwayGroup::setActive(const string &aptId, 
197                             double windSpeed, 
198                             double windHeading, 
199                             double maxTail, 
200                             double maxCross,
201                             stringVec *currentlyActive)
202 {
203
204   FGRunway rwy;
205   int activeRwys = rwyList.size(); // get the number of runways active
206   int nrOfPreferences;
207   // bool found = true;
208   // double heading;
209   double hdgDiff;
210   double crossWind;
211   double tailWind;
212   string name;
213   //stringVec names;
214   int bestMatch = 0, bestChoice = 0;
215
216   if (activeRwys > 0)
217     {  
218       // Now downward iterate across all the possible preferences
219       // starting by the least preferred choice working toward the most preferred choice
220
221       nrOfPreferences = rwyList[0].getRwyList()->size();
222       bool validSelection = true;
223       bool foundValidSelection = false;
224       for (int i = nrOfPreferences-1; i >= 0; i--)
225         {
226           int match = 0;
227           
228
229           // Test each runway listed in the preference to see if it's possible to use
230           // If one runway of the selection isn't allowed, we need to exclude this
231           // preference, however, we don't want to stop right there, because we also
232           // don't want to randomly swap runway preferences, unless there is a need to. 
233           //
234           validSelection = true;
235           for (int j = 0; j < activeRwys; j++)
236             {
237              
238               name = rwyList[j].getRwyList(i);
239               //cerr << "Name of Runway: " << name << endl;
240               if (globals->get_runways()->search( aptId, 
241                                                   name, 
242                                                   &rwy))
243                 {
244                   //cerr << "Succes" << endl;
245                   hdgDiff = fabs(windHeading - rwy._heading);
246                   //cerr << "Wind Heading: " << windHeading << "Runway Heading: " <<rwy._heading << endl;
247                   //cerr << "Wind Speed  : " << windSpeed << endl;
248                   if (hdgDiff > 180)
249                     hdgDiff = 360 - hdgDiff;
250                   //cerr << "Heading diff: " << hdgDiff << endl;
251                   hdgDiff *= ((2*M_PI)/360.0); // convert to radians
252                   crossWind = windSpeed * sin(hdgDiff);
253                   tailWind  = -windSpeed * cos(hdgDiff);
254                   //cerr << "Tailwind : " << tailWind << endl;
255                   //cerr << "Crosswnd : " << crossWind << endl;
256                   if ((tailWind > maxTail) || (crossWind > maxCross))
257                     {
258                       //cerr << "Invalid : ";
259                       validSelection = false;
260                    }
261                   else 
262                     {
263                       //cerr << "Valid   : ";
264                   }
265                 }else {
266                 SG_LOG( SG_GENERAL, SG_INFO, "Failed to find runway " << name << " at " << aptId );
267                 exit(1);
268               }
269             }
270           if (validSelection) 
271             {
272               //cerr << "Valid   : ";
273               foundValidSelection = true;
274               for (stringVecIterator it = currentlyActive->begin(); 
275                    it != currentlyActive->end(); it++)
276                 {
277                   if ((*it) == name)
278                     match++;
279                   if (match >= bestMatch) {
280                     bestMatch = match;
281                     bestChoice = i;
282                   }
283                 }
284             } 
285           //cerr << "Preference " << i << " bestMatch " << bestMatch << " choice " << bestChoice << endl;
286         }
287       if (foundValidSelection)
288         {
289           //cerr << "Valid runay selection : " << bestChoice << endl;
290           nrActive = activeRwys;
291           active = bestChoice;
292           return;
293         }
294       // If this didn't work, due to heavy winds, try again
295       // but select only one landing and one takeoff runway. 
296       choice[0] = 0;
297       choice[1] = 0;
298       for (int i = activeRwys-1;  i; i--)
299         {
300           if (rwyList[i].getType() == string("landing"))
301             choice[0] = i;
302           if (rwyList[i].getType() == string("takeoff"))
303             choice[1] = i;
304         }
305       //cerr << "Choosing " << choice[0] << " for landing and " << choice[1] << "for takeoff" << endl;
306       nrOfPreferences = rwyList[0].getRwyList()->size();
307       for (int i = 0; i < nrOfPreferences; i++)
308         {
309           bool validSelection = true;
310           for (int j = 0; j < 2; j++)
311             {
312               //cerr << "I J " << i << " " << j << endl;
313               name = rwyList[choice[j]].getRwyList(i);
314               //cerr << "Name of Runway: " << name << endl;
315               if (globals->get_runways()->search( aptId, 
316                                                   name, 
317                                                   &rwy))
318                 {
319                   //cerr << "Succes" << endl;
320                   hdgDiff = fabs(windHeading - rwy._heading);
321                   if (hdgDiff > 180)
322                     hdgDiff = 360 - hdgDiff;
323                   hdgDiff *= ((2*M_PI)/360.0); // convert to radians
324                   crossWind = windSpeed * sin(hdgDiff);
325                   tailWind  = -windSpeed * cos(hdgDiff);
326                   if ((tailWind > maxTail) || (crossWind > maxCross))
327                     validSelection = false;
328                 }else {
329                   SG_LOG( SG_GENERAL, SG_INFO, "Failed to find runway " << name << " at " << aptId );
330                   exit(1);
331                 }
332
333             }
334           if (validSelection)
335             {
336               //cerr << "Valid runay selection : " << i << endl;
337               active = i;
338               nrActive = 2;
339               return;
340             }
341         }
342     }
343   active = -1;
344   nrActive = 0;
345 }
346
347 void RunwayGroup::getActive(int i, string &name, string &type)
348 {
349   if (i == -1)
350     {
351       return;
352     }
353   if (nrActive == (int)rwyList.size())
354     {
355       name = rwyList[i].getRwyList(active);
356       type = rwyList[i].getType();
357     }
358   else
359     { 
360       name = rwyList[choice[i]].getRwyList(active);
361       type = rwyList[choice[i]].getType();
362     }
363 }
364 /*****************************************************************************
365  * FGRunway preference
366  ****************************************************************************/
367 FGRunwayPreference::FGRunwayPreference()
368 {
369   //cerr << "Running default Constructor" << endl;
370   initialized = false;
371 }
372
373 FGRunwayPreference::FGRunwayPreference(const FGRunwayPreference &other)
374 {
375   initialized = other.initialized;
376   value = other.value;
377   scheduleName = other.scheduleName;
378
379   comTimes = other.comTimes; // Commercial Traffic;
380   genTimes = other.genTimes; // General Aviation;
381   milTimes = other.milTimes; // Military Traffic;
382   currTimes= other.currTimes; // Needed for parsing;
383
384   rwyList = other.rwyList;
385   rwyGroup = other.rwyGroup;
386   PreferenceListConstIterator i;
387   for (i = other.preferences.begin(); i != other.preferences.end(); i++)
388     preferences.push_back(*i);
389 }
390   
391 FGRunwayPreference & FGRunwayPreference::operator= (const FGRunwayPreference &other)
392 {
393   initialized = other.initialized;
394   value = other.value;
395   scheduleName = other.scheduleName;
396   
397   comTimes = other.comTimes; // Commercial Traffic;
398   genTimes = other.genTimes; // General Aviation;
399   milTimes = other.milTimes; // Military Traffic;
400   currTimes= other.currTimes; // Needed for parsing;
401   
402   rwyList = other.rwyList;
403   rwyGroup = other.rwyGroup;
404   PreferenceListConstIterator i;
405   preferences.clear();
406   for (i = other.preferences.begin(); i != other.preferences.end(); i++)
407     preferences.push_back(*i);
408   return *this;
409 }
410
411 ScheduleTime *FGRunwayPreference::getSchedule(const char *trafficType)
412 {
413   if (!(strcmp(trafficType, "com"))) {
414     return &comTimes;
415   }
416   if (!(strcmp(trafficType, "gen"))) {
417     return &genTimes;
418   }
419   if (!(strcmp(trafficType, "mil"))) {
420     return &milTimes;
421   }
422   return 0;
423 }
424
425 RunwayGroup *FGRunwayPreference::getGroup(const string &groupName)
426 {
427   PreferenceListIterator i = preferences.begin();
428   if (preferences.begin() == preferences.end())
429     return 0;
430   while (!(i == preferences.end() || i->getName() == groupName))
431     i++;
432   if (i != preferences.end())
433     return &(*i);
434   else
435     return 0;
436 }
437
438 void  FGRunwayPreference::startXML () {
439   //  cout << "Start XML" << endl;
440 }
441
442 void  FGRunwayPreference::endXML () {
443   cout << "End XML" << endl;
444 }
445
446 void  FGRunwayPreference::startElement (const char * name, const XMLAttributes &atts) {
447   //cout << "StartElement " << name << endl;
448   value = string("");
449   if (!(strcmp(name, "wind"))) {
450     //cerr << "Will be processing Wind" << endl;
451     for (int i = 0; i < atts.size(); i++)
452       {
453         //cout << "  " << atts.getName(i) << '=' << atts.getValue(i) << endl; 
454         //attname = atts.getName(i);
455         if (atts.getName(i) == string("tail")) {
456           //cerr << "Tail Wind = " << atts.getValue(i) << endl;
457           currTimes.setTailWind(atof(atts.getValue(i)));
458         }       
459         if (atts.getName(i) == string("cross")) {
460           //cerr << "Cross Wind = " << atts.getValue(i) << endl;
461           currTimes.setCrossWind(atof(atts.getValue(i)));
462         }
463      }
464   }
465     if (!(strcmp(name, "time"))) {
466       //cerr << "Will be processing time" << endl;      
467     for (int i = 0; i < atts.size(); i++)
468       {
469         if (atts.getName(i) == string("start")) {
470           //cerr << "Start Time = " << atts.getValue(i) << endl;
471           currTimes.addStartTime(processTime(atts.getValue(i)));
472         }
473         if (atts.getName(i) == string("end")) {
474           //cerr << "End time = " << atts.getValue(i) << endl;
475           currTimes.addEndTime(processTime(atts.getValue(i)));
476         }
477         if (atts.getName(i) == string("schedule")) {
478           //cerr << "Schedule Name  = " << atts.getValue(i) << endl;
479           currTimes.addScheduleName(atts.getValue(i));
480         }       
481     }
482   }
483   if (!(strcmp(name, "takeoff"))) {
484     rwyList.clear();
485   }
486   if  (!(strcmp(name, "landing")))
487     {
488       rwyList.clear();
489     }
490   if (!(strcmp(name, "schedule"))) {
491     for (int i = 0; i < atts.size(); i++)
492       {
493         //cout << "  " << atts.getName(i) << '=' << atts.getValue(i) << endl; 
494         //attname = atts.getName(i);
495         if (atts.getName(i) == string("name")) {
496           //cerr << "Schedule name = " << atts.getValue(i) << endl;
497           scheduleName = atts.getValue(i);
498         }
499       }
500   }
501 }
502
503 //based on a string containing hour and minute, return nr seconds since day start.
504 time_t FGRunwayPreference::processTime(const string &tme)
505 {
506   string hour   = tme.substr(0, tme.find(":",0));
507   string minute = tme.substr(tme.find(":",0)+1, tme.length());
508
509   //cerr << "hour = " << hour << " Minute = " << minute << endl;
510   return (atoi(hour.c_str()) * 3600 + atoi(minute.c_str()) * 60);
511 }
512
513 void  FGRunwayPreference::endElement (const char * name) {
514   //cout << "End element " << name << endl;
515   if (!(strcmp(name, "rwyuse"))) {
516     initialized = true;
517   }
518   if (!(strcmp(name, "com"))) { // Commercial Traffic
519     //cerr << "Setting time table for commerical traffic" << endl;
520     comTimes = currTimes;
521     currTimes.clear();
522   }
523   if (!(strcmp(name, "gen"))) { // General Aviation
524     //cerr << "Setting time table for general aviation" << endl;
525     genTimes = currTimes;
526     currTimes.clear();
527   }  
528   if (!(strcmp(name, "mil"))) { // Military Traffic
529     //cerr << "Setting time table for military traffic" << endl;
530     genTimes = currTimes;
531     currTimes.clear();
532   }
533
534   if (!(strcmp(name, "takeoff"))) {
535     //cerr << "Adding takeoff: " << value << endl;
536     rwyList.set(name, value);
537     rwyGroup.add(rwyList);
538   }
539   if (!(strcmp(name, "landing"))) {
540     //cerr << "Adding landing: " << value << endl;
541     rwyList.set(name, value);
542     rwyGroup.add(rwyList);
543   }
544   if (!(strcmp(name, "schedule"))) {
545     //cerr << "Adding schedule" << scheduleName << endl;
546     rwyGroup.setName(scheduleName);
547     //rwyGroup.addRunways(rwyList);
548     preferences.push_back(rwyGroup);
549     rwyGroup.clear();
550     //exit(1);
551   }
552 }
553
554 void  FGRunwayPreference::data (const char * s, int len) {
555   string token = string(s,len);
556   //cout << "Character data " << string(s,len) << endl;
557   //if ((token.find(" ") == string::npos && (token.find('\n')) == string::npos))
558   //  value += token;
559   //else
560   //  value = string("");
561   value += token;
562 }
563
564 void  FGRunwayPreference::pi (const char * target, const char * data) {
565   //cout << "Processing instruction " << target << ' ' << data << endl;
566 }
567
568 void  FGRunwayPreference::warning (const char * message, int line, int column) {
569   //cout << "Warning: " << message << " (" << line << ',' << column << ')'   
570   //     << endl;
571 }
572
573 void  FGRunwayPreference::error (const char * message, int line, int column) {
574   //cout << "Error: " << message << " (" << line << ',' << column << ')'
575   //     << endl;
576 }