FlightGear next
procedure.cxx
Go to the documentation of this file.
1// procedure.cxx - define route storing an approach, arrival or departure procedure
2// Written by James Turner, started 2009.
3//
4// Copyright (C) 2009 Curtis L. Olson
5//
6// This program is free software; you can redistribute it and/or
7// modify it under the terms of the GNU General Public License as
8// published by the Free Software Foundation; either version 2 of the
9// License, or (at your option) any later version.
10//
11// This program is distributed in the hope that it will be useful, but
12// WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14// General Public License for more details.
15//
16// You should have received a copy of the GNU General Public License
17// along with this program; if not, write to the Free Software
18// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
20#include "procedure.hxx"
21
22#include <cassert>
23#include <algorithm> // for reverse_copy
24
25#include <simgear/structure/exception.hxx>
26
27#include <Airports/airport.hxx>
28#include <Airports/runways.hxx>
29#include <Navaids/waypoint.hxx>
30
31#include <iterator> // required for WIN
32using std::string;
33
34namespace flightgear
35{
36
37static void markWaypoints(WayptVec& wps, WayptFlag f)
38{
39 for (unsigned int i=0; i<wps.size(); ++i) {
40 wps[i]->setFlag(f, true);
41 }
42}
43
44Procedure::Procedure(const string& aIdent) :
45 _ident(aIdent)
46{
47}
48
49Approach::Approach(const string& aIdent, ProcedureType ty) :
50 Procedure(aIdent),
51 _type(ty)
52{
53
54}
55
56Approach::~Approach() = default;
57
58Approach* Approach::createTempApproach(const std::string& aIdent, FGRunway* aRunway, const WayptVec& aPath)
59{
60 Approach* app = new Approach(aIdent, PROCEDURE_APPROACH_RNAV);
61 app->setRunway(aRunway);
62 app->setPrimaryAndMissed(aPath, WayptVec());
63 return app;
64}
65
66void Approach::setRunway(FGRunwayRef aRwy)
67{
68 _runway = aRwy;
69}
70
72{
73 return _runway->airport();
74}
75
77{
78 RunwayVec r;
79 r.push_back(_runway);
80 return r;
81}
82
83void Approach::setPrimaryAndMissed(const WayptVec& aPrimary, const WayptVec& aMissed)
84{
85 _primary = aPrimary;
86 _primary[0]->setFlag(WPT_IAF, true);
87 _primary[_primary.size()-1]->setFlag(WPT_FAF, true);
88 markWaypoints(_primary, WPT_APPROACH);
89
90 _missed = aMissed;
91
92 if (!_missed.empty()) {
93 // mark the first point as the published missed-approach point
94 _missed[0]->setFlag(WPT_MAP, true);
95 markWaypoints(_missed, WPT_MISS);
97 }
98}
99
100void Approach::addTransition(Transition* aTrans)
101{
102 WayptRef entry = aTrans->enroute();
103 _transitions[entry] = aTrans;
104 aTrans->mark(WPT_APPROACH);
105}
106
108{
109 if (!trans)
110 return false;
111
112 trans->route(aWps);
113 bool ok = routeFromVectors(aWps);
114 return ok;
115}
116
118{
119 if (aIAF.valid()) {
120 bool haveTrans = false;
121 for (auto te : _transitions) {
122 auto t = te.second;
123 if (t->enroute()->matches(aIAF)) {
124 t->route(aWps);
125 haveTrans = true;
126 break;
127 }
128 } // of transitions iteration
129
130 if (!haveTrans) {
131 if (_primary.front()->matches(aIAF)) {
132 // direct IAF on the approach, no transition is needed
133 } else {
134 // we couldn't find the IAF at the front of any obvious thing - either
135 // the primary waypoints or any transition we have defined.
136 // warn and just use the primary waypoints down below
137 SG_LOG(SG_NAVAID, SG_INFO, "approach " << ident() << " has no transition " <<
138 "for IAF: " << aIAF->ident());
139 }
140 }
141 }
142
143 bool ok = routeFromVectors(aWps);
144
145 if (ok && !aWps.empty() && aIAF.valid() && aWps.front()->matches(aIAF)) {
146 // don't duplicate the IAF into the route we return. This avoids a
147 // duplicated waypt between the end of a STAR and the approach
148 aWps.erase(aWps.begin());
149 }
150
151 return ok;
152}
153
155{
156 aWps.insert(aWps.end(), _primary.begin(), _primary.end());
157 RunwayWaypt* rwy = new RunwayWaypt(_runway, NULL);
158 rwy->setFlag(WPT_APPROACH);
159 aWps.push_back(rwy);
160 aWps.insert(aWps.end(), _missed.begin(), _missed.end());
161 return true;
162}
163
168
170{
171 string_list r;
172 r.reserve(_transitions.size());
173 for (const auto& t : _transitions) {
174 r.push_back(t.second->ident());
175 }
176 return r;
177}
178
179Transition* Approach::findTransitionByName(const string& aIdent) const
180{
181 auto it = std::find_if(_transitions.begin(), _transitions.end(), [aIdent](const WptTransitionMap::value_type& t) {
182 return aIdent == t.second->ident();
183 });
184
185 if (it == _transitions.end())
186 return nullptr;
187
188 return it->second;
189}
190
192
194 Procedure(aIdent),
195 _airport(apt)
196{
197}
198
200{
201 assert(aWay->airport() == _airport);
202 _runways[aWay] = NULL;
203}
204
206{
207 // null runway always passes
208 if (!aWay) {
209 return true;
210 }
211
212 FGRunwayRef r(const_cast<FGRunway*>(aWay));
213 return (_runways.count(r) > 0);
214}
215
217{
218 RunwayVec r;
219 RunwayTransitionMap::const_iterator it = _runways.begin();
220 for (; it != _runways.end(); ++it) {
221 r.push_back(it->first);
222 }
223
224 return r;
225}
226
227void ArrivalDeparture::addTransition(Transition* aTrans)
228{
229 WayptRef entry = aTrans->enroute();
230 aTrans->mark(flagType());
231 _enrouteTransitions[entry] = aTrans;
232}
233
235{
236 string_list r;
237 WptTransitionMap::const_iterator eit;
238 for (eit = _enrouteTransitions.begin(); eit != _enrouteTransitions.end(); ++eit) {
239 r.push_back(eit->second->ident());
240 }
241 return r;
242}
243
244void ArrivalDeparture::addRunwayTransition(FGRunwayRef aWay, Transition* aTrans)
245{
246 assert(aWay->ident() == aTrans->ident());
247 if (!isForRunway(aWay)) {
248 throw sg_io_exception("adding transition for unspecified runway:" + aWay->ident(), ident());
249 }
250
251 aTrans->mark(flagType());
252 _runways[aWay] = aTrans;
253}
254
256{
257 _common = aWps;
258 markWaypoints(_common, flagType());
259}
260
262{
263 // assume we're routing from enroute, to the runway.
264 // for departures, we'll flip the result points
265
266 WayptVec::iterator firstCommon = _common.begin();
267 if (t) {
268 t->route(aPath);
269
270 Waypt* transEnd = t->procedureEnd();
271 for (; firstCommon != _common.end(); ++firstCommon) {
272 if ((*firstCommon)->matches(transEnd)) {
273 // found transition->common point, stop search
274 break;
275 }
276 } // of common points
277
278 // if we hit this point, the transition doesn't end (start, for a SID) on
279 // a common point. We assume this means we should just append the entire
280 // common section after the transition.
281 firstCommon = _common.begin();
282 } else {
283 // no tranasition
284 } // of not using a transition
285
286 // append (some) common points
287 aPath.insert(aPath.end(), firstCommon, _common.end());
288
289 if (!aRwy) {
290 // no runway specified, we're done
291 return true;
292 }
293
294 RunwayTransitionMap::iterator r = _runways.find(aRwy);
295 if (r == _runways.end()) {
296 // runway doesn't match STAR/SID; this may be intentional (cf. EDDF
297 // transitions), so we have to cater for it: we will just return at this
298 // point, and trust the calling code to insert VECTORS or select an
299 // appropriate approach transition.
300 return true;
301 }
302
303 if (!r->second) {
304 // no transitions specified. Not great, but not
305 // much we can do about it. Calling code will insert VECTORS to the approach
306 // if required, or maybe there's an approach transition defined.
307 return true;
308 }
309
310 SG_LOG(SG_NAVAID, SG_INFO, ident() << " using runway transition for " << r->first->ident());
311 r->second->route(aPath);
312 return true;
313}
314
316{
317 if (!aEnroute) {
318 return NULL;
319 }
320
321 WptTransitionMap::const_iterator eit;
322 for (eit = _enrouteTransitions.begin(); eit != _enrouteTransitions.end(); ++eit) {
323 if (eit->second->enroute()->matches(aEnroute)) {
324 return eit->second;
325 }
326 } // of enroute transition iteration
327
328 return NULL;
329}
330
332{
333 if (!aEnroute) {
334 return NULL;
335 }
336
337 WptTransitionMap::const_iterator eit;
338 for (eit = _enrouteTransitions.begin(); eit != _enrouteTransitions.end(); ++eit) {
339 if (eit->second->enroute()->matches(aEnroute)) {
340 return eit->second;
341 }
342 } // of enroute transition iteration
343
344 return NULL;
345}
346
348{
349 // no transitions, that's easy
350 if (_enrouteTransitions.empty()) {
351 SG_LOG(SG_NAVAID, SG_INFO, "no enroute transitions for " << ident());
352 return _common.front();
353 }
354
355 double d = 1e9;
356 WayptRef w;
357 WptTransitionMap::const_iterator eit;
358 for (eit = _enrouteTransitions.begin(); eit != _enrouteTransitions.end(); ++eit) {
359 WayptRef c = eit->second->enroute();
360 SG_LOG(SG_NAVAID, SG_INFO, "findBestTransition for " << ident() << ", looking at " << c->ident());
361 // assert(c->hasFixedPosition());
362 double cd = SGGeodesy::distanceM(aPos, c->position());
363
364 if (cd < d) { // distance to 'c' is less, new best match
365 d = cd;
366 w = c;
367 }
368 } // of transitions iteration
369
370 assert(w);
371 return w;
372}
373
375{
376 WptTransitionMap::const_iterator eit;
377 for (eit = _enrouteTransitions.begin(); eit != _enrouteTransitions.end(); ++eit) {
378 if (eit->second->ident() == aIdent) {
379 return eit->second;
380 }
381 }
382
383 return NULL;
384}
385
387
388SID::SID(const string& aIdent, FGAirport* apt) :
389 ArrivalDeparture(aIdent, apt)
390{
391}
392
393bool SID::route(FGRunwayRef aWay, Transition* trans, WayptVec& aPath)
394{
395 if (!isForRunway(aWay)) {
396 SG_LOG(SG_NAVAID, SG_WARN, "SID " << ident() << " not for runway " << aWay->ident());
397 return false;
398 }
399
400 WayptVec path;
401 if (!commonRoute(trans, path, aWay)) {
402 return false;
403 }
404
405 // SID waypoints (including transitions) are stored reversed, so we can
406 // re-use the routing code. This is where we fix the ordering for client code
407 std::back_insert_iterator<WayptVec> bi(aPath);
408 std::reverse_copy(path.begin(), path.end(), bi);
409
410 return true;
411}
412
413SID* SID::createTempSID(const std::string& aIdent, FGRunway* aRunway, const WayptVec& aPath)
414{
415// flip waypoints since SID stores them reversed
416 WayptVec path;
417 std::back_insert_iterator<WayptVec> bi(path);
418 std::reverse_copy(aPath.begin(), aPath.end(), bi);
419
420 SID* sid = new SID(aIdent, aRunway->airport());
421 sid->setCommon(path);
422 sid->addRunway(aRunway);
423 return sid;
424}
425
427
428STAR::STAR(const string& aIdent, FGAirport* apt) :
429 ArrivalDeparture(aIdent, apt)
430{
431}
432
433bool STAR::route(FGRunwayRef aWay, Transition* trans, WayptVec& aPath)
434{
435 if (aWay && !isForRunway(aWay)) {
436 return false;
437 }
438
439 return commonRoute(trans, aPath, aWay);
440}
441
443
444Transition::Transition(const std::string& aIdent, ProcedureType ty, Procedure* aPr) :
445 Procedure(aIdent),
446 _type(ty),
447 _parent(aPr)
448{
449 assert(aPr);
450}
451
452void Transition::setPrimary(const WayptVec& aWps)
453{
454 _primary = aWps;
455 assert(!_primary.empty());
456 _primary[0]->setFlag(WPT_TRANSITION, true);
457}
458
460{
461 assert(!_primary.empty());
462 return _primary[0];
463}
464
466{
467 assert(!_primary.empty());
468 return _primary[_primary.size() - 1];
469}
470
472{
473 aPath.insert(aPath.end(), _primary.begin(), _primary.end());
474 return true;
475}
476
478{
479 return _parent->airport();
480}
481
483{
484 markWaypoints(_primary, f);
485}
486
487} // of namespace
#define i(x)
SGSharedPtr< FGRunway > FGRunwayRef
FGAirportRef airport() const
Transition * findTransitionByName(const std::string &aIdent) const
FGRunwayRef runway()
virtual FGAirport * airport() const
Definition procedure.cxx:71
static Approach * createTempApproach(const std::string &aIdent, FGRunway *aRunway, const WayptVec &aPath)
Definition procedure.cxx:58
static bool isApproach(ProcedureType ty)
bool routeFromVectors(WayptVec &aWps)
Build route as above, but ignore transitions, and assume radar vectoring to the start of main approac...
string_list transitionIdents() const
virtual RunwayVec runways() const
Definition procedure.cxx:76
bool routeWithTransition(FGRunwayRef runway, Transition *trans, WayptVec &aWps)
bool route(FGRunwayRef runway, WayptRef aIAF, WayptVec &aWps)
Build a route from a valid IAF to the runway, including the missed segment.
virtual bool isForRunway(const FGRunway *aWay) const
Predicate, test if this procedure applies to the requested runway.
virtual WayptFlag flagType() const =0
ArrivalDeparture(const std::string &aIdent, FGAirport *apt)
RunwayTransitionMap _runways
void addRunway(FGRunwayRef aRwy)
string_list transitionIdents() const
bool commonRoute(Transition *t, WayptVec &aPath, FGRunwayRef aRwy)
void setCommon(const WayptVec &aWps)
Transition * findTransitionByEnroute(FGPositioned *aEnroute) const
WayptRef findBestTransition(const SGGeod &aPos) const
Given an enroute location, find the best enroute transition point for this arrival/departure.
virtual RunwayVec runways() const
Transition * findTransitionByName(const std::string &aIdent) const
Find an enroute transition waypoint by identifier.
virtual std::string ident() const
Definition procedure.hxx:53
Procedure(const std::string &aIdent)
Definition procedure.cxx:44
Waypoint based upon a runway.
Definition waypoint.hxx:117
virtual bool route(FGRunwayRef aWay, Transition *aTrans, WayptVec &aPath)
Find a path between the runway and enroute structure.
static SID * createTempSID(const std::string &aIdent, FGRunway *aRunway, const WayptVec &aPath)
virtual bool route(FGRunwayRef aWay, Transition *aTrans, WayptVec &aPath)
Find a path between the runway and enroute structure.
Encapsulate a transition segment.
Definition procedure.hxx:70
bool route(WayptVec &aPath)
void mark(WayptFlag f)
WayptRef procedureEnd() const
Return the procedure end of the transition.
virtual FGAirport * airport() const
WayptRef enroute() const
Return the enroute end of the transition.
Abstract base class for waypoints (and things that are treated similarly by navigation systems).
Definition route.hxx:105
void setFlag(WayptFlag aFlag, bool aV=true)
Definition route.cxx:209
std::vector< std::string > string_list
Definition globals.hxx:36
FlightPlan.hxx - defines a full flight-plan object, including departure, cruise, arrival information ...
Definition Addon.cxx:53
SGSharedPtr< Waypt > WayptRef
@ WPT_MAP
missed approach point
Definition route.hxx:41
@ WPT_MISS
segment is part of missed approach
Definition route.hxx:46
@ WPT_APPROACH
Definition route.hxx:60
@ WPT_TRANSITION
transition to/from enroute structure
Definition route.hxx:45
@ WPT_FAF
final approach fix
Definition route.hxx:43
@ WPT_IAF
initial approach fix
Definition route.hxx:42
std::vector< WayptRef > WayptVec
@ PROCEDURE_APPROACH_RNAV
Definition procedure.hxx:41
@ PROCEDURE_APPROACH_ILS
Definition procedure.hxx:38
std::vector< FGRunwayRef > RunwayVec
Definition procedure.hxx:34
static void markWaypoints(WayptVec &wps, WayptFlag f)
Definition procedure.cxx:37