]> git.mxchange.org Git - flightgear.git/blob - src/Navaids/procedure.cxx
Pass key-mod state into pick-callbacks.
[flightgear.git] / src / Navaids / procedure.cxx
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/runways.hxx>
28 #include <Navaids/waypoint.hxx>
29
30 using std::string;
31
32 namespace flightgear
33 {
34   
35 static void markWaypoints(WayptVec& wps, WayptFlag f)
36 {
37   for (unsigned int i=0; i<wps.size(); ++i) {
38     wps[i]->setFlag(f, true);
39   }
40 }
41
42 Procedure::Procedure(const string& aIdent) :
43   _ident(aIdent)
44 {
45 }
46
47 Approach::Approach(const string& aIdent, ProcedureType ty) : 
48   Procedure(aIdent),
49   _type(ty)
50 {
51
52 }
53     
54 Approach* Approach::createTempApproach(const std::string& aIdent, FGRunway* aRunway, const WayptVec& aPath)
55 {
56     Approach* app = new Approach(aIdent, PROCEDURE_APPROACH_RNAV);
57     app->setRunway(aRunway);
58     app->setPrimaryAndMissed(aPath, WayptVec());
59     return app;
60 }
61
62 void Approach::setRunway(FGRunwayRef aRwy)
63 {
64   _runway = aRwy;
65 }
66
67 FGAirport* Approach::airport() const
68 {
69   return _runway->airport();
70 }
71
72 RunwayVec Approach::runways() const
73 {
74   RunwayVec r;
75   r.push_back(_runway);
76   return r;
77 }
78   
79 void Approach::setPrimaryAndMissed(const WayptVec& aPrimary, const WayptVec& aMissed)
80 {
81   _primary = aPrimary;
82   _primary[0]->setFlag(WPT_IAF, true);
83   _primary[_primary.size()-1]->setFlag(WPT_FAF, true);
84   markWaypoints(_primary, WPT_APPROACH);
85   
86   _missed = aMissed;
87   
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);
93   }
94 }
95
96 void Approach::addTransition(Transition* aTrans)
97 {
98   WayptRef entry = aTrans->enroute();
99   _transitions[entry] = aTrans;
100   aTrans->mark(WPT_APPROACH);
101 }
102
103 bool Approach::route(WayptRef aIAF, WayptVec& aWps)
104 {
105   if (aIAF.valid()) {
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)) {
111         t->route(aWps);
112         haveTrans = true;
113         break;
114       }
115     } // of transitions iteration
116     
117     if (!haveTrans) {
118       SG_LOG(SG_NAVAID, SG_INFO, "approach " << ident() << " has no transition " <<
119         "for IAF: " << aIAF->ident());
120       return false;
121     }
122   }
123   
124   return routeFromVectors(aWps);
125 }
126
127 bool Approach::routeFromVectors(WayptVec& aWps)
128 {
129   aWps.insert(aWps.end(), _primary.begin(), _primary.end());
130   RunwayWaypt* rwy = new RunwayWaypt(_runway, NULL);
131   rwy->setFlag(WPT_APPROACH);
132   aWps.push_back(rwy);
133   aWps.insert(aWps.end(), _missed.begin(), _missed.end());
134   return true;
135 }
136
137 bool Approach::isApproach(ProcedureType ty)
138 {
139   return (ty >= PROCEDURE_APPROACH_ILS) && (ty <= PROCEDURE_APPROACH_RNAV);
140 }
141   
142 //////////////////////////////////////////////////////////////////////////////
143
144 ArrivalDeparture::ArrivalDeparture(const string& aIdent, FGAirport* apt) :
145   Procedure(aIdent),
146   _airport(apt)
147 {
148 }
149
150 void ArrivalDeparture::addRunway(FGRunwayRef aWay)
151 {
152   assert(aWay->airport() == _airport);
153   _runways[aWay] = NULL;
154 }
155
156 bool ArrivalDeparture::isForRunway(const FGRunway* aWay) const
157 {
158   // null runway always passes
159   if (!aWay) {
160     return true;
161   }
162   
163   FGRunwayRef r(const_cast<FGRunway*>(aWay));
164   return (_runways.count(r) > 0);
165 }
166
167 RunwayVec ArrivalDeparture::runways() const
168 {
169   RunwayVec r;
170   RunwayTransitionMap::const_iterator it = _runways.begin();
171   for (; it != _runways.end(); ++it) {
172     r.push_back(it->first);
173   }
174   
175   return r;
176 }
177     
178 void ArrivalDeparture::addTransition(Transition* aTrans)
179 {
180   WayptRef entry = aTrans->enroute();
181   aTrans->mark(flagType());
182   _enrouteTransitions[entry] = aTrans;
183 }
184
185 string_list ArrivalDeparture::transitionIdents() const
186 {
187   string_list r;
188   WptTransitionMap::const_iterator eit;
189   for (eit = _enrouteTransitions.begin(); eit != _enrouteTransitions.end(); ++eit) {
190     r.push_back(eit->second->ident());
191   }
192   return r;
193 }
194   
195 void ArrivalDeparture::addRunwayTransition(FGRunwayRef aWay, Transition* aTrans)
196 {
197   assert(aWay->ident() == aTrans->ident());
198   if (!isForRunway(aWay)) {
199     throw sg_io_exception("adding transition for unspecified runway:" + aWay->ident(), ident());
200   }
201   
202   aTrans->mark(flagType());
203   _runways[aWay] = aTrans;
204 }
205
206 void ArrivalDeparture::setCommon(const WayptVec& aWps)
207 {
208   _common = aWps;
209   markWaypoints(_common, flagType());
210 }
211
212 bool ArrivalDeparture::commonRoute(Transition* t, WayptVec& aPath, FGRunwayRef aRwy)
213 {
214   // assume we're routing from enroute, to the runway.
215   // for departures, we'll flip the result points
216
217   WayptVec::iterator firstCommon = _common.begin();
218   if (t) {
219     t->route(aPath);
220
221     Waypt* transEnd = t->procedureEnd();
222     for (; firstCommon != _common.end(); ++firstCommon) {
223       if ((*firstCommon)->matches(transEnd)) {
224         // found transition->common point, stop search
225         break;
226       }
227     } // of common points
228     
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();
233   } else {
234     // no tranasition
235   } // of not using a transition
236   
237   // append (some) common points
238   aPath.insert(aPath.end(), firstCommon, _common.end());
239   
240   if (!aRwy) {
241     // no runway specified, we're done
242     return true;
243   }
244   
245   RunwayTransitionMap::iterator r = _runways.find(aRwy);
246   assert(r != _runways.end());
247   if (!r->second) {
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.
251     return true;
252   }
253   
254   SG_LOG(SG_NAVAID, SG_INFO, ident() << " using runway transition for " << r->first->ident());
255   r->second->route(aPath);
256   return true;
257 }
258
259 Transition* ArrivalDeparture::findTransitionByEnroute(Waypt* aEnroute) const
260 {
261   if (!aEnroute) {
262     return NULL;
263   }
264   
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());
269       return eit->second;
270     }
271   } // of enroute transition iteration
272   
273   return NULL;
274 }
275
276 WayptRef ArrivalDeparture::findBestTransition(const SGGeod& aPos) const
277 {
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();
282   }
283   
284   double d = 1e9;
285   WayptRef w;
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());
292     
293     if (cd < d) { // distance to 'c' is less, new best match
294       d = cd;
295       w = c;
296     }
297   } // of transitions iteration
298   
299   assert(w);
300   return w;
301 }
302
303 Transition* ArrivalDeparture::findTransitionByName(const string& aIdent) const
304 {
305   WptTransitionMap::const_iterator eit;
306   for (eit = _enrouteTransitions.begin(); eit != _enrouteTransitions.end(); ++eit) {
307     if (eit->second->ident() == aIdent) {
308       return eit->second;
309     }
310   }
311   
312   return NULL;
313 }
314
315 ////////////////////////////////////////////////////////////////////////////
316
317 SID::SID(const string& aIdent, FGAirport* apt) :
318     ArrivalDeparture(aIdent, apt)
319 {
320 }
321
322 bool SID::route(FGRunwayRef aWay, Transition* trans, WayptVec& aPath)
323 {
324   if (!isForRunway(aWay)) {
325     SG_LOG(SG_NAVAID, SG_WARN, "SID " << ident() << " not for runway " << aWay->ident());
326     return false;
327   }
328   
329   WayptVec path;
330   if (!commonRoute(trans, path, aWay)) {
331     return false;
332   }
333   
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);
338
339   return true;
340 }
341     
342 SID* SID::createTempSID(const std::string& aIdent, FGRunway* aRunway, const WayptVec& aPath)
343 {
344 // flip waypoints since SID stores them reversed
345     WayptVec path;
346     std::back_insert_iterator<WayptVec> bi(path);
347     std::reverse_copy(aPath.begin(), aPath.end(), bi);
348     
349     SID* sid = new SID(aIdent, aRunway->airport());
350     sid->setCommon(path);
351     sid->addRunway(aRunway);
352     return sid;
353 }
354
355 ////////////////////////////////////////////////////////////////////////////
356
357 STAR::STAR(const string& aIdent, FGAirport* apt) :
358     ArrivalDeparture(aIdent, apt)
359 {
360 }
361
362 bool STAR::route(FGRunwayRef aWay, Transition* trans, WayptVec& aPath)
363 {
364   if (aWay && !isForRunway(aWay)) {
365     return false;
366   }
367     
368   return commonRoute(trans, aPath, aWay);
369 }
370
371 /////////////////////////////////////////////////////////////////////////////
372
373 Transition::Transition(const std::string& aIdent, ProcedureType ty, Procedure* aPr) :
374   Procedure(aIdent),
375   _type(ty),
376   _parent(aPr)
377 {
378   assert(aPr);
379 }
380   
381 void Transition::setPrimary(const WayptVec& aWps)
382 {
383   _primary = aWps;
384   assert(!_primary.empty());
385   _primary[0]->setFlag(WPT_TRANSITION, true);
386 }
387
388 WayptRef Transition::enroute() const
389 {
390   assert(!_primary.empty());
391   return _primary[0];
392 }
393
394 WayptRef Transition::procedureEnd() const
395 {
396   assert(!_primary.empty());
397   return _primary[_primary.size() - 1];
398 }
399
400 bool Transition::route(WayptVec& aPath)
401 {
402   aPath.insert(aPath.end(), _primary.begin(), _primary.end());
403   return true;
404 }
405
406 FGAirport* Transition::airport() const
407 {
408   return _parent->airport();
409 }
410   
411 void Transition::mark(WayptFlag f)
412 {
413   markWaypoints(_primary, f);
414 }
415   
416 } // of namespace