FlightGear next
runwayprefs.cxx
Go to the documentation of this file.
1/*
2 * SPDX-FileName: runwayprefs.cxx
3 * SPDX-FileComment: class implementations corresponding to runwayprefs.hxx assignments by the AI code
4 * SPDX-FileCopyrightText: Copyright (C) 2004 Durk Talsma.
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8#include <config.h>
9
10#include <algorithm>
11#include <cmath>
12#include <cstdlib>
13#include <cstring>
14
15#include <simgear/compiler.h>
16#include <simgear/debug/logstream.hxx>
17#include <simgear/misc/strutils.hxx>
18#include <simgear/structure/exception.hxx>
19
20#include <Airports/runways.hxx>
21#include <Main/globals.hxx>
22
23#include "airport.hxx"
24#include "runwayprefs.hxx"
25
26
27using namespace std::string_literals;
28using namespace simgear;
29
30
31/******************************************************************************
32 * ScheduleTime
33 ***************e*************************************************************/
35{
36 start.clear();
37 end.clear();
38 scheduleNames.clear();
39}
40
41
43{
45 for (i = other.start.begin(); i != other.start.end(); ++i)
46 start.push_back(*i);
47 for (i = other.end.begin(); i != other.end.end(); ++i)
48 end.push_back(*i);
50 for (k = other.scheduleNames.begin(); k != other.scheduleNames.end(); ++k)
51 scheduleNames.push_back(*k);
52
53 tailWind = other.tailWind;
54 crssWind = other.tailWind;
55}
56
57
59{
60 clear();
62 for (i = other.start.begin(); i != other.start.end(); ++i)
63 start.push_back(*i);
64 for (i = other.end.begin(); i != other.end.end(); ++i)
65 end.push_back(*i);
67 for (k = other.scheduleNames.begin(); k != other.scheduleNames.end(); ++k)
68 scheduleNames.push_back(*k);
69
70 tailWind = other.tailWind;
71 crssWind = other.tailWind;
72
73 return *this;
74}
75
76std::string ScheduleTime::getName(time_t dayStart)
77{
78 if ((start.size() != end.size()) || (start.size() != scheduleNames.size())) {
79 SG_LOG(SG_GENERAL, SG_INFO, "Unable to parse schedule times");
80 throw sg_exception("Unable to parse schedule times");
81 } else {
82 int nrItems = start.size();
83 //cerr << "Nr of items to process: " << nrItems << endl;
84 if (nrItems > 0) {
85 for (unsigned int i = 0; i < start.size(); i++) {
86 //cerr << i << endl;
87 if ((dayStart >= start[i]) && (dayStart <= end[i]))
88 return scheduleNames[i];
89 }
90 }
91 //couldn't find one so return 0;
92 //cerr << "Returning 0 " << endl;
93 }
94
95 return ""s;
96}
97
98/******************************************************************************
99 * RunwayList
100 *****************************************************************************/
101
102RunwayList::RunwayList(const RunwayList& other) : type{other.type}
103{
105 for (i = other.preferredRunways.begin();
106 i != other.preferredRunways.end(); ++i)
107 preferredRunways.push_back(*i);
108}
109
111{
112 type = other.type;
113 preferredRunways.clear();
115 for (i = other.preferredRunways.begin();
116 i != other.preferredRunways.end(); ++i)
117 preferredRunways.push_back(*i);
118 return *this;
119}
120
121void RunwayList::set(const std::string& tp, const std::string& lst)
122{
123 //weekday = atoi(timeCopy.substr(0,1).c_str());
124 // timeOffsetInDays = weekday - currTimeDate->getGmt()->tm_wday;
125 // timeCopy = timeCopy.substr(2,timeCopy.length());
126 type = tp;
127 for (const auto& s : strutils::split(lst, ",")) {
128 auto ident = strutils::strip(s);
129 // http://code.google.com/p/flightgear-bugs/issues/detail?id=1137
130 if ((ident.size() < 2) || !isdigit(ident[1])) {
131 ident = "0" + ident;
132 }
133
134 preferredRunways.push_back(ident);
135 }
136}
137
139{
140 type = "";
141 preferredRunways.clear();
142}
143
144/****************************************************************************
145 *
146 ***************************************************************************/
147
148RunwayGroup::RunwayGroup(const RunwayGroup& other) : name{other.name}
149{
151 for (i = other.rwyList.begin(); i != other.rwyList.end(); ++i)
152 rwyList.push_back(*i);
153 choice[0] = other.choice[0];
154 choice[1] = other.choice[1];
155 nrActive = other.nrActive;
156}
157
159{
160 rwyList.clear();
161 name = other.name;
163 for (i = other.rwyList.begin(); i != other.rwyList.end(); ++i)
164 rwyList.push_back(*i);
165 active = other.active;
166 choice[0] = other.choice[0];
167 choice[1] = other.choice[1];
168 nrActive = other.nrActive;
169
170 return *this;
171}
172
174 double windSpeed,
175 double windHeading,
176 double maxTail,
177 double maxCross, stringVec* currentlyActive)
178{
179 FGRunway* rwy;
180 int activeRwys = rwyList.size(); // get the number of runways active
181 int nrOfPreferences;
182 // bool found = true;
183 // double heading;
184 double hdgDiff;
185 double crossWind;
186 double tailWind;
187 //stringVec names;
188 int bestMatch = 0, bestChoice = 0;
189
190 if (activeRwys == 0) {
191 active = -1;
192 nrActive = 0;
193 return;
194 }
195
196 // Now downward iterate across all the possible preferences
197 // starting by the least preferred choice working toward the most preferred choice
198
199 nrOfPreferences = rwyList[0].getPreferredRunways().size();
200 bool foundValidSelection = false;
201 for (int i = nrOfPreferences - 1; i >= 0; i--) {
202 int match = 0;
203
204 // Test each runway listed in the preference to see if it's possible to use
205 // If one runway of the selection isn't allowed, we need to exclude this
206 // preference, however, we don't want to stop right there, because we also
207 // don't want to randomly swap runway preferences, unless there is a need to.
208 //
209 bool validSelection = true;
210
211 for (int j = 0; j < activeRwys; j++) {
212 const auto& currentRunwayPrefs = rwyList.at(j).getPreferredRunways();
213
214 // if this rwyList has shorter preferences vec than the first one,
215 // don't crash accessing an invalid index.
216 // see https://sourceforge.net/p/flightgear/codetickets/2439/
217 if ((int)currentRunwayPrefs.size() <= i) {
218 validSelection = false;
219 continue;
220 }
221
222 const auto& ident(currentRunwayPrefs.at(i));
223 if (!airport->hasRunwayWithIdent(ident)) {
224 SG_LOG(SG_GENERAL, SG_WARN,
225 "no such runway:" << ident << " at " << airport->ident());
226 validSelection = false;
227 continue;
228 }
229
230 rwy = airport->getRunwayByIdent(ident);
231
232 //cerr << "Succes" << endl;
233 hdgDiff = fabs(windHeading - rwy->headingDeg());
234 std::string l_name = rwy->name();
235
236 if (hdgDiff > 180)
237 hdgDiff = 360 - hdgDiff;
238 //cerr << "Heading diff: " << hdgDiff << endl;
239 hdgDiff *= ((2 * M_PI) / 360.0); // convert to radians
240 crossWind = windSpeed * sin(hdgDiff);
241 tailWind = -windSpeed * cos(hdgDiff);
242 //cerr << "Runway : " << rwy->name() << ": " << rwy->headingDeg() << endl;
243 //cerr << ". Tailwind : " << tailWind;
244 //cerr << ". Crosswnd : " << crossWind;
245 if ((tailWind > maxTail) || (crossWind > maxCross)) {
246 //cerr << ". [Invalid] " << endl;
247 validSelection = false;
248 } else {
249 //cerr << ". [Valid] ";
250 }
251 //cerr << endl;
252 for (stringVecIterator it = currentlyActive->begin();
253 it != currentlyActive->end(); ++it) {
254 //cerr << "Checking : \"" << (*it) << "\". vs \"" << l_name << "\"" << endl;
255 if ((*it) == l_name) {
256 match++;
257 }
258 }
259 } // of active runways iteration
260
261 if (validSelection) {
262 //cerr << "Valid selection : " << i << endl;;
263 foundValidSelection = true;
264 if (match >= bestMatch) {
265 bestMatch = match;
266 bestChoice = i;
267 }
268 }
269 //cerr << "Preference " << i << "Match " << match << " bestMatch " << bestMatch << " choice " << bestChoice << " valid selection " << validSelection << endl;
270 }
271
272 if (foundValidSelection) {
273 //cerr << "Valid runway selection : " << bestChoice << endl;
274 nrActive = activeRwys;
275 active = bestChoice;
276 return;
277 }
278
279 // If this didn't work, due to heavy winds, try again
280 // but select only one landing and one takeoff runway.
281 choice[0] = 0;
282 choice[1] = 0;
283 for (int i = activeRwys - 1; i; i--) {
284 if (rwyList[i].getType() == "landing"s)
285 choice[0] = i;
286 if (rwyList[i].getType() == "takeoff"s)
287 choice[1] = i;
288 }
289 //cerr << "Choosing " << choice[0] << " for landing and " << choice[1] << "for takeoff" << endl;
290 nrOfPreferences = rwyList[0].getPreferredRunways().size();
291 for (int i = 0; i < nrOfPreferences; i++) {
292 bool validSelection = true;
293 for (int j = 0; j < 2; j++) {
294 const auto& currentRunwayPrefs = rwyList.at(choice[j]).getPreferredRunways();
295 if (i >= (int)currentRunwayPrefs.size()) {
296 validSelection = false;
297 continue;
298 }
299
300 const auto& l_name = currentRunwayPrefs.at(i);
301 if (!airport->hasRunwayWithIdent(l_name)) {
302 validSelection = false;
303 continue;
304 }
305
306 rwy = airport->getRunwayByIdent(l_name);
307 //cerr << "Success" << endl;
308 hdgDiff = fabs(windHeading - rwy->headingDeg());
309 if (hdgDiff > 180)
310 hdgDiff = 360 - hdgDiff;
311 hdgDiff *= ((2 * M_PI) / 360.0); // convert to radians
312 crossWind = windSpeed * sin(hdgDiff);
313 tailWind = -windSpeed * cos(hdgDiff);
314 if ((tailWind > maxTail) || (crossWind > maxCross)) {
315 validSelection = false;
316 }
317 }
318
319 if (validSelection) {
320 //cerr << "Valid runway selection : " << i << endl;
321 active = i;
322 nrActive = 2;
323 return;
324 }
325 }
326
327 // failed to find any valid runways
328 active = -1;
329 nrActive = 0;
330}
331
332void RunwayGroup::getActive(int i, std::string& name, std::string& type)
333{
334 if (i == -1) {
335 return;
336 }
337
338 if (i >= nrActive) {
339 SG_LOG(SG_AI, SG_DEV_ALERT, "RunwayGroup::getActive: invalid index " << i);
340 return;
341 }
342
343 // nrActive is either the full size of the list *or* two, if we
344 // fell back due to heavy winds
345 const bool usingFullList = nrActive == (int)rwyList.size();
346 if (usingFullList) {
347 const auto& runways = rwyList.at(i).getPreferredRunways();
348 name = runways.at(active);
349 type = rwyList[i].getType();
350 } else {
351 const auto& runways = rwyList.at(choice[i]).getPreferredRunways();
352 name = runways.at(active);
353 type = rwyList[choice[i]].getType();
354 }
355}
356
357/*****************************************************************************
358 * FGRunway preference
359 ****************************************************************************/
361{
362 //cerr << "Running default Constructor" << endl;
363 initialized = false;
364}
365
367 comTimes{other.comTimes},
368 genTimes{other.genTimes},
369 milTimes{other.milTimes}
370{
371 initialized = other.initialized;
372
374 for (i = other.preferences.begin(); i != other.preferences.end(); ++i)
375 preferences.push_back(*i);
376}
377
379{
380 _ap = other._ap;
381
382 initialized = other.initialized;
383
384 comTimes = other.comTimes; // Commercial Traffic;
385 genTimes = other.genTimes; // General Aviation;
386 milTimes = other.milTimes; // Military Traffic;
387
389 preferences.clear();
390 for (i = other.preferences.begin(); i != other.preferences.end(); ++i)
391 preferences.push_back(*i);
392
393 return *this;
394}
395
397{
398 if (!(strcmp(trafficType, "com"))) {
399 return &comTimes;
400 }
401 if (!(strcmp(trafficType, "gen"))) {
402 return &genTimes;
403 }
404 if (!(strcmp(trafficType, "mil"))) {
405 return &milTimes;
406 }
407 return 0;
408}
409
410RunwayGroup* FGRunwayPreference::getGroup(const std::string& groupName)
411{
412 auto it = std::find_if(preferences.begin(), preferences.end(),
413 [&groupName](const RunwayGroup& g) {
414 return g.getName() == groupName;
415 });
416
417 if (it == preferences.end())
418 return nullptr;
419
420 return &(*it); // not thrilled about this, but ok
421}
422
424{
425 return _ap->getId();
426};
#define M_PI
Definition FGJSBBase.h:50
#define i(x)
std::vector< RunwayList >::const_iterator RunwayListVecConstIterator
std::vector< RunwayGroup >::const_iterator PreferenceListConstIterator
FGRunwayRef getRunwayByIdent(const std::string &aIdent) const
Definition airport.cxx:182
bool hasRunwayWithIdent(const std::string &aIdent) const
Definition airport.cxx:162
virtual const std::string & name() const
Return the name of this positioned.
const std::string & ident() const
double headingDeg() const
Runway heading in degrees.
FGRunwayPreference(FGAirport *ap)
std::string getId()
RunwayGroup * getGroup(const std::string &groupName)
ScheduleTime * getSchedule(const char *trafficType)
FGRunwayPreference & operator=(const FGRunwayPreference &other)
RunwayGroup & operator=(const RunwayGroup &other)
void getActive(int i, std::string &name, std::string &type)
void setActive(const FGAirport *airport, double windSpeed, double windHeading, double maxTail, double maxCross, stringVec *curr)
RunwayList & operator=(const RunwayList &other)
void set(const std::string &, const std::string &)
std::string getName(time_t dayStart)
ScheduleTime & operator=(const ScheduleTime &other)
std::vector< std::string >::iterator stringVecIterator
std::vector< std::string >::const_iterator stringVecConstIterator
std::vector< std::string > stringVec
std::vector< time_t >::const_iterator timeVecConstIterator