1
0
Fork 0

Overhaul the ground-net / parking code.

Use the nav-data-cache to cache groundnet information, including
parking positions and the taxi-node graph.
This commit is contained in:
James Turner 2012-10-01 17:18:36 +01:00
parent 72131a4a49
commit afcdbd3158
26 changed files with 1035 additions and 798 deletions

View file

@ -862,7 +862,8 @@ bool FGAIAircraft::handleAirportEndPoints(FGAIWaypoint* prev, time_t now) {
//cerr << trafficRef->getCallSign() << " has passed waypoint " << prev->name << " at speed " << speed << endl;
//cerr << "Passing waypoint : " << prev->getName() << endl;
if (prev->contains("PushBackPoint")) {
dep->getDynamics()->releaseParking(fp->getGate());
// clearing the parking assignment will release the gate
fp->setGate(ParkingAssignment());
AccelTo(0.0);
//setTaxiClearanceRequest(true);
}
@ -1209,18 +1210,15 @@ void FGAIAircraft::updatePitchAngleTarget() {
}
}
string FGAIAircraft::atGate() {
string tmp("");
if (fp->getLeg() < 3) {
if (trafficRef) {
if (fp->getGate() > 0) {
FGParking *park =
trafficRef->getDepartureAirport()->getDynamics()->getParking(fp->getGate());
tmp = park->getName();
}
}
string FGAIAircraft::atGate()
{
if ((fp->getLeg() < 3) && trafficRef) {
if (fp->getParkingGate()) {
return fp->getParkingGate()->ident();
}
}
return tmp;
return string();
}
void FGAIAircraft::handleATCRequests() {

View file

@ -105,9 +105,8 @@ FGAIFlightPlan::FGAIFlightPlan()
start_time = 0;
arrivalTime = 0;
leg = 10;
gateId = 0;
lastNodeVisited = 0;
taxiRoute = 0;
// taxiRoute = 0;
wpt_iterator = waypoints.begin();
isValid = true;
}
@ -121,9 +120,8 @@ FGAIFlightPlan::FGAIFlightPlan(const string& filename)
start_time = 0;
arrivalTime = 0;
leg = 10;
gateId = 0;
lastNodeVisited = 0;
taxiRoute = 0;
// taxiRoute = 0;
isValid = parseProperties(filename);
@ -161,9 +159,8 @@ FGAIFlightPlan::FGAIFlightPlan(FGAIAircraft *ac,
start_time = start;
arrivalTime = 0;
leg = 10;
gateId = 0;
lastNodeVisited = 0;
taxiRoute = 0;
// taxiRoute = 0;
if (parseProperties(p)) {
isValid = true;
@ -176,16 +173,7 @@ FGAIFlightPlan::FGAIFlightPlan(FGAIAircraft *ac,
FGAIFlightPlan::~FGAIFlightPlan()
{
deleteWaypoints();
delete taxiRoute;
// if we're parked at a gate, release it
if (gateId >= 0) {
FGAirport* apt = (leg >= 7) ? arrival : departure;
if (apt) {
SG_LOG(SG_AI, SG_INFO, "releasing parking gate " << gateId << " at " << apt->ident());
apt->getDynamics()->releaseParking(gateId);
}
}
//delete taxiRoute;
}
void FGAIFlightPlan::createWaypoints(FGAIAircraft *ac,
@ -227,7 +215,6 @@ void FGAIFlightPlan::createWaypoints(FGAIAircraft *ac,
isValid = create(ac, dep, arr, leg, alt, speed, lat, lon,
firstLeg, radius, fltType, acType, airline, dist);
wpt_iterator = waypoints.begin();
}
bool FGAIFlightPlan::parseProperties(const std::string& filename)
@ -454,14 +441,6 @@ void FGAIFlightPlan::restart()
wpt_iterator = waypoints.begin();
}
void FGAIFlightPlan::deleteTaxiRoute()
{
delete taxiRoute;
taxiRoute = 0;
}
int FGAIFlightPlan::getRouteIndex(int i) {
if ((i > 0) && (i < (int)waypoints.size())) {
return waypoints[i]->getRouteIndex();
@ -470,7 +449,6 @@ int FGAIFlightPlan::getRouteIndex(int i) {
return 0;
}
double FGAIFlightPlan::checkTrackLength(string wptName) {
// skip the first two waypoints: first one is behind, second one is partially done;
double trackDistance = 0;
@ -494,3 +472,13 @@ void FGAIFlightPlan::shortenToFirst(unsigned int number, string name)
}
(waypoints.back())->setName((waypoints.back())->getName() + name);
}
void FGAIFlightPlan::setGate(ParkingAssignment pka)
{
gate = pka;
}
FGParking* FGAIFlightPlan::getParkingGate()
{
return gate.parking();
}

View file

@ -25,6 +25,8 @@
#include <simgear/compiler.h>
#include <simgear/math/SGMath.hxx>
#include <simgear/structure/SGSharedPtr.hxx>
#include <Navaids/positioned.hxx>
#include <Airports/dynamics.hxx>
// forward decls
class FGTaxiRoute;
@ -139,8 +141,7 @@ public:
void setLeg(int val) { leg = val;}
void setTime(time_t st) { start_time = st; }
int getGate() const { return gateId; }
void setGate(int id) { gateId = id; };
double getLeadInAngle() const { return leadInAngle; }
const std::string& getRunway() const;
@ -149,9 +150,9 @@ public:
bool getRepeat(void) const { return repeat; }
void restart(void);
int getNrOfWayPoints() { return waypoints.size(); }
int getRouteIndex(int i); // returns the AI related index of this current routes.
FGTaxiRoute *getTaxiRoute() { return taxiRoute; }
void deleteTaxiRoute();
int getRouteIndex(int i); // returns the AI related index of this current routes.
std::string getRunway() { return activeRunway; }
bool isActive(time_t time) {return time >= this->getStartTime();}
@ -172,6 +173,8 @@ public:
void shortenToFirst(unsigned int number, std::string name);
void setGate(ParkingAssignment pka);
FGParking* getParkingGate();
private:
FGAIFlightPlan *sid;
typedef std::vector <FGAIWaypoint*> wpt_vector_type;
@ -188,9 +191,9 @@ private:
time_t start_time;
time_t arrivalTime; // For AI/ATC purposes.
int leg;
int gateId, lastNodeVisited;
ParkingAssignment gate;
PositionedID lastNodeVisited;
std::string activeRunway;
FGTaxiRoute *taxiRoute;
std::string name;
bool isValid;
FGAirportRef departure, arrival;

View file

@ -214,9 +214,9 @@ bool FGAIFlightPlan::createTakeoffTaxi(FGAIAircraft * ac, bool firstFlight,
// and place the model at the location of the gate.
if (firstFlight)
{
gateId = apt->getDynamics()->getAvailableParking(radius, fltType,
gate = apt->getDynamics()->getAvailableParking(radius, fltType,
acType, airline);
if (gateId < 0) {
if (!gate.isValid()) {
SG_LOG(SG_AI, SG_WARN, "Could not find parking for a " <<
acType <<
" of flight type " << fltType <<
@ -245,7 +245,7 @@ bool FGAIFlightPlan::createTakeoffTaxi(FGAIAircraft * ac, bool firstFlight,
}
intVec ids;
int runwayId = 0;
PositionedID runwayId = 0;
if (gn->getVersion() > 0) {
runwayId = gn->findNearestNodeOnRunway(runwayTakeoff);
} else {
@ -256,75 +256,74 @@ bool FGAIFlightPlan::createTakeoffTaxi(FGAIAircraft * ac, bool firstFlight,
// fallback mechanism for this.
// Starting from gate 0 in this case is a bit of a hack
// which requires a more proper solution later on.
delete taxiRoute;
taxiRoute = new FGTaxiRoute;
// delete taxiRoute;
// taxiRoute = new FGTaxiRoute;
// Determine which node to start from.
int node = 0;
PositionedID node = 0;
// Find out which node to start from
FGParking *park = apt->getDynamics()->getParking(gateId);
FGParking *park = gate.parking();
if (park) {
node = park->getPushBackPoint();
}
if (node == -1) {
node = gateId;
}
// HAndle case where parking doens't have a node
if ((node == 0) && park) {
if (firstFlight) {
node = gateId;
} else {
node = lastNodeVisited;
if (node == -1) {
node = park->guid();
} else if (node == 0) {
// HAndle case where parking doens't have a node
if (firstFlight) {
node = park->guid();
} else {
node = lastNodeVisited;
}
}
}
*taxiRoute = gn->findShortestRoute(node, runwayId);
FGTaxiRoute taxiRoute = gn->findShortestRoute(node, runwayId);
intVecIterator i;
if (taxiRoute->empty()) {
if (taxiRoute.empty()) {
createDefaultTakeoffTaxi(ac, apt, rwy);
return true;
}
taxiRoute->first();
taxiRoute.first();
//bool isPushBackPoint = false;
if (firstFlight) {
// If this is called during initialization, randomly
// skip a number of waypoints to get a more realistic
// taxi situation.
int nrWaypointsToSkip = rand() % taxiRoute->size();
int nrWaypointsToSkip = rand() % taxiRoute.size();
// but make sure we always keep two active waypoints
// to prevent a segmentation fault
for (int i = 0; i < nrWaypointsToSkip - 3; i++) {
taxiRoute->next(&node);
taxiRoute.next(&node);
}
apt->getDynamics()->releaseParking(gateId);
gate.release(); // free up our gate as required
} else {
if (taxiRoute->size() > 1) {
taxiRoute->next(&node); // chop off the first waypoint, because that is already the last of the pushback route
if (taxiRoute.size() > 1) {
taxiRoute.next(&node); // chop off the first waypoint, because that is already the last of the pushback route
}
}
// push each node on the taxi route as a waypoint
int route;
// int route;
//cerr << "Building taxi route" << endl;
while (taxiRoute->next(&node, &route)) {
while (taxiRoute.next(&node)) {
char buffer[10];
snprintf(buffer, 10, "%d", node);
snprintf(buffer, 10, "%lld", node);
FGTaxiNode *tn =
apt->getDynamics()->getGroundNetwork()->findNode(node);
FGAIWaypoint *wpt =
createOnGround(ac, buffer, tn->geod(), apt->getElevation(),
ac->getPerformance()->vTaxi());
wpt->setRouteIndex(route);
// wpt->setRouteIndex(route);
//cerr << "Nodes left " << taxiRoute->nodesLeft() << " ";
if (taxiRoute->nodesLeft() == 1) {
if (taxiRoute.nodesLeft() == 1) {
// Note that we actually have hold points in the ground network, but this is just an initial test.
//cerr << "Setting departurehold point: " << endl;
wpt->setName( wpt->getName() + string("DepartureHold"));
}
if (taxiRoute->nodesLeft() == 0) {
if (taxiRoute.nodesLeft() == 0) {
wpt->setName(wpt->getName() + string("Accel"));
}
pushBackWaypoint(wpt);
@ -356,9 +355,8 @@ void FGAIFlightPlan::createDefaultLandingTaxi(FGAIAircraft * ac,
ac->getPerformance()->vTaxi());
pushBackWaypoint(wpt);
FGParking* parkPos = aAirport->getDynamics()->getParking(gateId);
if (parkPos) {
wpt = createOnGround(ac, "ENDtaxi", parkPos->geod(), airportElev,
if (gate.isValid()) {
wpt = createOnGround(ac, "ENDtaxi", gate.parking()->geod(), airportElev,
ac->getPerformance()->vTaxi());
pushBackWaypoint(wpt);
}
@ -370,12 +368,10 @@ bool FGAIFlightPlan::createLandingTaxi(FGAIAircraft * ac, FGAirport * apt,
const string & acType,
const string & airline)
{
gateId = apt->getDynamics()->getAvailableParking(radius, fltType,
gate = apt->getDynamics()->getAvailableParking(radius, fltType,
acType, airline);
SGGeod lastWptPos =
SGGeod::fromDeg(waypoints.back()->getLongitude(),
waypoints.back()->getLatitude());
SGGeod lastWptPos = waypoints.back()->getPos();
FGGroundNetwork *gn = apt->getDynamics()->getGroundNetwork();
// Find a route from runway end to parking/gate.
@ -385,7 +381,7 @@ bool FGAIFlightPlan::createLandingTaxi(FGAIAircraft * ac, FGAirport * apt,
}
intVec ids;
int runwayId = 0;
PositionedID runwayId = 0;
if (gn->getVersion() == 1) {
runwayId = gn->findNearestNodeOnRunway(lastWptPos);
} else {
@ -396,34 +392,35 @@ bool FGAIFlightPlan::createLandingTaxi(FGAIAircraft * ac, FGAirport * apt,
// fallback mechanism for this.
// Starting from gate 0 is a bit of a hack...
//FGTaxiRoute route;
delete taxiRoute;
taxiRoute = new FGTaxiRoute;
if (gateId >= 0)
*taxiRoute = gn->findShortestRoute(runwayId, gateId);
// delete taxiRoute;
// taxiRoute = new FGTaxiRoute;
FGTaxiRoute taxiRoute;
if (gate.isValid())
taxiRoute = gn->findShortestRoute(runwayId, gate.parking()->guid());
else
*taxiRoute = gn->findShortestRoute(runwayId, 0);
taxiRoute = gn->findShortestRoute(runwayId, 0);
intVecIterator i;
if (taxiRoute->empty()) {
if (taxiRoute.empty()) {
createDefaultLandingTaxi(ac, apt);
return true;
}
int node;
taxiRoute->first();
int size = taxiRoute->size();
PositionedID node;
taxiRoute.first();
int size = taxiRoute.size();
// Omit the last two waypoints, as
// those are created by createParking()
int route;
// int route;
for (int i = 0; i < size - 2; i++) {
taxiRoute->next(&node, &route);
taxiRoute.next(&node);
char buffer[10];
snprintf(buffer, 10, "%d", node);
snprintf(buffer, 10, "%lld", node);
FGTaxiNode *tn = gn->findNode(node);
FGAIWaypoint *wpt =
createOnGround(ac, buffer, tn->geod(), apt->getElevation(),
ac->getPerformance()->vTaxi());
wpt->setRouteIndex(route);
// wpt->setRouteIndex(route);
pushBackWaypoint(wpt);
}
return true;
@ -638,21 +635,20 @@ bool FGAIFlightPlan::createDescent(FGAIAircraft * ac, FGAirport * apt,
origin = current;
}
double dAlt = 0; // = alt - (apt->getElevation() + 2000);
FGTaxiNode * tn = 0;
if (apt->getDynamics()->getGroundNetwork()) {
int node = apt->getDynamics()->getGroundNetwork()->findNearestNode(refPoint);
tn = apt->getDynamics()->getGroundNetwork()->findNode(node);
}
if (tn) {
dAlt = alt - ((tn->getElevationFt(apt->getElevation())) + 2000);
dAlt = alt - ((tn->getElevationFt()) + 2000);
} else {
dAlt = alt - (apt->getElevation() + 2000);
}
double nPoints = 100;
char buffer[16];
// The descent path contains the following phases:
@ -814,7 +810,7 @@ bool FGAIFlightPlan::createDescent(FGAIAircraft * ac, FGAirport * apt,
//FGTaxiNode * tn = apt->getDynamics()->getGroundNetwork()->findNearestNode(initialTarget);
double currentAltitude = 0;
if (tn) {
currentAltitude = (tn->getElevationFt(apt->getElevation())) + 2000;
currentAltitude = (tn->getElevationFt()) + 2000;
} else {
currentAltitude = apt->getElevation() + 2000;
}
@ -906,12 +902,12 @@ bool FGAIFlightPlan::createLanding(FGAIAircraft * ac, FGAirport * apt,
{
double vTouchdown = ac->getPerformance()->vTouchdown();
double vTaxi = ac->getPerformance()->vTaxi();
double decel = ac->getPerformance()->deceleration() * 1.4;
double decel = ac->getPerformance()->decelerationOnGround();
double vApproach = ac->getPerformance()->vApproach();
double vTouchdownMetric = (vTouchdown * SG_NM_TO_METER) / 3600;
double vTaxiMetric = (vTaxi * SG_NM_TO_METER) / 3600;
double decelMetric = (decel * SG_NM_TO_METER) / 3600;
double vTouchdownMetric = vTouchdown * SG_KT_TO_MPS;
double vTaxiMetric = vTaxi * SG_KT_TO_MPS;
double decelMetric = decel * SG_KT_TO_MPS;
char buffer[12];
FGRunway * rwy = apt->getRunwayByIdent(activeRunway);
@ -996,15 +992,14 @@ bool FGAIFlightPlan::createParking(FGAIAircraft * ac, FGAirport * apt,
double aptElev = apt->getElevation();
double vTaxi = ac->getPerformance()->vTaxi();
double vTaxiReduced = vTaxi * (2.0 / 3.0);
FGParking* parking = apt->getDynamics()->getParking(gateId);
if (!parking) {
if (!gate.isValid()) {
wpt = createOnGround(ac, "END-Parking", apt->geod(), aptElev,
vTaxiReduced);
pushBackWaypoint(wpt);
return true;
}
FGParking* parking = gate.parking();
double heading = SGMiscd::normalizePeriodic(0, 360, parking->getHeading() + 180.0);
double az; // unused
SGGeod pos;

View file

@ -49,7 +49,6 @@ bool FGAIFlightPlan::createPushBack(FGAIAircraft *ac,
double vTaxi = ac->getPerformance()->vTaxi();
double vTaxiBackward = vTaxi * (-2.0/3.0);
double vTaxiReduced = vTaxi * (2.0/3.0);
FGTaxiRoute *pushBackRoute;
// Active runway can be conditionally set by ATC, so at the start of a new flight, this
// must be reset.
activeRunway.clear();
@ -63,9 +62,9 @@ bool FGAIFlightPlan::createPushBack(FGAIAircraft *ac,
// establish the parking position / gate if required
if (firstFlight) {
gateId = dep->getDynamics()->getAvailableParking(radius, fltType,
gate = dep->getDynamics()->getAvailableParking(radius, fltType,
aircraftType, airline);
if (gateId < 0) {
if (!gate.isValid()) {
SG_LOG(SG_AI, SG_WARN, "Warning: Could not find parking for a " <<
aircraftType <<
" of flight type " << fltType <<
@ -73,47 +72,47 @@ bool FGAIFlightPlan::createPushBack(FGAIAircraft *ac,
" at airport " << dep->getId());
return false;
}
} else {
dep->getDynamics()->getParking(gateId);
}
if (gateId < 0) {
if (!gate.isValid()) {
createPushBackFallBack(ac, firstFlight, dep,
radius, fltType, aircraftType, airline);
return true;
}
FGParking *parking = dep->getDynamics()->getParking(gateId);
int pushBackNode = parking->getPushBackPoint();
pushBackRoute = parking->getPushBackRoute();
if ((pushBackNode > 0) && (pushBackRoute == 0)) { // Load the already established route for this gate
int node, rte;
FGTaxiRoute route;
//cerr << "Creating push-back for " << gateId << " (" << parking->getName() << ") using push-back point " << pushBackNode << endl;
route = dep->getDynamics()->getGroundNetwork()->findShortestRoute(gateId, pushBackNode, false);
parking->setPushBackRoute(std::auto_ptr<FGTaxiRoute>(new FGTaxiRoute(route)));
pushBackRoute = parking->getPushBackRoute();
int size = pushBackRoute->size();
FGGroundNetwork* groundNet = dep->getDynamics()->getGroundNetwork();
FGParking *parking = gate.parking();
if (parking && parking->getPushBackPoint() > 0) {
FGTaxiRoute route = groundNet->findShortestRoute(parking->guid(), parking->getPushBackPoint(), false);
int size = route.size();
if (size < 2) {
SG_LOG(SG_AI, SG_ALERT, "Push back route from gate " << gateId << " has only " << size << " nodes.");
SG_LOG(SG_AI, SG_ALERT, "Using " << pushBackNode);
SG_LOG(SG_AI, SG_ALERT, "Push back route from gate " << parking->ident() << " has only " << size << " nodes.");
SG_LOG(SG_AI, SG_ALERT, "Using " << parking->getPushBackPoint());
}
pushBackRoute->first();
while (pushBackRoute->next(&node, &rte))
route.first();
PositionedID node, previous= 0;
while (route.next(&node))
{
//FGTaxiNode *tn = apt->getDynamics()->getGroundNetwork()->findSegment(node)->getEnd();
char buffer[10];
snprintf (buffer, 10, "%d", node);
FGTaxiNode *tn = dep->getDynamics()->getGroundNetwork()->findNode(node);
//ids.pop_back();
//wpt = new waypoint;
snprintf (buffer, 10, "%lld", node);
FGTaxiNode *tn = groundNet->findNode(node);
FGAIWaypoint *wpt = createOnGround(ac, string(buffer), tn->geod(), dep->getElevation(), vTaxiBackward);
wpt->setRouteIndex(rte);
if (previous) {
FGTaxiSegment* segment = groundNet->findSegment(previous, node);
wpt->setRouteIndex(segment->getIndex());
} else {
// not on the route yet, make up a unique segment ID
int x = (int) tn->guid();
wpt->setRouteIndex(x);
}
pushBackWaypoint(wpt);
previous = node;
}
// some special considerations for the last point:
waypoints.back()->setName(string("PushBackPoint"));
@ -123,36 +122,32 @@ bool FGAIFlightPlan::createPushBack(FGAIAircraft *ac,
ac->setTaxiClearanceRequest(false);
double az2 = 0.0;
//cerr << "Creating final push forward point for gate " << gateId << endl;
FGTaxiNode *tn = dep->getDynamics()->getGroundNetwork()->findNode(gateId);
// there aren't any routes for this parking.
// in cases like these we should flag the gate as being inoperative and return false
if (tn->arcs().empty()) {
SG_LOG(SG_AI, SG_ALERT, "Gate " << gateId << "doesn't seem to have routes associated with it.");
parking->setAvailable(false);
return false;
}
FGTaxiSegment* pushForwardSegment = tn->arcs().front();
lastNodeVisited = pushForwardSegment->getEnd()->getIndex();
double distance = pushForwardSegment->getLength();
FGTaxiSegment* pushForwardSegment = dep->getDynamics()->getGroundNetwork()->findSegment(parking->guid(), 0);
// there aren't any routes for this parking.
if (!pushForwardSegment) {
SG_LOG(SG_AI, SG_ALERT, "Gate " << parking->ident() << "doesn't seem to have routes associated with it.");
return false;
}
double parkingHeading = parking->getHeading();
for (int i = 1; i < 10; i++) {
SGGeod pushForwardPt;
SGGeodesy::direct(parking->geod(), parkingHeading,
((i / 10.0) * distance), pushForwardPt, az2);
char buffer[16];
snprintf(buffer, 16, "pushback-%02d", i);
FGAIWaypoint *wpt = createOnGround(ac, string(buffer), pushForwardPt, dep->getElevation(), vTaxiReduced);
lastNodeVisited = pushForwardSegment->getEnd()->getIndex();
double distance = pushForwardSegment->getLength();
wpt->setRouteIndex(pushForwardSegment->getIndex());
pushBackWaypoint(wpt);
}
// cerr << "Done " << endl;
waypoints.back()->setName(string("PushBackPoint"));
// cerr << "Done assinging new name" << endl;
double parkingHeading = parking->getHeading();
for (int i = 1; i < 10; i++) {
SGGeod pushForwardPt;
SGGeodesy::direct(parking->geod(), parkingHeading,
((i / 10.0) * distance), pushForwardPt, az2);
char buffer[16];
snprintf(buffer, 16, "pushback-%02d", i);
FGAIWaypoint *wpt = createOnGround(ac, string(buffer), pushForwardPt, dep->getElevation(), vTaxiReduced);
wpt->setRouteIndex(pushForwardSegment->getIndex());
pushBackWaypoint(wpt);
}
waypoints.back()->setName(string("PushBackPoint"));
// cerr << "Done assinging new name" << endl;
}
return true;

View file

@ -107,6 +107,11 @@ double PerformanceData::actualSpeed(FGAIAircraft* ac, double tgt_speed, double d
return speed;
}
double PerformanceData::decelerationOnGround() const
{
return _deceleration * BRAKE_SETTING;
}
double PerformanceData::actualBankAngle(FGAIAircraft* ac, double tgt_roll, double dt) {
// check maximum bank angle
if (fabs(tgt_roll) > _maxbank)

View file

@ -46,6 +46,7 @@ public:
inline double vTouchdown () { return _vTouchdown; };
inline double vCruise () { return _vCruise; };
double decelerationOnGround() const;
private:
double _acceleration;
double _deceleration;

View file

@ -101,12 +101,11 @@ void FGATCManager::init() {
FGAirport *apt = FGAirport::findByIdent(airport);
if (apt && onGround) {// && !runway.empty()) {
FGAirportDynamics* dcs = apt->getDynamics();
int park_index = dcs->getNrOfParkings() - 1;
//cerr << "found information: " << runway << " " << airport << ": parking = " << parking << endl;
fp = new FGAIFlightPlan;
while (park_index >= 0 && dcs->getParkingName(park_index) != parking) park_index--;
ParkingAssignment pk(dcs->getParkingByName(parking));
// No valid parking location, so either at the runway or at a random location.
if (parking.empty() || (park_index < 0)) {
if (!pk.isValid()) {
if (!runway.empty()) {
controller = apt->getDynamics()->getTowerController();
int stationFreq = apt->getDynamics()->getTowerFrequency(2);
@ -134,12 +133,11 @@ void FGATCManager::init() {
leg = 1;
//double, lat, lon, head; // Unused variables;
//int getId = apt->getDynamics()->getParking(gateId, &lat, &lon, &head);
FGParking* parking = dcs->getParking(park_index);
aircraftRadius = parking->getRadius();
string fltType = parking->getType(); // gate / ramp, ga, etc etc.
aircraftRadius = pk.parking()->getRadius();
string fltType = pk.parking()->getType(); // gate / ramp, ga, etc etc.
string aircraftType; // Unused.
string airline; // Currently used for gate selection, but a fallback mechanism will apply when not specified.
fp->setGate(park_index);
fp->setGate(pk);
if (!(fp->createPushBack(&ai_ac,
false,
apt,

View file

@ -1363,7 +1363,7 @@ void FGStartupController::render(bool visible)
} else {
elevationStart = ((i)->getAircraft()->_getAltitude() * SG_FEET_TO_METER);
}
double elevationEnd = segment->getEnd()->getElevationM(parent->getElevation()*SG_FEET_TO_METER);
double elevationEnd = segment->getEnd()->getElevationM();
if ((elevationEnd == 0) || (elevationEnd == parent->getElevation())) {
SGGeod center2 = end;
center2.setElevationM(SG_MAX_ELEVATION_M);
@ -1425,8 +1425,8 @@ void FGStartupController::render(bool visible)
obj_trans->setDataVariance(osg::Object::STATIC);
FGTaxiSegment *segment = parent->getGroundNetwork()->findSegment(k);
double elevationStart = segment->getStart()->getElevationM(parent->getElevation()*SG_FEET_TO_METER);
double elevationEnd = segment->getEnd ()->getElevationM(parent->getElevation()*SG_FEET_TO_METER);
double elevationStart = segment->getStart()->getElevationM();
double elevationEnd = segment->getEnd ()->getElevationM();
if ((elevationStart == 0) || (elevationStart == parent->getElevation())) {
SGGeod center2 = segment->getStart()->geod();
center2.setElevationM(SG_MAX_ELEVATION_M);

View file

@ -19,9 +19,14 @@
#include <cstdlib>
#include <cstring> // for strcmp
#include <boost/foreach.hpp>
#include "dynamicloader.hxx"
#include <Navaids/NavDataCache.hxx>
#include <Airports/dynamics.hxx>
#include <Airports/simple.hxx>
/*****************************************************************************
* Helper function for parsing position string
****************************************************************************/
@ -53,8 +58,25 @@ void FGAirportDynamicsXMLLoader::startXML () {
//cout << "FGAirportDynamicsLoader::Start XML" << endl;
}
void FGAirportDynamicsXMLLoader::endXML () {
//cout << "End XML" << endl;
void FGAirportDynamicsXMLLoader::endXML ()
{
std::map<PositionedID, int>::iterator it;
flightgear::NavDataCache* cache = flightgear::NavDataCache::instance();
for (it = _parkingPushbacks.begin(); it != _parkingPushbacks.end(); ++it) {
std::map<int, PositionedID>::iterator j = _idMap.find(it->second);
if (j == _idMap.end()) {
SG_LOG(SG_GENERAL, SG_WARN, "bad groundnet, no node for index:" << it->first);
continue;
}
cache->setParkingPushBackRoute(it->first, j->second);
}
BOOST_FOREACH(PositionedID id, _unreferencedNodes) {
SG_LOG(SG_GENERAL, SG_WARN, "unreferenced groundnet node:" << id);
}
}
void FGAirportDynamicsXMLLoader::startParking(const XMLAttributes &atts)
@ -100,10 +122,14 @@ void FGAirportDynamicsXMLLoader::startParking(const XMLAttributes &atts)
SGGeod pos(SGGeod::fromDeg(processPosition(lon), processPosition(lat)));
FGParking* pk = new FGParking(0, index, pos, heading, radius,
gateName + gateNumber, type, airlineCodes);
pk->setPushBackPoint(pushBackRoute);
_dynamics->addParking(pk);
PositionedID guid = flightgear::NavDataCache::instance()->insertParking(gateName + gateNumber, pos,
_dynamics->parent()->guid(),
heading, radius, type, airlineCodes);
if (pushBackRoute > 0) {
_parkingPushbacks[guid] = pushBackRoute;
}
_idMap[index] = guid;
}
void FGAirportDynamicsXMLLoader::startNode(const XMLAttributes &atts)
@ -140,9 +166,15 @@ void FGAirportDynamicsXMLLoader::startNode(const XMLAttributes &atts)
}
}
if (_idMap.find(index) != _idMap.end()) {
SG_LOG(SG_GENERAL, SG_WARN, "duplicate ground-net index:" << index);
}
SGGeod pos(SGGeod::fromDeg(processPosition(lon), processPosition(lat)));
FGTaxiNode* taxiNode = new FGTaxiNode(0, index, pos, onRunway, holdPointType);
_dynamics->getGroundNetwork()->addNode(taxiNode);
PositionedID guid = flightgear::NavDataCache::instance()->insertTaxiNode(pos,
_dynamics->parent()->guid(), holdPointType, onRunway);
_idMap[index] = guid;
_unreferencedNodes.insert(guid);
}
void FGAirportDynamicsXMLLoader::startArc(const XMLAttributes &atts)
@ -161,7 +193,22 @@ void FGAirportDynamicsXMLLoader::startArc(const XMLAttributes &atts)
isPushBackRoute = std::atoi(atts.getValue(i)) != 0;
}
_dynamics->getGroundNetwork()->addSegment(new FGTaxiSegment(begin, end, isPushBackRoute));
IntPair e(begin, end);
if (_arcSet.find(e) != _arcSet.end()) {
SG_LOG(SG_GENERAL, SG_WARN, _dynamics->parent()->ident() << " ground-net: skipping duplicate edge:" << begin << "->" << end);
return;
}
_arcSet.insert(e);
flightgear::NavDataCache::instance()->insertGroundnetEdge(_dynamics->parent()->guid(),
_idMap[begin], _idMap[end]);
_unreferencedNodes.erase(_idMap[begin]);
_unreferencedNodes.erase(_idMap[end]);
if (isPushBackRoute) {
flightgear::NavDataCache::instance()->markGroundnetAsPushback(_idMap[end]);
}
}
void FGAirportDynamicsXMLLoader::startElement (const char * name, const XMLAttributes &atts)

View file

@ -19,6 +19,7 @@
#include <simgear/xml/easyxml.hxx>
#include "dynamics.hxx"
#include <Navaids/positioned.hxx>
class FGAirportDynamicsXMLLoader : public XMLVisitor {
public:
@ -41,6 +42,20 @@ private:
FGAirportDynamics* _dynamics;
string value;
// map from local (groundnet.xml) to global (nav-cache) IDs for nodes
std::map<int, PositionedID> _idMap;
// data integrity - watch for unreferenced nodes and duplicated edges
typedef std::pair<int, int> IntPair;
std::set<IntPair> _arcSet;
std::set<PositionedID> _unreferencedNodes;
// map from allocated parking position to its local push-back node
// used to defer binding the push-back node until we've processed
// all nodes
std::map<PositionedID, int> _parkingPushbacks;
};
#endif

View file

@ -40,6 +40,7 @@
#include <Main/fg_props.hxx>
#include <Airports/runways.hxx>
#include <ATCDCL/ATCutils.hxx>
#include <Navaids/NavDataCache.hxx>
#include "simple.hxx"
#include "dynamics.hxx"
@ -49,6 +50,105 @@ using std::vector;
using std::sort;
using std::random_shuffle;
class ParkingAssignment::ParkingAssignmentPrivate
{
public:
ParkingAssignmentPrivate(FGParking* pk, FGAirport* apt) :
refCount(0),
parking(pk),
airport(apt)
{
assert(pk);
assert(apt);
retain(); // initial count of 1
}
~ParkingAssignmentPrivate()
{
airport->getDynamics()->releaseParking(parking->guid());
}
void release()
{
if ((--refCount) == 0) {
delete this;
}
}
void retain()
{
++refCount;
}
unsigned int refCount;
SGSharedPtr<FGParking> parking;
SGSharedPtr<FGAirport> airport;
};
ParkingAssignment::ParkingAssignment() :
_sharedData(NULL)
{
}
ParkingAssignment::~ParkingAssignment()
{
if (_sharedData) {
_sharedData->release();
}
}
ParkingAssignment::ParkingAssignment(FGParking* pk, FGAirport* apt) :
_sharedData(NULL)
{
if (pk) {
_sharedData = new ParkingAssignmentPrivate(pk, apt);
}
}
ParkingAssignment::ParkingAssignment(const ParkingAssignment& aOther) :
_sharedData(aOther._sharedData)
{
if (_sharedData) {
_sharedData->retain();
}
}
void ParkingAssignment::operator=(const ParkingAssignment& aOther)
{
if (_sharedData == aOther._sharedData) {
return; // self-assignment, special case
}
if (_sharedData) {
_sharedData->release();
}
_sharedData = aOther._sharedData;
if (_sharedData) {
_sharedData->retain();
}
}
void ParkingAssignment::release()
{
if (_sharedData) {
_sharedData->release();
_sharedData = NULL;
}
}
bool ParkingAssignment::isValid() const
{
return (_sharedData != NULL);
}
FGParking* ParkingAssignment::parking() const
{
return _sharedData ? _sharedData->parking.ptr() : NULL;
}
////////////////////////////////////////////////////////////////////////////////
FGAirportDynamics::FGAirportDynamics(FGAirport * ap):
_ap(ap), rwyPrefs(ap),
startupController (this),
@ -70,96 +170,68 @@ FGAirportDynamics::~FGAirportDynamics()
// Initialization required after XMLRead
void FGAirportDynamics::init()
{
// This may seem a bit weird to first randomly shuffle the parkings
// and then sort them again. However, parkings are sorted here by ascending
// radius. Since many parkings have similar radii, with each radius class they will
// still be allocated relatively systematically. Randomizing prior to sorting will
// prevent any initial orderings to be destroyed, leading (hopefully) to a more
// naturalistic gate assignment.
random_shuffle(parkings.begin(), parkings.end());
sort(parkings.begin(), parkings.end());
// add the gate positions to the ground network.
groundNetwork.setParent(_ap);
groundNetwork.addNodes(&parkings);
groundNetwork.init();
groundNetwork.init(_ap);
groundNetwork.setTowerController(&towerController);
}
int FGAirportDynamics::innerGetAvailableParking(double radius, const string & flType,
const string & acType,
FGParking* FGAirportDynamics::innerGetAvailableParking(double radius, const string & flType,
const string & airline,
bool skipEmptyAirlineCode)
{
BOOST_FOREACH(FGParking* i, parkings) {
// Taken by another aircraft, or no airline codes
if (!i->isAvailable()) {
flightgear::NavDataCache* cache = flightgear::NavDataCache::instance();
BOOST_FOREACH(PositionedID pk, cache->findAirportParking(_ap->guid(), flType, radius)) {
if (!isParkingAvailable(pk)) {
continue;
}
if (skipEmptyAirlineCode && i->getCodes().empty()) {
FGParking* parking = getParking(pk);
if (skipEmptyAirlineCode && parking->getCodes().empty()) {
continue;
}
// check airline codes match
if (!airline.empty() && !i->getCodes().empty()) {
if (i->getCodes().find(airline, 0) == string::npos) {
if (!airline.empty() && !parking->getCodes().empty()) {
if (parking->getCodes().find(airline, 0) == string::npos) {
continue;
}
}
// Type doesn't match
if (i->getType() != flType) {
continue;
}
// too small
if (i->getRadius() < radius) {
continue;
}
i->setAvailable(false);
return i->getIndex();
}
return -1;
}
int FGAirportDynamics::getAvailableParking(double radius, const string & flType,
const string & acType,
const string & airline)
{
if (parkings.empty()) {
return -1;
}
// most exact seach - airline codes must be present and match
int result = innerGetAvailableParking(radius, flType, acType, airline, true);
if (result >= 0) {
return result;
}
// more tolerant - gates with empty airline codes are permitted
result = innerGetAvailableParking(radius, flType, acType, airline, false);
if (result >= 0) {
return result;
}
// fallback - ignore the airline code entirely
return innerGetAvailableParking(radius, flType, acType, string(), false);
}
FGParking *FGAirportDynamics::getParking(int id)
{
BOOST_FOREACH(FGParking* i, parkings) {
if (id == i->getIndex()) {
return i;
}
setParkingAvailable(pk, false);
return parking;
}
return NULL;
}
string FGAirportDynamics::getParkingName(int id)
ParkingAssignment FGAirportDynamics::getAvailableParking(double radius, const string & flType,
const string & acType,
const string & airline)
{
SG_UNUSED(acType); // sadly not used at the moment
// most exact seach - airline codes must be present and match
FGParking* result = innerGetAvailableParking(radius, flType, airline, true);
if (result) {
return ParkingAssignment(result, _ap);
}
// more tolerant - gates with empty airline codes are permitted
result = innerGetAvailableParking(radius, flType, airline, false);
if (result) {
return ParkingAssignment(result, _ap);
}
// fallback - ignore the airline code entirely
result = innerGetAvailableParking(radius, flType, string(), false);
return result ? ParkingAssignment(result, _ap) : ParkingAssignment();
}
FGParking *FGAirportDynamics::getParking(PositionedID id) const
{
return static_cast<FGParking*>(flightgear::NavDataCache::instance()->loadById(id));
}
string FGAirportDynamics::getParkingName(PositionedID id) const
{
FGParking* p = getParking(id);
if (p) {
@ -169,25 +241,38 @@ string FGAirportDynamics::getParkingName(int id)
return string();
}
int FGAirportDynamics::findParkingByName(const std::string& name) const
ParkingAssignment FGAirportDynamics::getParkingByName(const std::string& name) const
{
BOOST_FOREACH(FGParking* i, parkings) {
if (name == i->getName()) {
return i->getIndex();
}
PositionedID guid = flightgear::NavDataCache::instance()->airportItemWithIdent(parent()->guid(), FGPositioned::PARKING, name);
if (guid == 0) {
return ParkingAssignment();
}
return -1;
return ParkingAssignment(getParking(guid), _ap);
}
void FGAirportDynamics::releaseParking(int id)
void FGAirportDynamics::setParkingAvailable(PositionedID guid, bool available)
{
if (id >= 0) {
FGParking* parking = getParking(id);
if (parking) {
parking->setAvailable(true);
}
}
if (available) {
releaseParking(guid);
} else {
occupiedParkings.insert(guid);
}
}
bool FGAirportDynamics::isParkingAvailable(PositionedID parking) const
{
return (occupiedParkings.find(parking) == occupiedParkings.end());
}
void FGAirportDynamics::releaseParking(PositionedID id)
{
ParkingSet::iterator it = occupiedParkings.find(id);
if (it == occupiedParkings.end()) {
return;
}
occupiedParkings.erase(it);
}
void FGAirportDynamics::setRwyUse(const FGRunwayPreference & ref)
@ -373,11 +458,6 @@ string FGAirportDynamics::chooseRunwayFallback()
return rwy->ident();
}
void FGAirportDynamics::addParking(FGParking* park)
{
parkings.push_back(park);
}
double FGAirportDynamics::getElevation() const
{
return _ap->getElevation();

View file

@ -22,6 +22,8 @@
#ifndef _AIRPORT_DYNAMICS_HXX_
#define _AIRPORT_DYNAMICS_HXX_
#include <set>
#include <ATC/trafficcontrol.hxx>
#include "parking.hxx"
#include "groundnetwork.hxx"
@ -31,12 +33,38 @@
class FGAirport;
class FGEnvironment;
class ParkingAssignment
{
public:
ParkingAssignment();
~ParkingAssignment();
// create a parking assignment (and mark it as unavailable)
ParkingAssignment(FGParking* pk, FGAirport* apt);
ParkingAssignment(const ParkingAssignment& aOther);
void operator=(const ParkingAssignment& aOther);
bool isValid() const;
FGParking* parking() const;
void release();
private:
void clear();
class ParkingAssignmentPrivate;
ParkingAssignmentPrivate* _sharedData;
};
class FGAirportDynamics {
private:
FGAirport* _ap;
FGParkingVec parkings;
typedef std::set<PositionedID> ParkingSet;
// if a parking item is in this set, it is occupied
ParkingSet occupiedParkings;
FGRunwayPreference rwyPrefs;
FGStartupController startupController;
FGGroundNetwork groundNetwork;
@ -63,8 +91,8 @@ private:
bool innerGetActiveRunway(const std::string &trafficType, int action, std::string &runway, double heading);
std::string chooseRwyByHeading(stringVec rwys, double heading);
int innerGetAvailableParking(double radius, const std::string & flType,
const std::string & acType, const std::string & airline,
FGParking* innerGetAvailableParking(double radius, const std::string & flType,
const std::string & airline,
bool skipEmptyAirlineCode);
public:
FGAirportDynamics(FGAirport* ap);
@ -98,28 +126,28 @@ public:
{ return _ap; }
void getActiveRunway(const string& trafficType, int action, string& runway, double heading);
void addParking(FGParking* park);
/**
* retrieve an available parking by GateID, or -1 if no suitable
* parking location could be found.
*/
int getAvailableParking(double radius, const std::string& fltype,
ParkingAssignment getAvailableParking(double radius, const std::string& fltype,
const std::string& acType, const std::string& airline);
FGParking *getParking(int i);
void releaseParking(int id);
std::string getParkingName(int i);
int getNrOfParkings() {
return parkings.size();
};
void setParkingAvailable(PositionedID guid, bool available);
bool isParkingAvailable(PositionedID parking) const;
FGParking *getParking(PositionedID i) const;
void releaseParking(PositionedID id);
std::string getParkingName(PositionedID i) const;
/**
* Find a parking gate index by name. Note names are often not unique
* in our data, so will return the first match.
* in our data, so will return the first match. If the parking is found,
* it will be marked as in-use (unavailable)
*/
int findParkingByName(const std::string& name) const;
ParkingAssignment getParkingByName(const std::string& name) const;
// ATC related functions.
FGStartupController *getStartupController() {

View file

@ -4,16 +4,18 @@
#include "groundnetwork.hxx"
#include <Navaids/NavDataCache.hxx>
#include <Main/globals.hxx>
#include <Scenery/scenery.hxx>
using namespace flightgear;
/**************************************************************************
* FGTaxiNode
*************************************************************************/
FGTaxiNode::FGTaxiNode(PositionedID aGuid, int index, const SGGeod& pos, bool aOnRunway, int aHoldType) :
FGTaxiNode::FGTaxiNode(PositionedID aGuid, const SGGeod& pos, bool aOnRunway, int aHoldType) :
FGPositioned(aGuid, FGPositioned::PARKING, "", pos),
index(index),
isOnRunway(aOnRunway),
holdType(aHoldType)
{
@ -29,35 +31,24 @@ void FGTaxiNode::setElevation(double val)
// ignored for the moment
}
double FGTaxiNode::getElevationFt(double refelev)
double FGTaxiNode::getElevationFt()
{
#if 0
double elevF = elevation();
double elevationEnd = 0;
if ((elevF == 0) || (elevF == refelev)) {
SGGeod center2 = mPosition;
FGScenery * local_scenery = globals->get_scenery();
center2.setElevationM(SG_MAX_ELEVATION_M);
if (local_scenery->get_elevation_m( center2, elevationEnd, NULL )) {
geod.setElevationM(elevationEnd);
}
}
#endif
return mPosition.getElevationFt();
}
double FGTaxiNode::getElevationM(double refelev)
{
return geod().getElevationM();
}
FGTaxiSegment* FGTaxiNode::getArcTo(FGTaxiNode* aEnd) const
{
BOOST_FOREACH(FGTaxiSegment* arc, next) {
if (arc->getEnd() == aEnd) {
return arc;
if (mPosition.getElevationFt() == 0.0) {
SGGeod center2 = mPosition;
FGScenery* local_scenery = globals->get_scenery();
center2.setElevationM(SG_MAX_ELEVATION_M);
double elevationEnd = -100;
if (local_scenery->get_elevation_m( center2, elevationEnd, NULL )) {
mPosition.setElevationM(elevationEnd);
NavDataCache::instance()->updatePosition(guid(), mPosition);
}
}
return NULL;
return mPosition.getElevationFt();
}
double FGTaxiNode::getElevationM()
{
return getElevationFt() * SG_FEET_TO_METER;
}

View file

@ -16,70 +16,29 @@
#ifndef _GN_NODE_HXX_
#define _GN_NODE_HXX_
#include <vector>
#include <string>
#include <simgear/compiler.h>
#include <simgear/structure/SGSharedPtr.hxx>
#include <Navaids/positioned.hxx>
class FGTaxiSegment;
typedef std::vector<FGTaxiSegment*> FGTaxiSegmentVector;
typedef FGTaxiSegmentVector::iterator FGTaxiSegmentVectorIterator;
bool sortByHeadingDiff(FGTaxiSegment *a, FGTaxiSegment *b);
bool sortByLength (FGTaxiSegment *a, FGTaxiSegment *b);
class FGTaxiNode : public FGPositioned
{
protected:
int index;
bool isOnRunway;
int holdType;
FGTaxiSegmentVector next; // a vector of pointers to all the segments leaving from this node
// used in way finding - should really move to a dynamic struct
double pathScore;
FGTaxiNode* previousNode;
FGTaxiSegment* previousSeg;
public:
FGTaxiNode(PositionedID aGuid, int index, const SGGeod& pos, bool aOnRunway, int aHoldType);
FGTaxiNode(PositionedID aGuid, const SGGeod& pos, bool aOnRunway, int aHoldType);
virtual ~FGTaxiNode();
void setElevation(double val);
void addSegment(FGTaxiSegment *segment) { next.push_back(segment); };
void setPathScore (double val) { pathScore = val; };
void setPreviousNode(FGTaxiNode *val) { previousNode = val; };
void setPreviousSeg (FGTaxiSegment *val) { previousSeg = val; };
FGTaxiNode *getPreviousNode() { return previousNode; };
FGTaxiSegment *getPreviousSegment() { return previousSeg; };
double getPathScore() { return pathScore; };
double getElevationM (double refelev);
double getElevationFt(double refelev);
double getElevationM ();
double getElevationFt();
int getIndex() const { return index; };
PositionedID getIndex() const { return guid(); };
int getHoldPointType() const { return holdType; };
bool getIsOnRunway() const { return isOnRunway; };
const FGTaxiSegmentVector& arcs() const
{ return next; }
/// find the arg which leads from this node to another.
/// returns NULL if no such arc exists.
FGTaxiSegment* getArcTo(FGTaxiNode* aEnd) const;
bool operator<(const FGTaxiNode &other) const { return index < other.index; };
};
typedef SGSharedPtr<FGTaxiNode> FGTaxiNode_ptr;

View file

@ -27,6 +27,7 @@
#include <math.h>
#include <algorithm>
#include <fstream>
#include <map>
#include <boost/foreach.hpp>
#include <osg/Geode>
@ -40,6 +41,7 @@
#include <simgear/scene/material/mat.hxx>
#include <simgear/scene/util/OsgMath.hxx>
#include <simgear/structure/exception.hxx>
#include <simgear/timing/timestamp.hxx>
#include <Airports/simple.hxx>
#include <Airports/dynamics.hxx>
@ -48,6 +50,7 @@
#include <AIModel/AIAircraft.hxx>
#include <AIModel/performancedata.hxx>
#include <AIModel/AIFlightPlan.hxx>
#include <Navaids/NavDataCache.hxx>
#include <ATC/atc_mgr.hxx>
@ -56,52 +59,50 @@
#include "groundnetwork.hxx"
using std::string;
using flightgear::NavDataCache;
/***************************************************************************
* FGTaxiSegment
**************************************************************************/
FGTaxiSegment::FGTaxiSegment(int aStart, int aEnd, bool isPushBack) :
FGTaxiSegment::FGTaxiSegment(PositionedID aStart, PositionedID aEnd) :
startNode(aStart),
endNode(aEnd),
length(0),
heading(0),
isActive(0),
isPushBackRoute(isPushBack),
start(0),
end(0),
index(0),
oppositeDirection(0)
{
};
bool FGTaxiSegment::bindToNodes(const IndexTaxiNodeMap& nodes)
{
IndexTaxiNodeMap::const_iterator it = nodes.find(startNode);
if (it == nodes.end()) {
return false;
}
start = it->second;
it = nodes.find(endNode);
if (it == nodes.end()) {
return false;
}
end = it->second;
start->addSegment(this);
double az2;
SGGeodesy::inverse(start->geod(), end->geod(), heading, az2, length);
return true;
}
SGGeod FGTaxiSegment::getCenter() const
{
FGTaxiNode* start(getStart()), *end(getEnd());
double heading, length, az2;
SGGeodesy::inverse(start->geod(), end->geod(), heading, az2, length);
return SGGeodesy::direct(start->geod(), heading, length * 0.5);
}
FGTaxiNode* FGTaxiSegment::getEnd() const
{
return static_cast<FGTaxiNode*>(NavDataCache::instance()->loadById(endNode));
}
FGTaxiNode* FGTaxiSegment::getStart() const
{
return static_cast<FGTaxiNode*>(NavDataCache::instance()->loadById(startNode));
}
double FGTaxiSegment::getLength() const
{
return dist(getStart()->cart(), getEnd()->cart());
}
double FGTaxiSegment::getHeading() const
{
return SGGeodesy::courseDeg(getStart()->geod(), getEnd()->geod());
}
void FGTaxiSegment::block(int id, time_t blockTime, time_t now)
{
BlockListIterator i = blockTimes.begin();
@ -144,81 +145,17 @@ void FGTaxiSegment::unblock(time_t now)
/***************************************************************************
* FGTaxiRoute
**************************************************************************/
bool FGTaxiRoute::next(int *nde)
bool FGTaxiRoute::next(PositionedID *nde)
{
//for (intVecIterator i = nodes.begin(); i != nodes.end(); i++)
// cerr << "FGTaxiRoute contains : " << *(i) << endl;
//cerr << "Offset from end: " << nodes.end() - currNode << endl;
//if (currNode != nodes.end())
// cerr << "true" << endl;
//else
// cerr << "false" << endl;
//if (nodes.size() != (routes.size()) +1)
// cerr << "ALERT: Misconfigured TaxiRoute : " << nodes.size() << " " << routes.size() << endl;
if (currNode == nodes.end())
return false;
*nde = *(currNode);
if (currNode != nodes.begin()) // make sure route corresponds to the end node
currRoute++;
currNode++;
return true;
};
bool FGTaxiRoute::next(int *nde, int *rte)
{
//for (intVecIterator i = nodes.begin(); i != nodes.end(); i++)
// cerr << "FGTaxiRoute contains : " << *(i) << endl;
//cerr << "Offset from end: " << nodes.end() - currNode << endl;
//if (currNode != nodes.end())
// cerr << "true" << endl;
//else
// cerr << "false" << endl;
if (nodes.size() != (routes.size()) + 1) {
SG_LOG(SG_GENERAL, SG_ALERT,
"ALERT: Misconfigured TaxiRoute : " << nodes.
size() << " " << routes.size());
throw sg_range_exception("misconfigured taxi route");
}
if (currNode == nodes.end())
return false;
*nde = *(currNode);
//*rte = *(currRoute);
if (currNode != nodes.begin()) // Make sure route corresponds to the end node
{
*rte = *(currRoute);
currRoute++;
} else {
// If currNode points to the first node, this means the aircraft is not on the taxi node
// yet. Make sure to return a unique identifyer in this situation though, because otherwise
// the speed adjust AI code may be unable to resolve whether two aircraft are on the same
// taxi route or not. the negative of the preceding route seems a logical choice, as it is
// unique for any starting location.
// Note that this is probably just a temporary fix until I get Parking / tower control working.
*rte = -1 * *(currRoute);
}
currNode++;
return true;
};
void FGTaxiRoute::rewind(int route)
{
int currPoint;
int currRoute;
first();
do {
if (!(next(&currPoint, &currRoute))) {
SG_LOG(SG_GENERAL, SG_ALERT,
"Error in rewinding TaxiRoute: current" << currRoute <<
" goal " << route);
}
} while (currRoute != route);
}
/***************************************************************************
* FGGroundNetwork()
**************************************************************************/
@ -232,7 +169,6 @@ FGGroundNetwork::FGGroundNetwork() :
parent(NULL)
{
hasNetwork = false;
foundRoute = false;
totalDistance = 0;
maxDistance = 0;
//maxDepth = 1000;
@ -253,52 +189,16 @@ FGGroundNetwork::~FGGroundNetwork()
// When I fix FGPositioned lifetimes (unloading-at-runtime support), this
// will need to be re-visited so it can run safely during shutdown.
#if 0
//cerr << "Running Groundnetwork Destructor " << endl;
bool saveData = false;
ofstream cachefile;
if (fgGetBool("/sim/ai/groundnet-cache")) {
SGPath cacheData(globals->get_fg_home());
cacheData.append("ai");
string airport = parent->getId();
if ((airport) != "") {
char buffer[128];
::snprintf(buffer, 128, "%c/%c/%c/",
airport[0], airport[1], airport[2]);
cacheData.append(buffer);
if (!cacheData.exists()) {
cacheData.create_dir(0777);
}
cacheData.append(airport + "-groundnet-cache.txt");
cachefile.open(cacheData.str().c_str());
saveData = true;
}
}
cachefile << "[GroundNetcachedata:ref:2011:09:04]" << endl;
for (FGTaxiNodeVectorIterator node = nodes.begin();
node != nodes.end(); node++) {
if (saveData) {
cachefile << (*node)->getIndex () << " "
<< (*node)->getElevationM (parent->getElevation()*SG_FEET_TO_METER) << " "
<< endl;
}
delete(*node);
}
nodes.clear();
pushBackNodes.clear();
for (FGTaxiSegmentVectorIterator seg = segments.begin();
seg != segments.end(); seg++) {
delete(*seg);
}
segments.clear();
if (saveData) {
cachefile.close();
}
saveElevationCache();
#endif
BOOST_FOREACH(FGTaxiSegment* seg, segments) {
delete seg;
}
}
void FGGroundNetwork::saveElevationCache() {
//cerr << "Running Groundnetwork Destructor " << endl;
void FGGroundNetwork::saveElevationCache()
{
#if 0
bool saveData = false;
ofstream cachefile;
if (fgGetBool("/sim/ai/groundnet-cache")) {
@ -331,59 +231,38 @@ void FGGroundNetwork::saveElevationCache() {
if (saveData) {
cachefile.close();
}
#endif
}
void FGGroundNetwork::addSegment(FGTaxiSegment* seg)
{
segments.push_back(seg);
}
void FGGroundNetwork::addNode(FGTaxiNode* node)
{
assert(node);
IndexTaxiNodeMap::iterator it = nodes.find(node->getIndex());
if (it != nodes.end()) {
throw sg_range_exception();
}
nodes.insert(it, std::make_pair(node->getIndex(), node));
}
void FGGroundNetwork::addNodes(FGParkingVec * parkings)
{
BOOST_FOREACH(FGParking* parking, *parkings) {
addNode(parking);
}
}
void FGGroundNetwork::init()
void FGGroundNetwork::init(FGAirport* pr)
{
if (networkInitialized) {
FGATCController::init();
//cerr << "FGground network already initialized" << endl;
return;
}
parent = pr;
assert(parent);
hasNetwork = true;
nextSave = 0;
int index = 1;
// bind segments to nodes
loadSegments();
// establish pairing of segments
BOOST_FOREACH(FGTaxiSegment* segment, segments) {
if (!segment->bindToNodes(nodes)) {
SG_LOG(SG_GENERAL, SG_ALERT, "unable to bind taxiway segment");
segment->setIndex(index++);
if (segment->oppositeDirection) {
continue; // already establish
}
segment->setIndex(index++);
if (segment->isPushBack()) {
pushBackNodes.push_back(segment->getEnd());
}
}
// establish pairing of segments
BOOST_FOREACH(FGTaxiSegment* segment, segments) {
FGTaxiSegment* opp = segment->getEnd()->getArcTo(segment->getStart());
FGTaxiSegment* opp = findSegment(segment->endNode, segment->startNode);
if (opp) {
segment->setOpposite(opp);
assert(opp->oppositeDirection == NULL);
segment->oppositeDirection = opp;
opp->oppositeDirection = segment;
}
}
@ -394,6 +273,18 @@ void FGGroundNetwork::init()
networkInitialized = true;
}
void FGGroundNetwork::loadSegments()
{
flightgear::NavDataCache* cache = flightgear::NavDataCache::instance();
// iterate over all ground-net nodes in this airport
BOOST_FOREACH(PositionedID node, cache->groundNetNodes(parent->guid(), false)) {
// find all segments leaving the node
BOOST_FOREACH(PositionedID end, cache->groundNetEdgesFrom(node, false)) {
segments.push_back(new FGTaxiSegment(node, end));
}
}
}
void FGGroundNetwork::parseCache()
{
SGPath cacheData(globals->get_fg_home());
@ -403,7 +294,7 @@ void FGGroundNetwork::parseCache()
if (airport.empty()) {
return;
}
#if 0
char buffer[128];
::snprintf(buffer, 128, "%c/%c/%c/",
airport[0], airport[1], airport[2]);
@ -437,76 +328,29 @@ void FGGroundNetwork::parseCache()
}
}
}
#endif
}
int FGGroundNetwork::findNearestNode(const SGGeod & aGeod)
int FGGroundNetwork::findNearestNode(const SGGeod & aGeod) const
{
double minDist = HUGE_VAL;
int index = -1;
IndexTaxiNodeMap::iterator i;
for (i = nodes.begin(); i != nodes.end(); i++) {
double d = SGGeodesy::distanceM(aGeod, i->second->geod());
if (d < minDist) {
minDist = d;
index = i->first;
}
}
return index;
const bool onRunway = false;
return NavDataCache::instance()->findGroundNetNode(parent->guid(), aGeod, onRunway);
}
int FGGroundNetwork::findNearestNodeOnRunway(const SGGeod & aGeod, FGRunway* aRunway)
int FGGroundNetwork::findNearestNodeOnRunway(const SGGeod & aGeod, FGRunway* aRunway) const
{
double minDist = HUGE_VAL;
int index = -1;
IndexTaxiNodeMap::iterator i;
for (i = nodes.begin(); i != nodes.end(); i++) {
if (!i->second->getIsOnRunway()) {
continue;
}
// check point lies on the runway - i.e that course from aGeod to the
// runway end, matches the runway heading
if (aRunway) {
double course = SGGeodesy::courseDeg(i->second->geod(), aRunway->end());
double headingDiff = course - aRunway->headingDeg();
SG_NORMALIZE_RANGE(headingDiff, -180.0, 180.0);
if (fabs(headingDiff) > 3.0) { // 3 degrees tolerance
continue;
}
}
double d = SGGeodesy::distanceM(aGeod, i->second->geod());
if (d < minDist) {
minDist = d;
index = i->first;
}
}
return index;
const bool onRunway = true;
return NavDataCache::instance()->findGroundNetNode(parent->guid(), aGeod, onRunway, aRunway);
}
FGTaxiNode* FGGroundNetwork::findNode(unsigned int idx)
{
IndexTaxiNodeMap::iterator i = nodes.find(idx);
if (i == nodes.end()) {
return NULL;
}
return i->second;
FGTaxiNode* FGGroundNetwork::findNode(PositionedID idx) const
{
return static_cast<FGTaxiNode*>(NavDataCache::instance()->loadById(idx));
}
FGTaxiSegment *FGGroundNetwork::findSegment(unsigned idx)
{ /*
for (FGTaxiSegmentVectorIterator
itr = segments.begin();
itr != segments.end(); itr++)
{
if (itr->getIndex() == idx)
return itr->getAddress();
}
*/
FGTaxiSegment *FGGroundNetwork::findSegment(unsigned idx) const
{
if ((idx > 0) && (idx <= segments.size()))
return segments[idx - 1];
else {
@ -515,34 +359,57 @@ FGTaxiSegment *FGGroundNetwork::findSegment(unsigned idx)
}
}
FGTaxiSegment* FGGroundNetwork::findSegment(PositionedID from, PositionedID to) const
{
if (from == 0) {
return NULL;
}
// completely boring linear search of segments. Can be improved if/when
// this ever becomes a hot-spot
BOOST_FOREACH(FGTaxiSegment* seg, segments) {
if (seg->startNode != from) {
continue;
}
if ((to == 0) || (seg->endNode == to)) {
return seg;
}
}
return NULL; // not found
}
FGTaxiRoute FGGroundNetwork::findShortestRoute(int start, int end,
static int edgePenalty(FGTaxiNode* tn)
{
return (tn->type() == FGPositioned::PARKING ? 10000 : 0) +
(tn->getIsOnRunway() ? 1000 : 0);
}
class ShortestPathData
{
public:
ShortestPathData() :
score(HUGE_VAL)
{}
double score;
FGTaxiNode_ptr previousNode;
};
FGTaxiRoute FGGroundNetwork::findShortestRoute(PositionedID start, PositionedID end,
bool fullSearch)
{
//implements Dijkstra's algorithm to find shortest distance route from start to end
//taken from http://en.wikipedia.org/wiki/Dijkstra's_algorithm
//double INFINITE = 100000000000.0;
// initialize scoring values
int nParkings = parent->getDynamics()->getNrOfParkings();
FGTaxiNodeVector unvisited;
flightgear::NavDataCache* cache = flightgear::NavDataCache::instance();
std::map<FGTaxiNode*, ShortestPathData> searchData;
if (fullSearch) {
// create vector from map values
IndexTaxiNodeMap::iterator i;
for (i = nodes.begin(); i != nodes.end(); i++) {
unvisited.push_back(i->second);
}
} else {
unvisited = pushBackNodes;
BOOST_FOREACH(PositionedID n, cache->groundNetNodes(parent->guid(), !fullSearch)) {
unvisited.push_back(findNode(n));
}
BOOST_FOREACH(FGTaxiNode* node, unvisited) {
node->setPathScore(HUGE_VAL); //infinity by all practical means
node->setPreviousNode(0); //
node->setPreviousSeg(0); //
}
FGTaxiNode *firstNode = findNode(start);
if (!firstNode)
{
@ -551,7 +418,7 @@ FGTaxiRoute FGGroundNetwork::findShortestRoute(int start, int end,
<< " at " << ((parent) ? parent->getId() : "<unknown>"));
return FGTaxiRoute();
}
firstNode->setPathScore(0);
searchData[firstNode].score = 0.0;
FGTaxiNode *lastNode = findNode(end);
if (!lastNode)
@ -565,11 +432,11 @@ FGTaxiRoute FGGroundNetwork::findShortestRoute(int start, int end,
while (!unvisited.empty()) {
FGTaxiNode *best = unvisited.front();
BOOST_FOREACH(FGTaxiNode* i, unvisited) {
if (i->getPathScore() < best->getPathScore()) {
if (searchData[i].score < searchData[best].score) {
best = i;
}
}
// remove 'best' from the unvisited set
FGTaxiNodeVectorIterator newend =
remove(unvisited.begin(), unvisited.end(), best);
@ -579,59 +446,38 @@ FGTaxiRoute FGGroundNetwork::findShortestRoute(int start, int end,
break;
}
BOOST_FOREACH(FGTaxiSegment* seg, best->arcs()) {
if (!fullSearch && !seg->isPushBack()) {
continue; // inelligible!
}
FGTaxiNode *tgt = seg->getEnd();
double alt = best->getPathScore() + seg->getLength() +
seg->getPenalty(nParkings);
if (alt < tgt->getPathScore()) { // Relax (u,v)
tgt->setPathScore(alt);
tgt->setPreviousNode(best);
tgt->setPreviousSeg(seg);
BOOST_FOREACH(PositionedID targetId, cache->groundNetEdgesFrom(best->guid(), !fullSearch)) {
FGTaxiNode* tgt = (FGTaxiNode*) cache->loadById(targetId);
double edgeLength = dist(best->cart(), tgt->cart());
double alt = searchData[best].score + edgeLength + edgePenalty(tgt);
if (alt < searchData[tgt].score) { // Relax (u,v)
searchData[tgt].score = alt;
searchData[tgt].previousNode = best;
}
} // of outgoing arcs/segments from current best node iteration
} // of unvisited nodes remaining
if (lastNode->getPathScore() == HUGE_VAL) {
if (searchData[lastNode].score == HUGE_VAL) {
// no valid route found
if (fullSearch) {
SG_LOG(SG_GENERAL, SG_ALERT,
"Failed to find route from waypoint " << start << " to "
<< end << " at " << parent->getId());
}
FGTaxiRoute empty;
return empty;
//exit(1); //TODO exit more gracefully, no need to stall the whole sim with broken GN's
} else {
// assemble route from backtrace information
intVec nodes, routes;
FGTaxiNode *bt = lastNode;
while (bt->getPreviousNode() != 0) {
nodes.push_back(bt->getIndex());
routes.push_back(bt->getPreviousSegment()->getIndex());
bt = bt->getPreviousNode();
}
nodes.push_back(start);
reverse(nodes.begin(), nodes.end());
reverse(routes.begin(), routes.end());
return FGTaxiRoute(nodes, routes, lastNode->getPathScore(), 0);
return FGTaxiRoute();
}
}
int FGTaxiSegment::getPenalty(int nGates)
{
int penalty = 0;
if (end->getIndex() < nGates) {
penalty += 10000;
// assemble route from backtrace information
PositionedIDVec nodes;
FGTaxiNode *bt = lastNode;
while (searchData[bt].previousNode != 0) {
nodes.push_back(bt->guid());
bt = searchData[bt].previousNode;
}
if (end->getIsOnRunway()) { // For now. In future versions, need to find out whether runway is active.
penalty += 1000;
}
return penalty;
nodes.push_back(start);
reverse(nodes.begin(), nodes.end());
return FGTaxiRoute(nodes, searchData[lastNode].score, 0);
}
/* ATC Related Functions */
@ -644,7 +490,8 @@ void FGGroundNetwork::announcePosition(int id,
double radius, int leg,
FGAIAircraft * aircraft)
{
init();
assert(parent);
TrafficVectorIterator i = activeTraffic.begin();
// Search search if the current id alread has an entry
// This might be faster using a map instead of a vector, but let's start by taking a safe route
@ -1345,7 +1192,7 @@ void FGGroundNetwork::render(bool visible)
} else {
elevationStart = ((i)->getAircraft()->_getAltitude());
}
double elevationEnd = segments[pos]->getEnd()->getElevationM(parent->getElevation()*SG_FEET_TO_METER);
double elevationEnd = segments[pos]->getEnd()->getElevationM();
//cerr << "Using elevation " << elevationEnd << endl;
if ((elevationEnd == 0) || (elevationEnd = parent->getElevation())) {
@ -1406,8 +1253,8 @@ void FGGroundNetwork::render(bool visible)
obj_trans->setDataVariance(osg::Object::STATIC);
// Experimental: Calculate slope here, based on length, and the individual elevations
double elevationStart = segments[k]->getStart()->getElevationM(parent->getElevation()*SG_FEET_TO_METER);
double elevationEnd = segments[k]->getEnd ()->getElevationM(parent->getElevation()*SG_FEET_TO_METER);
double elevationStart = segments[k]->getStart()->getElevationM();
double elevationEnd = segments[k]->getEnd ()->getElevationM();
if ((elevationStart == 0) || (elevationStart == parent->getElevation())) {
SGGeod center2 = segments[k]->getStart()->geod();
center2.setElevationM(SG_MAX_ELEVATION_M);

View file

@ -72,57 +72,40 @@ typedef BlockList::iterator BlockListIterator;
class FGTaxiSegment
{
private:
int startNode;
int endNode;
double length;
double heading;
const PositionedID startNode;
const PositionedID endNode;
bool isActive;
bool isPushBackRoute;
BlockList blockTimes;
FGTaxiNode *start;
FGTaxiNode *end;
int index;
FGTaxiSegment *oppositeDirection;
friend class FGGroundNetwork;
public:
FGTaxiSegment(int start, int end, bool isPushBack);
FGTaxiSegment(PositionedID start, PositionedID end);
void setIndex (int val) {
index = val;
};
void setOpposite(FGTaxiSegment *opp) {
oppositeDirection = opp;
};
bool bindToNodes(const IndexTaxiNodeMap& nodes);
void setDimensions(double elevation);
void block(int id, time_t blockTime, time_t now);
void unblock(time_t now);
bool hasBlock(time_t now);
FGTaxiNode * getEnd() {
return end;
};
FGTaxiNode * getStart() {
return start;
};
double getLength() {
return length;
};
int getIndex() {
return index;
};
FGTaxiNode * getEnd() const;
FGTaxiNode * getStart() const;
double getLength() const;
// compute the center of the arc
SGGeod getCenter() const;
double getHeading() {
return heading;
};
bool isPushBack() {
return isPushBackRoute;
double getHeading() const;
int getIndex() {
return index;
};
int getPenalty(int nGates);
@ -130,7 +113,7 @@ public:
bool operator<(const FGTaxiSegment &other) const {
return index < other.index;
};
//bool hasSmallerHeadingDiff (const FGTaxiSegment &other) const { return headingDiff < other.headingDiff; };
FGTaxiSegment *opposite() {
return oppositeDirection;
};
@ -150,60 +133,45 @@ typedef std::vector<int>::iterator intVecIterator;
class FGTaxiRoute
{
private:
intVec nodes;
intVec routes;
PositionedIDVec nodes;
double distance;
// int depth;
intVecIterator currNode;
intVecIterator currRoute;
PositionedIDVec::iterator currNode;
public:
FGTaxiRoute() {
distance = 0;
currNode = nodes.begin();
currRoute = routes.begin();
};
FGTaxiRoute(intVec nds, intVec rts, double dist, int dpth) {
FGTaxiRoute(const PositionedIDVec& nds, double dist, int dpth) {
nodes = nds;
routes = rts;
distance = dist;
currNode = nodes.begin();
currRoute = routes.begin();
// depth = dpth;
};
FGTaxiRoute& operator= (const FGTaxiRoute &other) {
nodes = other.nodes;
routes = other.routes;
distance = other.distance;
// depth = other.depth;
currNode = nodes.begin();
currRoute = routes.begin();
return *this;
};
FGTaxiRoute(const FGTaxiRoute& copy) :
nodes(copy.nodes),
routes(copy.routes),
distance(copy.distance),
// depth(copy.depth),
currNode(nodes.begin()),
currRoute(routes.begin())
currNode(nodes.begin())
{};
bool operator< (const FGTaxiRoute &other) const {
return distance < other.distance;
};
bool empty () {
return nodes.begin() == nodes.end();
return nodes.empty();
};
bool next(int *nde);
bool next(int *nde, int *rte);
void rewind(int legNr);
bool next(PositionedID *nde);
void first() {
currNode = nodes.begin();
currRoute = routes.begin();
};
int size() {
return nodes.size();
@ -211,8 +179,6 @@ public:
int nodesLeft() {
return nodes.end() - currNode;
};
// int getDepth() { return depth; };
};
typedef std::vector<FGTaxiRoute> TaxiRouteVector;
@ -231,12 +197,8 @@ private:
int count;
int version;
IndexTaxiNodeMap nodes;
FGTaxiNodeVector pushBackNodes;
FGTaxiSegmentVector segments;
TaxiRouteVector routes;
TrafficVector activeTraffic;
TrafficVectorIterator currTraffic;
@ -255,18 +217,16 @@ private:
void parseCache();
void loadSegments();
public:
FGGroundNetwork();
~FGGroundNetwork();
void addNode (FGTaxiNode* node);
void addNodes (FGParkingVec *parkings);
void addSegment(FGTaxiSegment* seg);
void setVersion (int v) { version = v;};
void setVersion (int v) { version = v;};
int getVersion() { return version; };
void init();
void init(FGAirport* pr);
bool exists() {
return hasNetwork;
};
@ -274,21 +234,21 @@ public:
towerController = twrCtrlr;
};
int findNearestNode(const SGGeod& aGeod);
int findNearestNodeOnRunway(const SGGeod& aGeod, FGRunway* aRunway = NULL);
int findNearestNode(const SGGeod& aGeod) const;
int findNearestNodeOnRunway(const SGGeod& aGeod, FGRunway* aRunway = NULL) const;
FGTaxiNode *findNode(unsigned idx);
FGTaxiSegment *findSegment(unsigned idx);
FGTaxiRoute findShortestRoute(int start, int end, bool fullSearch=true);
//void trace(FGTaxiNode *, int, int, double dist);
int getNrOfNodes() {
return nodes.size();
};
void setParent(FGAirport *par) {
parent = par;
};
FGTaxiNode *findNode(PositionedID idx) const;
FGTaxiSegment *findSegment(unsigned idx) const;
/**
* Find the taxiway segment joining two (ground-net) nodes. Returns
* NULL if no such segment exists.
* It is permitted to pass 0 for the 'to' ID, indicating that any
* segment originating at 'from' is acceptable.
*/
FGTaxiSegment* findSegment(PositionedID from, PositionedID to) const;
FGTaxiRoute findShortestRoute(PositionedID start, PositionedID end, bool fullSearch=true);
virtual void announcePosition(int id, FGAIFlightPlan *intendedRoute, int currentRoute,
double lat, double lon, double hdg, double spd, double alt,

View file

@ -31,24 +31,23 @@
#include <string>
#include "parking.hxx"
#include "groundnetwork.hxx"
/*********************************************************************************
* FGParking
********************************************************************************/
FGParking::FGParking(PositionedID aGuid, int index, const SGGeod& pos,
FGParking::FGParking(PositionedID aGuid, const SGGeod& pos,
double aHeading, double aRadius,
const std::string& name, const std::string& aType,
const std::string& codes) :
FGTaxiNode(aGuid, index, pos, false, 0),
const std::string& codes,
PositionedID pushBackNode) :
FGTaxiNode(aGuid, pos, false, 0),
heading(aHeading),
radius(aRadius),
parkingName(name),
type(aType),
airlineCodes(codes),
available(true),
pushBackPoint(0)
pushBackPoint(pushBackNode)
{
}

View file

@ -38,42 +38,33 @@
#include "gnnode.hxx"
class FGTaxiRoute;
class FGParking : public FGTaxiNode
{
private:
double heading;
double radius;
std::string parkingName;
std::string type;
std::string airlineCodes;
bool available;
int pushBackPoint;
std::auto_ptr<FGTaxiRoute> pushBackRoute;
const double heading;
const double radius;
const std::string parkingName;
const std::string type;
const std::string airlineCodes;
const PositionedID pushBackPoint;
SG_DISABLE_COPY(FGParking);
public:
FGParking(PositionedID aGuid, int index, const SGGeod& pos,
FGParking(PositionedID aGuid, const SGGeod& pos,
double heading, double radius,
const std::string& name, const std::string& type,
const std::string& codes);
const std::string& codes,
PositionedID pushBackNode);
virtual ~FGParking();
#if 0
void setHeading (double hdg) { heading = hdg; };
void setRadius (double rad) { radius = rad; };
void setName (const std::string& name) { parkingName = name; };
void setType (const std::string& tpe) { type = tpe; };
void setCodes (const std::string& codes){ airlineCodes= codes;};
void setPushBackRoute(std::auto_ptr<FGTaxiRoute> val) { pushBackRoute = val; };
void setPushBackPoint(int val) { pushBackPoint = val; };
bool isAvailable () const { return available;};
void setAvailable(bool val) { available = val; };
#endif
double getHeading () const { return heading; };
double getRadius () const { return radius; };
@ -82,8 +73,6 @@ public:
std::string getCodes () const { return airlineCodes;};
std::string getName () const { return parkingName; };
FGTaxiRoute * getPushBackRoute () { return pushBackRoute.get(); };
int getPushBackPoint () { return pushBackPoint; };
bool operator< (const FGParking &other) const {

View file

@ -112,7 +112,8 @@ FGAirportDynamics * FGAirport::getDynamics()
_dynamics = new FGAirportDynamics(this);
XMLLoader::load(_dynamics);
_dynamics->init();
FGRunwayPreference rwyPrefs(this);
XMLLoader::load(&rwyPrefs);
_dynamics->setRwyUse(rwyPrefs);

View file

@ -20,6 +20,7 @@
#include <simgear/misc/sg_path.hxx>
#include <simgear/xml/easyxml.hxx>
#include <simgear/misc/strutils.hxx>
#include <simgear/timing/timestamp.hxx>
#include <Main/globals.hxx>
#include <Main/fg_props.hxx>
@ -32,6 +33,8 @@
#include "simple.hxx"
#include "runwayprefs.hxx"
#include <Navaids/NavDataCache.hxx>
using std::string;
XMLLoader::XMLLoader() {}
@ -39,10 +42,32 @@ XMLLoader::~XMLLoader() {}
void XMLLoader::load(FGAirportDynamics* d)
{
FGAirportDynamicsXMLLoader visitor(d);
if(loadAirportXMLDataIntoVisitor(d->parent()->ident(), "groundnet", visitor)) {
d->init();
SGPath path;
if (!findAirportData(d->parent()->ident(), "groundnet", path)) {
return;
}
flightgear::NavDataCache* cache = flightgear::NavDataCache::instance();
if (!cache->isCachedFileModified(path)) {
return;
}
SG_LOG(SG_GENERAL, SG_INFO, "reading groundnet data from " << path);
SGTimeStamp t;
try {
cache->beginTransaction();
t.stamp();
{
FGAirportDynamicsXMLLoader visitor(d);
readXML(path.str(), visitor);
} // ensure visitor is destroyed so its destructor runs
cache->stampCacheFile(path);
cache->commitTransaction();
} catch (sg_exception& e) {
cache->abortTransaction();
}
SG_LOG(SG_GENERAL, SG_INFO, "parsing XML took " << t.elapsedMSec());
}
void XMLLoader::load(FGRunwayPreference* p) {

View file

@ -169,7 +169,7 @@ static bool fgSetPosFromAirportIDandParkpos( const string& id, const string& par
return false;
}
int gateID;
ParkingAssignment pka;
double radius = fgGetDouble("/sim/dimensions/radius-m");
if ((parkpos == string("AVAILABLE")) && (radius > 0)) {
string fltType;
@ -203,27 +203,25 @@ static bool fgSetPosFromAirportIDandParkpos( const string& id, const string& par
}
string acType; // Currently not used by findAvailable parking, so safe to leave empty.
gateID = dcs->getAvailableParking(radius, fltType, acType, acOperator);
if (gateID >=0 ) {
pka = dcs->getAvailableParking(radius, fltType, acType, acOperator);
if (pka.isValid()) {
fgGetString("/sim/presets/parkpos");
fgSetString("/sim/presets/parkpos", dcs->getParking(gateID)->getName());
fgSetString("/sim/presets/parkpos", pka.parking()->getName());
} else {
SG_LOG( SG_GENERAL, SG_ALERT,
"Failed to find a suitable parking at airport " << id );
return false;
}
} else {
gateID = dcs->findParkingByName(parkpos);
if (gateID < 0) {
pka = dcs->getParkingByName(parkpos);
if (!pka.isValid()) {
SG_LOG( SG_GENERAL, SG_ALERT,
"Failed to find a parking at airport " << id << ":" << parkpos);
return false;
}
}
FGParking* parking = dcs->getParking(gateID);
parking->setAvailable(false);
fgApplyStartOffset(parking->geod(), parking->getHeading());
fgApplyStartOffset(pka.parking()->geod(), pka.parking()->getHeading());
return true;
}

View file

@ -60,6 +60,8 @@
#include "PositionedOctree.hxx"
#include <Airports/apt_loader.hxx>
#include <Navaids/airways.hxx>
#include <Airports/parking.hxx>
#include <Airports/gnnode.hxx>
using std::string;
@ -68,7 +70,7 @@ using std::string;
namespace {
const int SCHEMA_VERSION = 4;
const int SCHEMA_VERSION = 5;
// bind a std::string to a sqlite statement. The std::string must live the
// entire duration of the statement execution - do not pass a temporary
@ -461,6 +463,29 @@ public:
")");
runSQL("CREATE INDEX airway_edge_from ON airway_edge(a)");
runSQL("CREATE TABLE taxi_node ("
"hold_type INT,"
"on_runway BOOL,"
"pushback BOOL"
")");
runSQL("CREATE TABLE parking ("
"heading FLOAT,"
"radius INT,"
"gate_type VARCHAR,"
"airlines VARCHAR,"
"pushback INT64"
")");
runSQL("CREATE TABLE groundnet_edge ("
"airport INT64,"
"a INT64,"
"b INT64"
")");
runSQL("CREATE INDEX groundnet_edge_airport ON groundnet_edge(airport)");
runSQL("CREATE INDEX groundnet_edge_from ON groundnet_edge(a)");
}
void prepareQueries()
@ -570,6 +595,7 @@ public:
sqlite3_bind_int(findILS, 4, FGPositioned::ILS);
sqlite3_bind_int(findILS, 5, FGPositioned::LOC);
// airways
findAirway = prepare("SELECT rowid FROM airway WHERE network=?1 AND ident=?2");
insertAirway = prepare("INSERT INTO airway (ident, network) "
"VALUES (?1, ?2)");
@ -580,6 +606,47 @@ public:
isPosInAirway = prepare("SELECT rowid FROM airway_edge WHERE network=?1 AND a=?2");
airwayEdgesFrom = prepare("SELECT airway, b FROM airway_edge WHERE network=?1 AND a=?2");
// parking / taxi-node graph
insertTaxiNode = prepare("INSERT INTO taxi_node (rowid, hold_type, on_runway, pushback) VALUES(?1, ?2, ?3, 0)");
insertParkingPos = prepare("INSERT INTO parking (rowid, heading, radius, gate_type, airlines) "
"VALUES (?1, ?2, ?3, ?4, ?5)");
setParkingPushBack = prepare("UPDATE parking SET pushback=?2 WHERE rowid=?1");
loadTaxiNodeStmt = prepare("SELECT hold_type, on_runway FROM taxi_node WHERE rowid=?1");
loadParkingPos = prepare("SELECT heading, radius, gate_type, airlines, pushback FROM parking WHERE rowid=?1");
taxiEdgesFrom = prepare("SELECT b FROM groundnet_edge WHERE a=?1");
pushbackEdgesFrom = prepare("SELECT b FROM groundnet_edge, taxi_node WHERE "
"a=?1 AND groundnet_edge.b = taxi_node.rowid AND pushback=1");
insertTaxiEdge = prepare("INSERT INTO groundnet_edge (airport, a,b) VALUES(?1, ?2, ?3)");
markTaxiNodeAsPushback = prepare("UPDATE taxi_node SET pushback=1 WHERE rowid=?1");
airportTaxiNodes = prepare("SELECT rowid FROM positioned WHERE (type=?2 OR type=?3) AND airport=?1");
sqlite3_bind_int(airportTaxiNodes, 2, FGPositioned::PARKING);
sqlite3_bind_int(airportTaxiNodes, 3, FGPositioned::TAXI_NODE);
airportPushbackNodes = prepare("SELECT positioned.rowid FROM positioned, taxi_node WHERE "\
"airport=?1 AND positioned.rowid=taxi_node.rowid AND pushback=1 "
"AND (type=?2 OR type=?3)");
sqlite3_bind_int(airportPushbackNodes, 2, FGPositioned::PARKING);
sqlite3_bind_int(airportPushbackNodes, 3, FGPositioned::TAXI_NODE);
findNearestTaxiNode = prepare("SELECT positioned.rowid FROM positioned, taxi_node WHERE "
"positioned.rowid = taxi_node.rowid AND airport=?1 "
"ORDER BY distanceCartSqr(cart_x, cart_y, cart_z, ?2, ?3, ?4) "
"LIMIT 1");
findNearestRunwayTaxiNode = prepare("SELECT positioned.rowid FROM positioned, taxi_node WHERE "
"positioned.rowid = taxi_node.rowid AND airport=?1 "
"AND on_runway=1 "
"ORDER BY distanceCartSqr(cart_x, cart_y, cart_z, ?2, ?3, ?4) ");
findAirportParking = prepare("SELECT positioned.rowid FROM positioned, parking WHERE "
"airport=?1 AND type=?4 AND "
"radius >= ?2 AND gate_type = ?3 AND "
"parking.rowid=positioned.rowid");
sqlite3_bind_int(findAirportParking, 4, FGPositioned::PARKING);
}
void writeIntProperty(const string& key, int value)
@ -678,6 +745,35 @@ public:
return new FGNavRecord(rowId, ty, id, name, pos, freq, rangeNm, mulituse, runway);
}
FGPositioned* loadParking(sqlite3_int64 rowId,
const string& name, const SGGeod& pos,
PositionedID airport)
{
reset(loadParkingPos);
sqlite3_bind_int64(loadParkingPos, 1, rowId);
execSelect1(loadParkingPos);
double heading = sqlite3_column_double(loadParkingPos, 0);
int radius = sqlite3_column_int(loadParkingPos, 1);
string aircraftType((char*) sqlite3_column_text(loadParkingPos, 2));
string airlines((char*) sqlite3_column_text(loadParkingPos, 3));
PositionedID pushBack = sqlite3_column_int64(loadParkingPos, 4);
return new FGParking(rowId, pos, heading, radius, name, aircraftType, airlines, pushBack);
}
FGPositioned* loadTaxiNode(sqlite3_int64 rowId, const SGGeod& pos,
PositionedID airport)
{
reset(loadTaxiNodeStmt);
sqlite3_bind_int64(loadTaxiNodeStmt, 1, rowId);
execSelect1(loadTaxiNodeStmt);
int hold_type = sqlite3_column_int(loadTaxiNodeStmt, 0);
bool onRunway = sqlite3_column_int(loadTaxiNodeStmt, 1);
return new FGTaxiNode(rowId, pos, onRunway, hold_type);
}
PositionedID insertPositioned(FGPositioned::Type ty, const string& ident,
const string& name, const SGGeod& pos, PositionedID apt,
bool spatialIndex)
@ -822,6 +918,12 @@ public:
sqlite3_stmt_ptr findAirway, insertAirwayEdge, isPosInAirway, airwayEdgesFrom,
insertAirway;
// groundnet (parking, taxi node graph)
sqlite3_stmt_ptr loadTaxiNodeStmt, loadParkingPos, insertTaxiNode, insertParkingPos;
sqlite3_stmt_ptr taxiEdgesFrom, pushbackEdgesFrom, insertTaxiEdge, markTaxiNodeAsPushback,
airportTaxiNodes, airportPushbackNodes, findNearestTaxiNode, findAirportParking,
setParkingPushBack, findNearestRunwayTaxiNode;
// since there's many permutations of ident/name queries, we create
// them programtically, but cache the exact query by its raw SQL once
// used.
@ -910,6 +1012,12 @@ FGPositioned* NavDataCache::NavDataCachePrivate::loadFromStmt(sqlite3_stmt_ptr q
case FGPositioned::FREQ_UNICOM:
return loadComm(rowid, ty, ident, name, pos, aptId);
case FGPositioned::TAXI_NODE:
return loadTaxiNode(rowid, pos, aptId);
case FGPositioned::PARKING:
return loadParking(rowid, ident, pos, aptId);
default:
return NULL;
}
@ -1173,6 +1281,20 @@ void NavDataCache::stampCacheFile(const SGPath& path)
d->execInsert(d->stampFileCache);
}
void NavDataCache::beginTransaction()
{
d->runSQL("BEGIN");
}
void NavDataCache::commitTransaction()
{
d->runSQL("COMMIT");
}
void NavDataCache::abortTransaction()
{
d->runSQL("ROLLBACK");
}
FGPositioned* NavDataCache::loadById(PositionedID rowid)
{
@ -1723,5 +1845,128 @@ PositionedID NavDataCache::findNavaidForRunway(PositionedID runway, FGPositioned
return sqlite3_column_int64(d->findNavaidForRunway, 0);
}
PositionedID
NavDataCache::insertParking(const std::string& name, const SGGeod& aPos,
PositionedID aAirport,
double aHeading, int aRadius, const std::string& aAircraftType,
const std::string& aAirlines)
{
sqlite3_int64 rowId = d->insertPositioned(FGPositioned::PARKING, name, "", aPos, aAirport, false);
// we need to insert a row into the taxi_node table, otherwise we can't maintain
// the appropriate pushback flag.
d->reset(d->insertTaxiNode);
sqlite3_bind_int64(d->insertTaxiNode, 1, rowId);
sqlite3_bind_int(d->insertTaxiNode, 2, 0);
sqlite3_bind_int(d->insertTaxiNode, 3, 0);
d->execInsert(d->insertTaxiNode);
d->reset(d->insertParkingPos);
sqlite3_bind_int64(d->insertParkingPos, 1, rowId);
sqlite3_bind_double(d->insertParkingPos, 2, aHeading);
sqlite3_bind_int(d->insertParkingPos, 3, aRadius);
sqlite_bind_stdstring(d->insertParkingPos, 4, aAircraftType);
sqlite_bind_stdstring(d->insertParkingPos, 5, aAirlines);
return d->execInsert(d->insertParkingPos);
}
void NavDataCache::setParkingPushBackRoute(PositionedID parking, PositionedID pushBackNode)
{
d->reset(d->setParkingPushBack);
sqlite3_bind_int64(d->setParkingPushBack, 1, parking);
sqlite3_bind_int64(d->setParkingPushBack, 2, pushBackNode);
d->execUpdate(d->setParkingPushBack);
}
PositionedID
NavDataCache::insertTaxiNode(const SGGeod& aPos, PositionedID aAirport, int aHoldType, bool aOnRunway)
{
sqlite3_int64 rowId = d->insertPositioned(FGPositioned::TAXI_NODE, string(), string(), aPos, aAirport, false);
d->reset(d->insertTaxiNode);
sqlite3_bind_int64(d->insertTaxiNode, 1, rowId);
sqlite3_bind_int(d->insertTaxiNode, 2, aHoldType);
sqlite3_bind_int(d->insertTaxiNode, 3, aOnRunway);
return d->execInsert(d->insertTaxiNode);
}
void NavDataCache::insertGroundnetEdge(PositionedID aAirport, PositionedID from, PositionedID to)
{
d->reset(d->insertTaxiEdge);
sqlite3_bind_int64(d->insertTaxiEdge, 1, aAirport);
sqlite3_bind_int64(d->insertTaxiEdge, 2, from);
sqlite3_bind_int64(d->insertTaxiEdge, 3, to);
d->execInsert(d->insertTaxiEdge);
}
PositionedIDVec NavDataCache::groundNetNodes(PositionedID aAirport, bool onlyPushback)
{
sqlite3_stmt_ptr q = onlyPushback ? d->airportPushbackNodes : d->airportTaxiNodes;
d->reset(q);
sqlite3_bind_int64(q, 1, aAirport);
return d->selectIds(q);
}
void NavDataCache::markGroundnetAsPushback(PositionedID nodeId)
{
d->reset(d->markTaxiNodeAsPushback);
sqlite3_bind_int64(d->markTaxiNodeAsPushback, 1, nodeId);
d->execUpdate(d->markTaxiNodeAsPushback);
}
static double headingDifferenceDeg(double crs1, double crs2)
{
double diff = crs2 - crs1;
SG_NORMALIZE_RANGE(diff, -180.0, 180.0);
return diff;
}
PositionedID NavDataCache::findGroundNetNode(PositionedID airport, const SGGeod& aPos,
bool onRunway, FGRunway* aRunway)
{
sqlite3_stmt_ptr q = onRunway ? d->findNearestRunwayTaxiNode : d->findNearestTaxiNode;
d->reset(q);
sqlite3_bind_int64(q, 1, airport);
SGVec3d cartPos(SGVec3d::fromGeod(aPos));
sqlite3_bind_double(q, 2, cartPos.x());
sqlite3_bind_double(q, 3, cartPos.y());
sqlite3_bind_double(q, 4, cartPos.z());
while (d->execSelect(q)) {
PositionedID id = sqlite3_column_int64(q, 0);
if (!aRunway) {
return id;
}
// ensure found node lies on the runway
FGPositionedRef node = loadById(id);
double course = SGGeodesy::courseDeg(node->geod(), aRunway->end());
if (fabs(headingDifferenceDeg(course, aRunway->headingDeg())) < 3.0 ) {
return id;
}
}
return 0;
}
PositionedIDVec NavDataCache::groundNetEdgesFrom(PositionedID pos, bool onlyPushback)
{
sqlite3_stmt_ptr q = onlyPushback ? d->pushbackEdgesFrom : d->taxiEdgesFrom;
d->reset(q);
sqlite3_bind_int64(q, 1, pos);
return d->selectIds(q);
}
PositionedIDVec NavDataCache::findAirportParking(PositionedID airport, const std::string& flightType,
int radius)
{
d->reset(d->findAirportParking);
sqlite3_bind_int64(d->findAirportParking, 1, airport);
sqlite3_bind_int(d->findAirportParking, 2, radius);
sqlite_bind_stdstring(d->findAirportParking, 3, flightType);
return d->selectIds(d->findAirportParking);
}
} // of namespace flightgear

View file

@ -30,6 +30,7 @@
#include <Navaids/positioned.hxx>
class SGPath;
class FGRunway;
namespace flightgear
{
@ -84,6 +85,16 @@ public:
string_list readStringListProperty(const std::string& key);
void writeStringListProperty(const std::string& key, const string_list& values);
// transaction API wrappers
void beginTransaction();
void commitTransaction();
void abortTransaction();
/**
* retrieve an FGPositioned from the cache.
* This may be trivial if the object is previously loaded, or require actual
* disk IO.
*/
FGPositioned* loadById(PositionedID guid);
PositionedID insertAirport(FGPositioned::Type ty, const std::string& ident,
@ -112,8 +123,25 @@ public:
PositionedID createUserWaypoint(const std::string& ident, const SGGeod& aPos);
PositionedID insertParking(const std::string& name, const SGGeod& aPos,
PositionedID aAirport,
double aHeading, int aRadius, const std::string& aAircraftType,
const std::string& aAirlines);
void setParkingPushBackRoute(PositionedID parking, PositionedID pushBackNode);
PositionedID insertTaxiNode(const SGGeod& aPos, PositionedID aAirport, int aHoldType, bool aOnRunway);
void insertGroundnetEdge(PositionedID aAirport, PositionedID from, PositionedID to);
/// update the metar flag associated with an airport
void setAirportMetar(const std::string& icao, bool hasMetar);
/**
* Modify the position of an existing item.
* Use with care, since loaded instances will not be updated (at present -
* this behaviour could in theorey be improved)
*/
void updatePosition(PositionedID item, const SGGeod &pos);
FGPositioned::List findAllWithIdent(const std::string& ident,
@ -125,15 +153,35 @@ public:
const SGGeod& aPos, FGPositioned::Filter* aFilter);
/**
* Helper to implement the AirportSearch widget. Optimised text search of
* airport names and idents, returning a list suitable for passing directly
* to PLIB.
*/
char** searchAirportNamesAndIdents(const std::string& aFilter);
/**
* Find the closest matching comm-station on a frequency, to a position.
* The filter with be used for both type ranging and to validate the result
* candidates.
*/
FGPositionedRef findCommByFreq(int freqKhz, const SGGeod& pos, FGPositioned::Filter* filt);
/**
* find all items of a specified type (or range of types) at an airport
*/
PositionedIDVec airportItemsOfType(PositionedID apt, FGPositioned::Type ty,
FGPositioned::Type maxTy = FGPositioned::INVALID);
/**
* find the first match item of the specified type and ident, at an airport
*/
PositionedID airportItemWithIdent(PositionedID apt, FGPositioned::Type ty, const std::string& ident);
/**
* Find all navaids matching a particular frequency, sorted by range from the
* supplied position. Type-range will be determined from the filter
*/
PositionedIDVec findNavaidsByFreq(int freqKhz, const SGGeod& pos, FGPositioned::Filter* filt);
/// overload version of the above that does not consider positioned when
@ -175,8 +223,15 @@ public:
// airways
int findAirway(int network, const std::string& aName);
/**
* insert an edge between two positioned nodes, into the network.
* The airway identifier will be set accordingly. No reverse edge is created
* by this method - edges are directional so a reverses must be explicitly
* created.
*/
void insertEdge(int network, int airwayID, PositionedID from, PositionedID to);
/// is the specified positioned a node on the network?
bool isInAirwayNetwork(int network, PositionedID pos);
/**
@ -184,6 +239,17 @@ public:
* in an airway
*/
AirwayEdgeVec airwayEdgesFrom(int network, PositionedID pos);
// ground-network
PositionedIDVec groundNetNodes(PositionedID aAirport, bool onlyPushback);
void markGroundnetAsPushback(PositionedID nodeId);
PositionedID findGroundNetNode(PositionedID airport, const SGGeod& aPos,
bool onRunway, FGRunway* aRunway = NULL);
PositionedIDVec groundNetEdgesFrom(PositionedID pos, bool onlyPushback);
PositionedIDVec findAirportParking(PositionedID airport, const std::string& flightType,
int radius);
private:
NavDataCache();

View file

@ -229,7 +229,7 @@ protected:
void modifyPosition(const SGGeod& newPos);
const PositionedID mGuid;
const SGGeod mPosition;
SGGeod mPosition;
const SGVec3d mCart;
const Type mType;
const std::string mIdent;

View file

@ -53,6 +53,7 @@
#include <Navaids/routePath.hxx>
#include <Navaids/procedure.hxx>
#include <Navaids/airways.hxx>
#include <Navaids/NavDataCache.hxx>
using namespace flightgear;
@ -1339,13 +1340,16 @@ static naRef f_airport_parking(naContext c, naRef me, int argc, naRef* args)
}
FGAirportDynamics* dynamics = apt->getDynamics();
for (int i=0; i<dynamics->getNrOfParkings(); ++i) {
FGParking* park = dynamics->getParking(i);
// filter out based on availability and type
if (onlyAvailable && !park->isAvailable()) {
PositionedIDVec parkings = flightgear::NavDataCache::instance()->airportItemsOfType(apt->guid(),
FGPositioned::PARKING);
BOOST_FOREACH(PositionedID parking, parkings) {
// filter out based on availability and type
if (onlyAvailable && !dynamics->isParkingAvailable(parking)) {
continue;
}
FGParking* park = dynamics->getParking(parking);
if (!type.empty() && (park->getType() != type)) {
continue;
}