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 <Navaids/waypoint.hxx>
34 Procedure::Procedure(const string& aIdent) :
39 Approach::Approach(const string& aIdent) :
45 void Approach::setRunway(FGRunwayRef aRwy)
50 void Approach::setPrimaryAndMissed(const WayptVec& aPrimary, const WayptVec& aMissed)
53 _primary[0]->setFlag(WPT_IAF, true);
54 _primary[_primary.size()-1]->setFlag(WPT_FAF, true);
58 if (!_missed.empty()) {
59 // mark the first point as the published missed-approach point
60 _missed[0]->setFlag(WPT_MAP, true);
62 // mark all the points as being on the missed approach route
63 for (unsigned int i=0; i<_missed.size(); ++i) {
64 _missed[i]->setFlag(WPT_MISS, true);
69 void Approach::addTransition(Transition* aTrans)
71 WayptRef entry = aTrans->enroute();
72 _transitions[entry] = aTrans;
75 bool Approach::route(WayptRef aIAF, WayptVec& aWps)
77 WptTransitionMap::iterator it;
78 bool haveTrans = false;
79 for (it = _transitions.begin(); it != _transitions.end(); ++it) {
80 Transition* t= it->second;
81 if (t->route(aIAF, aWps)) {
85 } // of transitions iteration
88 SG_LOG(SG_GENERAL, SG_INFO, "approach " << ident() << " has no transition " <<
89 "for IAF: " << aIAF->ident());
93 aWps.insert(aWps.end(), _primary.begin(), _primary.end());
94 aWps.push_back(new RunwayWaypt(_runway, NULL));
95 aWps.insert(aWps.end(), _missed.begin(), _missed.end());
99 bool Approach::routeFromVectors(WayptVec& aWps)
101 aWps.insert(aWps.end(), _primary.begin(), _primary.end());
102 aWps.push_back(new RunwayWaypt(_runway, NULL));
103 aWps.insert(aWps.end(), _missed.begin(), _missed.end());
107 //////////////////////////////////////////////////////////////////////////////
109 ArrivalDeparture::ArrivalDeparture(const string& aIdent) :
114 void ArrivalDeparture::addRunway(FGRunwayRef aWay)
116 _runways[aWay] = NULL;
119 bool ArrivalDeparture::isForRunway(FGRunwayRef aWay) const
121 // null runway always passes
126 return (_runways.count(aWay));
129 void ArrivalDeparture::addTransition(Transition* aTrans)
131 WayptRef entry = aTrans->enroute();
132 _enrouteTransitions[entry] = aTrans;
135 void ArrivalDeparture::addRunwayTransition(FGRunwayRef aWay, Transition* aTrans)
137 assert(aWay->ident() == aTrans->ident());
138 if (!isForRunway(aWay)) {
139 throw sg_io_exception("adding transition for unspecified runway:" + aWay->ident(), ident());
142 _runways[aWay] = aTrans;
145 void ArrivalDeparture::setCommon(const WayptVec& aWps)
150 bool ArrivalDeparture::commonRoute(Waypt* aEnroute, WayptVec& aPath, FGRunwayRef aRwy)
152 // assume we're routing from enroute, to the runway.
153 // for departures, we'll flip the result points
155 Transition* t = findTransitionByEnroute(aEnroute);
156 WayptVec::iterator firstCommon = _common.begin();
158 t->route(aEnroute, aPath);
160 Waypt* transEnd = t->procedureEnd();
161 for (; firstCommon != _common.end(); ++firstCommon) {
162 if ((*firstCommon)->matches(transEnd)) {
163 // found transition->common point, stop search
166 } // of common points
168 // if we hit this point, the transition doesn't end (start, for a SID) on
169 // a common point. We assume this means we should just append the entire
170 // common section after the transition.
171 firstCommon = _common.begin();
173 if (aEnroute && !(*firstCommon)->matches(aEnroute)) {
176 } // of not using a transition
178 // append (some) common points
179 aPath.insert(aPath.end(), firstCommon, _common.end());
182 // no runway specified, we're done
186 RunwayTransitionMap::iterator r = _runways.find(aRwy);
187 assert(r != _runways.end());
189 // no transitions specified. Not great, but not
190 // much we can do about it. Calling code will insert VECTORS to the approach
191 // if required, or maybe there's an approach transition defined.
195 SG_LOG(SG_GENERAL, SG_INFO, ident() << " using runway transition for " << r->first->ident());
196 r->second->route(NULL, aPath);
200 Transition* ArrivalDeparture::findTransitionByEnroute(Waypt* aEnroute) const
206 WptTransitionMap::const_iterator eit;
207 for (eit = _enrouteTransitions.begin(); eit != _enrouteTransitions.end(); ++eit) {
208 if (eit->second->enroute()->matches(aEnroute)) {
209 SG_LOG(SG_GENERAL, SG_INFO, ident() << " using enroute transition " << eit->second->ident());
212 } // of enroute transition iteration
217 WayptRef ArrivalDeparture::findBestTransition(const SGGeod& aPos) const
219 // no transitions, that's easy
220 if (_enrouteTransitions.empty()) {
221 SG_LOG(SG_GENERAL, SG_INFO, "no enroute transitions for " << ident());
222 return _common.front();
227 WptTransitionMap::const_iterator eit;
228 for (eit = _enrouteTransitions.begin(); eit != _enrouteTransitions.end(); ++eit) {
229 WayptRef c = eit->second->enroute();
230 SG_LOG(SG_GENERAL, SG_INFO, "findBestTransition for " << ident() << ", looking at " << c->ident());
231 // assert(c->hasFixedPosition());
232 double cd = SGGeodesy::distanceM(aPos, c->position());
234 if (cd < d) { // distance to 'c' is less, new best match
238 } // of transitions iteration
244 WayptRef ArrivalDeparture::findTransitionByName(const string& aIdent) const
246 WptTransitionMap::const_iterator eit;
247 for (eit = _enrouteTransitions.begin(); eit != _enrouteTransitions.end(); ++eit) {
248 WayptRef c = eit->second->enroute();
249 if (c->ident() == aIdent) {
257 ////////////////////////////////////////////////////////////////////////////
259 SID::SID(const string& aIdent) :
260 ArrivalDeparture(aIdent)
264 bool SID::route(FGRunwayRef aWay, Waypt* aEnroute, WayptVec& aPath)
266 if (!isForRunway(aWay)) {
267 SG_LOG(SG_GENERAL, SG_WARN, "SID " << ident() << " not for runway " << aWay->ident());
272 if (!commonRoute(aEnroute, path, aWay)) {
276 // SID waypoints (including transitions) are stored reversed, so we can
277 // re-use the routing code. This is where we fix the ordering for client code
278 std::back_insert_iterator<WayptVec> bi(aPath);
279 std::reverse_copy(path.begin(), path.end(), bi);
284 ////////////////////////////////////////////////////////////////////////////
286 STAR::STAR(const string& aIdent) :
287 ArrivalDeparture(aIdent)
291 bool STAR::route(FGRunwayRef aWay, Waypt* aEnroute, WayptVec& aPath)
293 if (aWay && !isForRunway(aWay)) {
297 return commonRoute(aEnroute, aPath, aWay);
300 /////////////////////////////////////////////////////////////////////////////
302 Transition::Transition(const std::string& aIdent, Procedure* aPr) :
309 void Transition::setPrimary(const WayptVec& aWps)
312 assert(!_primary.empty());
313 _primary[0]->setFlag(WPT_TRANSITION, true);
316 WayptRef Transition::enroute() const
318 assert(!_primary.empty());
322 WayptRef Transition::procedureEnd() const
324 assert(!_primary.empty());
325 return _primary[_primary.size() - 1];
328 bool Transition::route(Waypt* aEnroute, WayptVec& aPath)
330 if (aEnroute && !enroute()->matches(aEnroute)) {
334 aPath.insert(aPath.end(), _primary.begin(), _primary.end());