1
0
Fork 0
flightgear/src/Navaids/procedure.cxx
2010-10-20 11:48:06 +01:00

336 lines
8.6 KiB
C++

// procedure.cxx - define route storing an approach, arrival or departure procedure
// Written by James Turner, started 2009.
//
// Copyright (C) 2009 Curtis L. Olson
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "procedure.hxx"
#include <cassert>
#include <algorithm> // for reverse_copy
#include <simgear/structure/exception.hxx>
#include <Navaids/waypoint.hxx>
using std::string;
namespace flightgear
{
Procedure::Procedure(const string& aIdent) :
_ident(aIdent)
{
}
Approach::Approach(const string& aIdent) :
Procedure(aIdent)
{
}
void Approach::setRunway(FGRunwayRef aRwy)
{
_runway = aRwy;
}
void Approach::setPrimaryAndMissed(const WayptVec& aPrimary, const WayptVec& aMissed)
{
_primary = aPrimary;
_primary[0]->setFlag(WPT_IAF, true);
_primary[_primary.size()-1]->setFlag(WPT_FAF, true);
_missed = aMissed;
if (!_missed.empty()) {
// mark the first point as the published missed-approach point
_missed[0]->setFlag(WPT_MAP, true);
// mark all the points as being on the missed approach route
for (unsigned int i=0; i<_missed.size(); ++i) {
_missed[i]->setFlag(WPT_MISS, true);
}
}
}
void Approach::addTransition(Transition* aTrans)
{
WayptRef entry = aTrans->enroute();
_transitions[entry] = aTrans;
}
bool Approach::route(WayptRef aIAF, WayptVec& aWps)
{
WptTransitionMap::iterator it;
bool haveTrans = false;
for (it = _transitions.begin(); it != _transitions.end(); ++it) {
Transition* t= it->second;
if (t->route(aIAF, aWps)) {
haveTrans = true;
break;
}
} // of transitions iteration
if (!haveTrans) {
SG_LOG(SG_GENERAL, SG_INFO, "approach " << ident() << " has no transition " <<
"for IAF: " << aIAF->ident());
return false;
}
aWps.insert(aWps.end(), _primary.begin(), _primary.end());
aWps.push_back(new RunwayWaypt(_runway, NULL));
aWps.insert(aWps.end(), _missed.begin(), _missed.end());
return true;
}
bool Approach::routeFromVectors(WayptVec& aWps)
{
aWps.insert(aWps.end(), _primary.begin(), _primary.end());
aWps.push_back(new RunwayWaypt(_runway, NULL));
aWps.insert(aWps.end(), _missed.begin(), _missed.end());
return true;
}
//////////////////////////////////////////////////////////////////////////////
ArrivalDeparture::ArrivalDeparture(const string& aIdent) :
Procedure(aIdent)
{
}
void ArrivalDeparture::addRunway(FGRunwayRef aWay)
{
_runways[aWay] = NULL;
}
bool ArrivalDeparture::isForRunway(FGRunwayRef aWay) const
{
// null runway always passes
if (!aWay) {
return true;
}
return (_runways.count(aWay));
}
void ArrivalDeparture::addTransition(Transition* aTrans)
{
WayptRef entry = aTrans->enroute();
_enrouteTransitions[entry] = aTrans;
}
void ArrivalDeparture::addRunwayTransition(FGRunwayRef aWay, Transition* aTrans)
{
assert(aWay->ident() == aTrans->ident());
if (!isForRunway(aWay)) {
throw sg_io_exception("adding transition for unspecified runway:" + aWay->ident(), ident());
}
_runways[aWay] = aTrans;
}
void ArrivalDeparture::setCommon(const WayptVec& aWps)
{
_common = aWps;
}
bool ArrivalDeparture::commonRoute(Waypt* aEnroute, WayptVec& aPath, FGRunwayRef aRwy)
{
// assume we're routing from enroute, to the runway.
// for departures, we'll flip the result points
Transition* t = findTransitionByEnroute(aEnroute);
WayptVec::iterator firstCommon = _common.begin();
if (t) {
t->route(aEnroute, aPath);
Waypt* transEnd = t->procedureEnd();
for (; firstCommon != _common.end(); ++firstCommon) {
if ((*firstCommon)->matches(transEnd)) {
// found transition->common point, stop search
break;
}
} // of common points
// if we hit this point, the transition doesn't end (start, for a SID) on
// a common point. We assume this means we should just append the entire
// common section after the transition.
firstCommon = _common.begin();
} else {
if (aEnroute && !(*firstCommon)->matches(aEnroute)) {
return false;
}
} // of not using a transition
// append (some) common points
aPath.insert(aPath.end(), firstCommon, _common.end());
if (!aRwy) {
// no runway specified, we're done
return true;
}
RunwayTransitionMap::iterator r = _runways.find(aRwy);
assert(r != _runways.end());
if (!r->second) {
// no transitions specified. Not great, but not
// much we can do about it. Calling code will insert VECTORS to the approach
// if required, or maybe there's an approach transition defined.
return true;
}
SG_LOG(SG_GENERAL, SG_INFO, ident() << " using runway transition for " << r->first->ident());
r->second->route(NULL, aPath);
return true;
}
Transition* ArrivalDeparture::findTransitionByEnroute(Waypt* aEnroute) const
{
if (!aEnroute) {
return NULL;
}
WptTransitionMap::const_iterator eit;
for (eit = _enrouteTransitions.begin(); eit != _enrouteTransitions.end(); ++eit) {
if (eit->second->enroute()->matches(aEnroute)) {
SG_LOG(SG_GENERAL, SG_INFO, ident() << " using enroute transition " << eit->second->ident());
return eit->second;
}
} // of enroute transition iteration
return NULL;
}
WayptRef ArrivalDeparture::findBestTransition(const SGGeod& aPos) const
{
// no transitions, that's easy
if (_enrouteTransitions.empty()) {
SG_LOG(SG_GENERAL, SG_INFO, "no enroute transitions for " << ident());
return _common.front();
}
double d = 1e9;
WayptRef w;
WptTransitionMap::const_iterator eit;
for (eit = _enrouteTransitions.begin(); eit != _enrouteTransitions.end(); ++eit) {
WayptRef c = eit->second->enroute();
SG_LOG(SG_GENERAL, SG_INFO, "findBestTransition for " << ident() << ", looking at " << c->ident());
// assert(c->hasFixedPosition());
double cd = SGGeodesy::distanceM(aPos, c->position());
if (cd < d) { // distance to 'c' is less, new best match
d = cd;
w = c;
}
} // of transitions iteration
assert(w);
return w;
}
WayptRef ArrivalDeparture::findTransitionByName(const string& aIdent) const
{
WptTransitionMap::const_iterator eit;
for (eit = _enrouteTransitions.begin(); eit != _enrouteTransitions.end(); ++eit) {
WayptRef c = eit->second->enroute();
if (c->ident() == aIdent) {
return c;
}
}
return NULL;
}
////////////////////////////////////////////////////////////////////////////
SID::SID(const string& aIdent) :
ArrivalDeparture(aIdent)
{
}
bool SID::route(FGRunwayRef aWay, Waypt* aEnroute, WayptVec& aPath)
{
if (!isForRunway(aWay)) {
SG_LOG(SG_GENERAL, SG_WARN, "SID " << ident() << " not for runway " << aWay->ident());
return false;
}
WayptVec path;
if (!commonRoute(aEnroute, path, aWay)) {
return false;
}
// SID waypoints (including transitions) are stored reversed, so we can
// re-use the routing code. This is where we fix the ordering for client code
std::back_insert_iterator<WayptVec> bi(aPath);
std::reverse_copy(path.begin(), path.end(), bi);
return true;
}
////////////////////////////////////////////////////////////////////////////
STAR::STAR(const string& aIdent) :
ArrivalDeparture(aIdent)
{
}
bool STAR::route(FGRunwayRef aWay, Waypt* aEnroute, WayptVec& aPath)
{
if (aWay && !isForRunway(aWay)) {
return false;
}
return commonRoute(aEnroute, aPath, aWay);
}
/////////////////////////////////////////////////////////////////////////////
Transition::Transition(const std::string& aIdent, Procedure* aPr,
const WayptVec& aWps) :
_ident(aIdent),
_parent(aPr),
_primary(aWps)
{
assert(aPr);
assert(!_primary.empty());
_primary[0]->setFlag(WPT_TRANSITION, true);
}
WayptRef Transition::enroute() const
{
assert(!_primary.empty());
return _primary[0];
}
WayptRef Transition::procedureEnd() const
{
assert(!_primary.empty());
return _primary[_primary.size() - 1];
}
bool Transition::route(Waypt* aEnroute, WayptVec& aPath)
{
if (aEnroute && !enroute()->matches(aEnroute)) {
return false;
}
aPath.insert(aPath.end(), _primary.begin(), _primary.end());
return true;
}
} // of namespace