1 // procedure.cxx - define route storing an approach, arrival or departure procedure
2 // Written by James Turner, started 2009.
4 // Copyright (C) 2009 Curtis L. Olson
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.
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.
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.
20 #include "procedure.hxx"
23 #include <algorithm> // for reverse_copy
25 #include <simgear/structure/exception.hxx>
27 #include <Airports/runways.hxx>
28 #include <Navaids/waypoint.hxx>
35 static void markWaypoints(WayptVec& wps, WayptFlag f)
37 for (unsigned int i=0; i<wps.size(); ++i) {
38 wps[i]->setFlag(f, true);
42 Procedure::Procedure(const string& aIdent) :
47 Approach::Approach(const string& aIdent, ProcedureType ty) :
54 Approach* Approach::createTempApproach(const std::string& aIdent, FGRunway* aRunway, const WayptVec& aPath)
56 Approach* app = new Approach(aIdent, PROCEDURE_APPROACH_RNAV);
57 app->setRunway(aRunway);
58 app->setPrimaryAndMissed(aPath, WayptVec());
62 void Approach::setRunway(FGRunwayRef aRwy)
67 FGAirport* Approach::airport() const
69 return _runway->airport();
72 RunwayVec Approach::runways() const
79 void Approach::setPrimaryAndMissed(const WayptVec& aPrimary, const WayptVec& aMissed)
82 _primary[0]->setFlag(WPT_IAF, true);
83 _primary[_primary.size()-1]->setFlag(WPT_FAF, true);
84 markWaypoints(_primary, WPT_APPROACH);
88 if (!_missed.empty()) {
89 // mark the first point as the published missed-approach point
90 _missed[0]->setFlag(WPT_MAP, true);
91 markWaypoints(_missed, WPT_MISS);
92 markWaypoints(_missed, WPT_APPROACH);
96 void Approach::addTransition(Transition* aTrans)
98 WayptRef entry = aTrans->enroute();
99 _transitions[entry] = aTrans;
100 aTrans->mark(WPT_APPROACH);
103 bool Approach::route(WayptRef aIAF, WayptVec& aWps)
106 WptTransitionMap::iterator it;
107 bool haveTrans = false;
108 for (it = _transitions.begin(); it != _transitions.end(); ++it) {
109 Transition* t= it->second;
110 if (t->enroute()->matches(aIAF)) {
115 } // of transitions iteration
118 SG_LOG(SG_NAVAID, SG_INFO, "approach " << ident() << " has no transition " <<
119 "for IAF: " << aIAF->ident());
124 return routeFromVectors(aWps);
127 bool Approach::routeFromVectors(WayptVec& aWps)
129 aWps.insert(aWps.end(), _primary.begin(), _primary.end());
130 RunwayWaypt* rwy = new RunwayWaypt(_runway, NULL);
131 rwy->setFlag(WPT_APPROACH);
133 aWps.insert(aWps.end(), _missed.begin(), _missed.end());
137 bool Approach::isApproach(ProcedureType ty)
139 return (ty >= PROCEDURE_APPROACH_ILS) && (ty <= PROCEDURE_APPROACH_RNAV);
142 //////////////////////////////////////////////////////////////////////////////
144 ArrivalDeparture::ArrivalDeparture(const string& aIdent, FGAirport* apt) :
150 void ArrivalDeparture::addRunway(FGRunwayRef aWay)
152 assert(aWay->airport() == _airport);
153 _runways[aWay] = NULL;
156 bool ArrivalDeparture::isForRunway(const FGRunway* aWay) const
158 // null runway always passes
163 FGRunwayRef r(const_cast<FGRunway*>(aWay));
164 return (_runways.count(r) > 0);
167 RunwayVec ArrivalDeparture::runways() const
170 RunwayTransitionMap::const_iterator it = _runways.begin();
171 for (; it != _runways.end(); ++it) {
172 r.push_back(it->first);
178 void ArrivalDeparture::addTransition(Transition* aTrans)
180 WayptRef entry = aTrans->enroute();
181 aTrans->mark(flagType());
182 _enrouteTransitions[entry] = aTrans;
185 string_list ArrivalDeparture::transitionIdents() const
188 WptTransitionMap::const_iterator eit;
189 for (eit = _enrouteTransitions.begin(); eit != _enrouteTransitions.end(); ++eit) {
190 r.push_back(eit->second->ident());
195 void ArrivalDeparture::addRunwayTransition(FGRunwayRef aWay, Transition* aTrans)
197 assert(aWay->ident() == aTrans->ident());
198 if (!isForRunway(aWay)) {
199 throw sg_io_exception("adding transition for unspecified runway:" + aWay->ident(), ident());
202 aTrans->mark(flagType());
203 _runways[aWay] = aTrans;
206 void ArrivalDeparture::setCommon(const WayptVec& aWps)
209 markWaypoints(_common, flagType());
212 bool ArrivalDeparture::commonRoute(Transition* t, WayptVec& aPath, FGRunwayRef aRwy)
214 // assume we're routing from enroute, to the runway.
215 // for departures, we'll flip the result points
217 WayptVec::iterator firstCommon = _common.begin();
221 Waypt* transEnd = t->procedureEnd();
222 for (; firstCommon != _common.end(); ++firstCommon) {
223 if ((*firstCommon)->matches(transEnd)) {
224 // found transition->common point, stop search
227 } // of common points
229 // if we hit this point, the transition doesn't end (start, for a SID) on
230 // a common point. We assume this means we should just append the entire
231 // common section after the transition.
232 firstCommon = _common.begin();
235 } // of not using a transition
237 // append (some) common points
238 aPath.insert(aPath.end(), firstCommon, _common.end());
241 // no runway specified, we're done
245 RunwayTransitionMap::iterator r = _runways.find(aRwy);
246 assert(r != _runways.end());
248 // no transitions specified. Not great, but not
249 // much we can do about it. Calling code will insert VECTORS to the approach
250 // if required, or maybe there's an approach transition defined.
254 SG_LOG(SG_NAVAID, SG_INFO, ident() << " using runway transition for " << r->first->ident());
255 r->second->route(aPath);
259 Transition* ArrivalDeparture::findTransitionByEnroute(Waypt* aEnroute) const
265 WptTransitionMap::const_iterator eit;
266 for (eit = _enrouteTransitions.begin(); eit != _enrouteTransitions.end(); ++eit) {
267 if (eit->second->enroute()->matches(aEnroute)) {
268 SG_LOG(SG_NAVAID, SG_INFO, ident() << " using enroute transition " << eit->second->ident());
271 } // of enroute transition iteration
276 WayptRef ArrivalDeparture::findBestTransition(const SGGeod& aPos) const
278 // no transitions, that's easy
279 if (_enrouteTransitions.empty()) {
280 SG_LOG(SG_NAVAID, SG_INFO, "no enroute transitions for " << ident());
281 return _common.front();
286 WptTransitionMap::const_iterator eit;
287 for (eit = _enrouteTransitions.begin(); eit != _enrouteTransitions.end(); ++eit) {
288 WayptRef c = eit->second->enroute();
289 SG_LOG(SG_NAVAID, SG_INFO, "findBestTransition for " << ident() << ", looking at " << c->ident());
290 // assert(c->hasFixedPosition());
291 double cd = SGGeodesy::distanceM(aPos, c->position());
293 if (cd < d) { // distance to 'c' is less, new best match
297 } // of transitions iteration
303 Transition* ArrivalDeparture::findTransitionByName(const string& aIdent) const
305 WptTransitionMap::const_iterator eit;
306 for (eit = _enrouteTransitions.begin(); eit != _enrouteTransitions.end(); ++eit) {
307 if (eit->second->ident() == aIdent) {
315 ////////////////////////////////////////////////////////////////////////////
317 SID::SID(const string& aIdent, FGAirport* apt) :
318 ArrivalDeparture(aIdent, apt)
322 bool SID::route(FGRunwayRef aWay, Transition* trans, WayptVec& aPath)
324 if (!isForRunway(aWay)) {
325 SG_LOG(SG_NAVAID, SG_WARN, "SID " << ident() << " not for runway " << aWay->ident());
330 if (!commonRoute(trans, path, aWay)) {
334 // SID waypoints (including transitions) are stored reversed, so we can
335 // re-use the routing code. This is where we fix the ordering for client code
336 std::back_insert_iterator<WayptVec> bi(aPath);
337 std::reverse_copy(path.begin(), path.end(), bi);
342 SID* SID::createTempSID(const std::string& aIdent, FGRunway* aRunway, const WayptVec& aPath)
344 // flip waypoints since SID stores them reversed
346 std::back_insert_iterator<WayptVec> bi(path);
347 std::reverse_copy(aPath.begin(), aPath.end(), bi);
349 SID* sid = new SID(aIdent, aRunway->airport());
350 sid->setCommon(path);
351 sid->addRunway(aRunway);
355 ////////////////////////////////////////////////////////////////////////////
357 STAR::STAR(const string& aIdent, FGAirport* apt) :
358 ArrivalDeparture(aIdent, apt)
362 bool STAR::route(FGRunwayRef aWay, Transition* trans, WayptVec& aPath)
364 if (aWay && !isForRunway(aWay)) {
368 return commonRoute(trans, aPath, aWay);
371 /////////////////////////////////////////////////////////////////////////////
373 Transition::Transition(const std::string& aIdent, ProcedureType ty, Procedure* aPr) :
381 void Transition::setPrimary(const WayptVec& aWps)
384 assert(!_primary.empty());
385 _primary[0]->setFlag(WPT_TRANSITION, true);
388 WayptRef Transition::enroute() const
390 assert(!_primary.empty());
394 WayptRef Transition::procedureEnd() const
396 assert(!_primary.empty());
397 return _primary[_primary.size() - 1];
400 bool Transition::route(WayptVec& aPath)
402 aPath.insert(aPath.end(), _primary.begin(), _primary.end());
406 FGAirport* Transition::airport() const
408 return _parent->airport();
411 void Transition::mark(WayptFlag f)
413 markWaypoints(_primary, f);