//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);
}
}
}
-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() {
start_time = 0;
arrivalTime = 0;
leg = 10;
- gateId = 0;
lastNodeVisited = 0;
- taxiRoute = 0;
+ // taxiRoute = 0;
wpt_iterator = waypoints.begin();
isValid = true;
}
start_time = 0;
arrivalTime = 0;
leg = 10;
- gateId = 0;
lastNodeVisited = 0;
- taxiRoute = 0;
+// taxiRoute = 0;
isValid = parseProperties(filename);
start_time = start;
arrivalTime = 0;
leg = 10;
- gateId = 0;
lastNodeVisited = 0;
- taxiRoute = 0;
+ // taxiRoute = 0;
if (parseProperties(p)) {
isValid = true;
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,
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)
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();
return 0;
}
-
double FGAIFlightPlan::checkTrackLength(string wptName) {
// skip the first two waypoints: first one is behind, second one is partially done;
double trackDistance = 0;
}
(waypoints.back())->setName((waypoints.back())->getName() + name);
}
+
+void FGAIFlightPlan::setGate(ParkingAssignment pka)
+{
+ gate = pka;
+}
+
+FGParking* FGAIFlightPlan::getParkingGate()
+{
+ return gate.parking();
+}
#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;
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;
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();}
void shortenToFirst(unsigned int number, std::string name);
+ void setGate(ParkingAssignment pka);
+ FGParking* getParkingGate();
private:
FGAIFlightPlan *sid;
typedef std::vector <FGAIWaypoint*> wpt_vector_type;
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;
// 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 <<
}
intVec ids;
- int runwayId = 0;
+ PositionedID runwayId = 0;
if (gn->getVersion() > 0) {
runwayId = gn->findNearestNodeOnRunway(runwayTakeoff);
} else {
// 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);
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);
}
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.
}
intVec ids;
- int runwayId = 0;
+ PositionedID runwayId = 0;
if (gn->getVersion() == 1) {
runwayId = gn->findNearestNodeOnRunway(lastWptPos);
} else {
// 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;
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:
//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;
}
{
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);
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;
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();
// 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 <<
" 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"));
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);
-
- wpt->setRouteIndex(pushForwardSegment->getIndex());
- pushBackWaypoint(wpt);
- }
- // cerr << "Done " << endl;
- waypoints.back()->setName(string("PushBackPoint"));
- // cerr << "Done assinging new name" << endl;
+ lastNodeVisited = pushForwardSegment->getEnd()->getIndex();
+ double distance = pushForwardSegment->getLength();
+
+ 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;
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)
inline double vTouchdown () { return _vTouchdown; };
inline double vCruise () { return _vCruise; };
+ double decelerationOnGround() const;
private:
double _acceleration;
double _deceleration;
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);
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,
} 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);
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);
#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
****************************************************************************/
//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)
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)
}
}
+ 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)
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)
#include <simgear/xml/easyxml.hxx>
#include "dynamics.hxx"
+#include <Navaids/positioned.hxx>
class FGAirportDynamicsXMLLoader : public XMLVisitor {
public:
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
#include <Main/fg_props.hxx>
#include <Airports/runways.hxx>
#include <ATCDCL/ATCutils.hxx>
+#include <Navaids/NavDataCache.hxx>
#include "simple.hxx"
#include "dynamics.hxx"
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),
// 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();
+ setParkingAvailable(pk, false);
+ return parking;
}
- return -1;
+ return NULL;
}
-int FGAirportDynamics::getAvailableParking(double radius, const string & flType,
+ParkingAssignment FGAirportDynamics::getAvailableParking(double radius, const string & flType,
const string & acType,
const string & airline)
{
- if (parkings.empty()) {
- return -1;
- }
+ SG_UNUSED(acType); // sadly not used at the moment
// most exact seach - airline codes must be present and match
- int result = innerGetAvailableParking(radius, flType, acType, airline, true);
- if (result >= 0) {
- return result;
+ 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, acType, airline, false);
- if (result >= 0) {
- return result;
+ result = innerGetAvailableParking(radius, flType, airline, false);
+ if (result) {
+ return ParkingAssignment(result, _ap);
}
// fallback - ignore the airline code entirely
- return innerGetAvailableParking(radius, flType, acType, string(), false);
+ result = innerGetAvailableParking(radius, flType, string(), false);
+ return result ? ParkingAssignment(result, _ap) : ParkingAssignment();
}
-FGParking *FGAirportDynamics::getParking(int id)
+FGParking *FGAirportDynamics::getParking(PositionedID id) const
{
- BOOST_FOREACH(FGParking* i, parkings) {
- if (id == i->getIndex()) {
- return i;
- }
- }
-
- return NULL;
+ return static_cast<FGParking*>(flightgear::NavDataCache::instance()->loadById(id));
}
-string FGAirportDynamics::getParkingName(int id)
+string FGAirportDynamics::getParkingName(PositionedID id) const
{
FGParking* p = getParking(id);
if (p) {
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 ParkingAssignment(getParking(guid), _ap);
+}
- return -1;
+void FGAirportDynamics::setParkingAvailable(PositionedID guid, bool available)
+{
+ if (available) {
+ releaseParking(guid);
+ } else {
+ occupiedParkings.insert(guid);
+ }
}
-void FGAirportDynamics::releaseParking(int id)
+bool FGAirportDynamics::isParkingAvailable(PositionedID parking) const
{
- if (id >= 0) {
- FGParking* parking = getParking(id);
- if (parking) {
- parking->setAvailable(true);
- }
- }
+ 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)
return rwy->ident();
}
-void FGAirportDynamics::addParking(FGParking* park)
-{
- parkings.push_back(park);
-}
-
double FGAirportDynamics::getElevation() const
{
return _ap->getElevation();
#ifndef _AIRPORT_DYNAMICS_HXX_
#define _AIRPORT_DYNAMICS_HXX_
+#include <set>
+
#include <ATC/trafficcontrol.hxx>
#include "parking.hxx"
#include "groundnetwork.hxx"
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;
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);
{ 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() {
#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)
{
// 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);
- }
+ 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);
}
-#endif
+ }
+
return mPosition.getElevationFt();
}
-double FGTaxiNode::getElevationM(double refelev)
+double FGTaxiNode::getElevationM()
{
- return geod().getElevationM();
-}
-
-FGTaxiSegment* FGTaxiNode::getArcTo(FGTaxiNode* aEnd) const
-{
- BOOST_FOREACH(FGTaxiSegment* arc, next) {
- if (arc->getEnd() == aEnd) {
- return arc;
- }
- }
-
- return NULL;
+ return getElevationFt() * SG_FEET_TO_METER;
}
#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;
#include <math.h>
#include <algorithm>
#include <fstream>
+#include <map>
#include <boost/foreach.hpp>
#include <osg/Geode>
#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>
#include <AIModel/AIAircraft.hxx>
#include <AIModel/performancedata.hxx>
#include <AIModel/AIFlightPlan.hxx>
+#include <Navaids/NavDataCache.hxx>
#include <ATC/atc_mgr.hxx>
#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)
+SGGeod FGTaxiSegment::getCenter() const
{
- 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;
+ FGTaxiNode* start(getStart()), *end(getEnd());
+ double heading, length, az2;
SGGeodesy::inverse(start->geod(), end->geod(), heading, az2, length);
- return true;
+ return SGGeodesy::direct(start->geod(), heading, length * 0.5);
}
-SGGeod FGTaxiSegment::getCenter() const
+FGTaxiNode* FGTaxiSegment::getEnd() const
{
- return SGGeodesy::direct(start->geod(), heading, length * 0.5);
+ 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();
/***************************************************************************
* 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()
**************************************************************************/
parent(NULL)
{
hasNetwork = false;
- foundRoute = false;
totalDistance = 0;
maxDistance = 0;
//maxDepth = 1000;
// 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")) {
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->isPushBack()) {
- pushBackNodes.push_back(segment->getEnd());
+
+ if (segment->oppositeDirection) {
+ continue; // already establish
}
- }
-
- // 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;
}
}
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());
if (airport.empty()) {
return;
}
-
+#if 0
char buffer[128];
::snprintf(buffer, 128, "%c/%c/%c/",
airport[0], airport[1], airport[2]);
}
}
}
+#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 {
}
}
+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
+}
+
+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(int start, int end,
+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)
{
<< " at " << ((parent) ? parent->getId() : "<unknown>"));
return FGTaxiRoute();
}
- firstNode->setPathScore(0);
+ searchData[firstNode].score = 0.0;
FGTaxiNode *lastNode = findNode(end);
if (!lastNode)
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);
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);
- }
-}
-
-int FGTaxiSegment::getPenalty(int nGates)
-{
- int penalty = 0;
- if (end->getIndex() < nGates) {
- penalty += 10000;
- }
- if (end->getIsOnRunway()) { // For now. In future versions, need to find out whether runway is active.
- penalty += 1000;
+
+ return FGTaxiRoute();
}
- return penalty;
+
+ // assemble route from backtrace information
+ PositionedIDVec nodes;
+ FGTaxiNode *bt = lastNode;
+ while (searchData[bt].previousNode != 0) {
+ nodes.push_back(bt->guid());
+ bt = searchData[bt].previousNode;
+ }
+ nodes.push_back(start);
+ reverse(nodes.begin(), nodes.end());
+ return FGTaxiRoute(nodes, searchData[lastNode].score, 0);
}
/* ATC Related Functions */
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
} 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())) {
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);
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);
bool operator<(const FGTaxiSegment &other) const {
return index < other.index;
};
- //bool hasSmallerHeadingDiff (const FGTaxiSegment &other) const { return headingDiff < other.headingDiff; };
+
FGTaxiSegment *opposite() {
return oppositeDirection;
};
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();
int nodesLeft() {
return nodes.end() - currNode;
};
-
-// int getDepth() { return depth; };
};
typedef std::vector<FGTaxiRoute> TaxiRouteVector;
int count;
int version;
- IndexTaxiNodeMap nodes;
- FGTaxiNodeVector pushBackNodes;
-
FGTaxiSegmentVector segments;
- TaxiRouteVector routes;
TrafficVector activeTraffic;
TrafficVectorIterator currTraffic;
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;
};
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,
#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)
{
}
#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; };
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 {
_dynamics = new FGAirportDynamics(this);
XMLLoader::load(_dynamics);
-
+ _dynamics->init();
+
FGRunwayPreference rwyPrefs(this);
XMLLoader::load(&rwyPrefs);
_dynamics->setRwyUse(rwyPrefs);
#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>
#include "simple.hxx"
#include "runwayprefs.hxx"
+#include <Navaids/NavDataCache.hxx>
+
using std::string;
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) {
return false;
}
- int gateID;
+ ParkingAssignment pka;
double radius = fgGetDouble("/sim/dimensions/radius-m");
if ((parkpos == string("AVAILABLE")) && (radius > 0)) {
string fltType;
}
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;
}
#include "PositionedOctree.hxx"
#include <Airports/apt_loader.hxx>
#include <Navaids/airways.hxx>
+#include <Airports/parking.hxx>
+#include <Airports/gnnode.hxx>
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
")");
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()
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)");
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)
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)
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.
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;
}
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)
{
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
#include <Navaids/positioned.hxx>
class SGPath;
+class FGRunway;
namespace flightgear
{
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,
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,
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
// 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);
/**
* 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();
void modifyPosition(const SGGeod& newPos);
const PositionedID mGuid;
- const SGGeod mPosition;
+ SGGeod mPosition;
const SGVec3d mCart;
const Type mType;
const std::string mIdent;
#include <Navaids/routePath.hxx>
#include <Navaids/procedure.hxx>
#include <Navaids/airways.hxx>
+#include <Navaids/NavDataCache.hxx>
using namespace flightgear;
}
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;
}