# include <config.h>
#endif
+#ifdef _MSC_VER
+# define _USE_MATH_DEFINES
+#endif
#include <math.h>
+#include <algorithm>
#include <simgear/compiler.h>
+
+#include <plib/sg.h>
+#include <plib/ul.h>
+
#include <Environment/environment_mgr.hxx>
#include <Environment/environment.hxx>
#include <simgear/misc/sg_path.hxx>
#include "simple.hxx"
-SG_USING_NAMESPACE(std);
+SG_USING_STD(sort);
+SG_USING_STD(random_shuffle);
+
/******************************************************************************
* ScheduleTime
//cerr << "Nr of items to process: " << nrItems << endl;
if (nrItems > 0)
{
- for (int i = 0; i < start.size(); i++)
+ for (unsigned int i = 0; i < start.size(); i++)
{
//cerr << i << endl;
if ((dayStart >= start[i]) && (dayStart <= end[i]))
}
//couldn't find one so return 0;
//cerr << "Returning 0 " << endl;
- return string(0);
}
+ return string(0);
}
/******************************************************************************
* RunwayList
preferredRunways.push_back(*i);
return *this;
}
-void RunwayList::set(string tp, string lst)
+void RunwayList::set(const string &tp, const string &lst)
{
//weekday = atoi(timeCopy.substr(0,1).c_str());
// timeOffsetInDays = weekday - currTimeDate->getGmt()->tm_wday;
choice[0] = other.choice[0];
choice[1] = other.choice[1];
nrActive = other.nrActive;
+ return *this;
}
-void RunwayGroup::setActive(string aptId,
+void RunwayGroup::setActive(const string &aptId,
double windSpeed,
double windHeading,
double maxTail,
FGRunway rwy;
int activeRwys = rwyList.size(); // get the number of runways active
int nrOfPreferences;
- bool found = true;
- double heading;
+ // bool found = true;
+ // double heading;
double hdgDiff;
double crossWind;
double tailWind;
}
-void RunwayGroup::getActive(int i, string *name, string *type)
+void RunwayGroup::getActive(int i, string &name, string &type)
{
if (i == -1)
{
return;
}
- if (nrActive == rwyList.size())
+ if (nrActive == (int)rwyList.size())
{
- *name = rwyList[i].getRwyList(active);
- *type = rwyList[i].getType();
+ name = rwyList[i].getRwyList(active);
+ type = rwyList[i].getType();
}
else
{
- *name = rwyList[choice[i]].getRwyList(active);
- *type = rwyList[choice[i]].getType();
+ name = rwyList[choice[i]].getRwyList(active);
+ type = rwyList[choice[i]].getType();
}
}
/*****************************************************************************
if (!(strcmp(trafficType, "mil"))) {
return &milTimes;
}
+ return 0;
}
-RunwayGroup *FGRunwayPreference::getGroup(const string groupName)
+RunwayGroup *FGRunwayPreference::getGroup(const string &groupName)
{
PreferenceListIterator i = preferences.begin();
if (preferences.begin() == preferences.end())
}
//based on a string containing hour and minute, return nr seconds since day start.
-time_t FGRunwayPreference::processTime(string tme)
+time_t FGRunwayPreference::processTime(const string &tme)
{
string hour = tme.substr(0, tme.find(":",0));
string minute = tme.substr(tme.find(":",0)+1, tme.length());
<< endl;
}
-/*********************************************************************************
- * FGParking
- ********************************************************************************/
-FGParking::FGParking(double lat,
- double lon,
- double hdg,
- double rad,
- int idx,
- string name,
- string tpe,
- string codes)
-{
- latitude = lat;
- longitude = lon;
- heading = hdg;
- parkingName = name;
- index = idx;
- type = tpe;
- airlineCodes = codes;
-}
-
-double FGParking::processPosition(string pos)
+/*****************************************************************************
+ * Helper function for parsing position string
+ ****************************************************************************/
+double processPosition(const string &pos)
{
string prefix;
string subs;
return value;
}
+
+/*********************************************************************************
+ * FGParking
+ ********************************************************************************/
+FGParking::FGParking(double lat,
+ double lon,
+ double hdg,
+ double rad,
+ int idx,
+ const string &name,
+ const string &tpe,
+ const string &codes)
+{
+ latitude = lat;
+ longitude = lon;
+ heading = hdg;
+ parkingName = name;
+ index = idx;
+ type = tpe;
+ airlineCodes = codes;
+}
+
+
/***************************************************************************
* FGAirport
***************************************************************************/
_elevation = other._elevation;
_name = other._name;
_has_metar = other._has_metar;
- for (FGParkingVecConstIterator i= other.parkings.begin(); i != other.parkings.end(); i++)
- parkings.push_back(*(i));
+ for (FGParkingVecConstIterator ip= other.parkings.begin(); ip != other.parkings.end(); ip++)
+ parkings.push_back(*(ip));
rwyPrefs = other.rwyPrefs;
lastUpdate = other.lastUpdate;
- stringVecConstIterator i;
- for (i = other.landing.begin(); i != other.landing.end(); i++)
- landing.push_back(*i);
- for (i = other.takeoff.begin(); i != other.takeoff.end(); i++)
- takeoff.push_back(*i);
+ stringVecConstIterator il;
+ for (il = other.landing.begin(); il != other.landing.end(); il++)
+ landing.push_back(*il);
+ for (il = other.takeoff.begin(); il != other.takeoff.end(); il++)
+ takeoff.push_back(*il);
lastUpdate = other.lastUpdate;
for (int i = 0; i < 10; i++)
{
}
}
-FGAirport::FGAirport(string id, double lon, double lat, double elev, string name, bool has_metar)
+FGAirport::FGAirport(const string &id, double lon, double lat, double elev, const string &name, bool has_metar)
{
_id = id;
_longitude = lon;
}
-bool FGAirport::getAvailableParking(double *lat, double *lon, double *heading, int *gateId, double rad, string flType, string acType, string airline)
+// Initialization required after XMLRead
+void FGAirport::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.addNodes(&parkings);
+ groundNetwork.init();
+}
+
+bool FGAirport::getAvailableParking(double *lat, double *lon, double *heading, int *gateId, double rad, const string &flType, const string &acType, const string &airline)
{
bool found = false;
bool available = false;
FGParking *FGAirport::getParking(int i)
{
- if (i < parkings.size())
+ if (i < (int)parkings.size())
return &(parkings[i]);
else
return 0;
}
string FGAirport::getParkingName(int i)
{
- if (i < parkings.size() && i >= 0)
+ if (i < (int)parkings.size() && i >= 0)
return (parkings[i].getName());
else
return string("overflow");
}
void FGAirport::startElement (const char * name, const XMLAttributes &atts) {
- const char * attval;
+ // const char *attval;
FGParking park;
+ FGTaxiNode taxiNode;
+ FGTaxiSegment taxiSegment;
+ int index = 0;
+ taxiSegment.setIndex(index);
//cout << "Start element " << name << endl;
string attname;
string value;
}
park.setName((gateName+gateNumber));
parkings.push_back(park);
- }
+ }
+ if (name == string("node"))
+ {
+ for (int i = 0; i < atts.size() ; i++)
+ {
+ attname = atts.getName(i);
+ if (attname == string("index"))
+ taxiNode.setIndex(atoi(atts.getValue(i)));
+ if (attname == string("lat"))
+ taxiNode.setLatitude(atts.getValue(i));
+ if (attname == string("lon"))
+ taxiNode.setLongitude(atts.getValue(i));
+ }
+ groundNetwork.addNode(taxiNode);
+ }
+ if (name == string("arc"))
+ {
+ taxiSegment.setIndex(++index);
+ for (int i = 0; i < atts.size() ; i++)
+ {
+ attname = atts.getName(i);
+ if (attname == string("begin"))
+ taxiSegment.setStartNodeRef(atoi(atts.getValue(i)));
+ if (attname == string("end"))
+ taxiSegment.setEndNodeRef(atoi(atts.getValue(i)));
+ }
+ groundNetwork.addSegment(taxiSegment);
+ }
// sort by radius, in asending order, so that smaller gates are first in the list
- sort(parkings.begin(), parkings.end());
}
void FGAirport::endElement (const char * name) {
<< endl;
}
-void FGAirport::setRwyUse(FGRunwayPreference& ref)
+void FGAirport::setRwyUse(const FGRunwayPreference& ref)
{
rwyPrefs = ref;
//cerr << "Exiting due to not implemented yet" << endl;
//exit(1);
}
-void FGAirport::getActiveRunway(string trafficType, int action, string *runway)
+void FGAirport::getActiveRunway(const string &trafficType, int action, string &runway)
{
double windSpeed;
double windHeading;
if (!(rwyPrefs.available()))
{
- chooseRunwayFallback(runway);
+ runway = chooseRunwayFallback();
return; // generic fall back goes here
}
else
for (int i = 0; i < nrActiveRunways; i++)
{
type = "unknown"; // initialize to something other than landing or takeoff
- currRunwayGroup->getActive(i, &name, &type);
+ currRunwayGroup->getActive(i, name, type);
if (type == "landing")
{
landing.push_back(name);
int nr = takeoff.size();
if (nr)
{
- *runway = takeoff[(rand() % nr)];
+ runway = takeoff[(rand() % nr)];
}
else
{ // Fallback
- chooseRunwayFallback(runway);
+ runway = chooseRunwayFallback();
}
}
if (action == 2) // landing
int nr = landing.size();
if (nr)
{
- *runway = landing[(rand() % nr)];
+ runway = landing[(rand() % nr)];
}
else
{ //fallback
- chooseRunwayFallback(runway);
+ runway = chooseRunwayFallback();
}
}
- //*runway = globals->get_runways()->search(_id, int(windHeading));
- //cerr << "Seleceted runway: " << *runway << endl;
+ //runway = globals->get_runways()->search(_id, int(windHeading));
+ //cerr << "Seleceted runway: " << runway << endl;
}
}
-void FGAirport::chooseRunwayFallback(string *runway)
+string FGAirport::chooseRunwayFallback()
{
FGEnvironment
stationweather = ((FGEnvironmentMgr *) globals->get_subsystem("environment"))
//which is consistent with Flightgear's initial setup.
}
- *runway = globals->get_runways()->search(_id, int(windHeading));
- return; // generic fall back goes here
+ return globals->get_runways()->search(_id, int(windHeading));
+}
+
+
+
+/**************************************************************************
+ * FGTaxiNode
+ *************************************************************************/
+FGTaxiNode::FGTaxiNode()
+{
+}
+
+/***************************************************************************
+ * FGTaxiSegment
+ **************************************************************************/
+FGTaxiSegment::FGTaxiSegment()
+{
+}
+
+void FGTaxiSegment::setStart(FGTaxiNodeVector *nodes)
+{
+ FGTaxiNodeVectorIterator i = nodes->begin();
+ while (i != nodes->end())
+ {
+ if (i->getIndex() == startNode)
+ {
+ start = i->getAddress();
+ i->addSegment(this);
+ return;
+ }
+ i++;
+ }
+}
+
+void FGTaxiSegment::setEnd(FGTaxiNodeVector *nodes)
+{
+ FGTaxiNodeVectorIterator i = nodes->begin();
+ while (i != nodes->end())
+ {
+ if (i->getIndex() == endNode)
+ {
+ end = i->getAddress();
+ return;
+ }
+ i++;
+ }
+}
+
+// There is probably a computationally cheaper way of
+// doing this.
+void FGTaxiSegment::setTrackDistance()
+{
+ double course;
+ SGWayPoint first (start->getLongitude(),
+ start->getLatitude(),
+ 0);
+ SGWayPoint second (end->getLongitude(),
+ end->getLatitude(),
+ 0);
+ first.CourseAndDistance(second, &course, &length);
+
+}
+
+bool FGTaxiRoute::next(int *val)
+{
+ //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 (currNode == nodes.end())
+ return false;
+ *val = *(currNode);
+ currNode++;
+ return true;
+};
+/***************************************************************************
+ * FGGroundNetwork()
+ **************************************************************************/
+
+FGGroundNetwork::FGGroundNetwork()
+{
+ hasNetwork = false;
+}
+
+void FGGroundNetwork::addSegment(const FGTaxiSegment &seg)
+{
+ segments.push_back(seg);
+}
+
+void FGGroundNetwork::addNode(const FGTaxiNode &node)
+{
+ nodes.push_back(node);
+}
+
+void FGGroundNetwork::addNodes(FGParkingVec *parkings)
+{
+ FGTaxiNode n;
+ FGParkingVecIterator i = parkings->begin();
+ while (i != parkings->end())
+ {
+ n.setIndex(i->getIndex());
+ n.setLatitude(i->getLatitude());
+ n.setLongitude(i->getLongitude());
+ nodes.push_back(n);
+
+ i++;
+ }
+}
+
+
+
+void FGGroundNetwork::init()
+{
+ hasNetwork = true;
+ FGTaxiSegmentVectorIterator i = segments.begin();
+ while(i != segments.end()) {
+ //cerr << "initializing node " << i->getIndex() << endl;
+ i->setStart(&nodes);
+ i->setEnd (&nodes);
+ i->setTrackDistance();
+ //cerr << "Track distance = " << i->getLength() << endl;
+ //cerr << "Track ends at" << i->getEnd()->getIndex() << endl;
+ i++;
+ }
+ //exit(1);
+}
+
+int FGGroundNetwork::findNearestNode(double lat, double lon)
+{
+ double minDist = HUGE_VAL;
+ double course, dist;
+ int index;
+ SGWayPoint first (lon,
+ lat,
+ 0);
+
+ for (FGTaxiNodeVectorIterator
+ itr = nodes.begin();
+ itr != nodes.end(); itr++)
+ {
+ double course;
+ SGWayPoint second (itr->getLongitude(),
+ itr->getLatitude(),
+ 0);
+ first.CourseAndDistance(second, &course, &dist);
+ if (dist < minDist)
+ {
+ minDist = dist;
+ index = itr->getIndex();
+ //cerr << "Minimum distance of " << minDist << " for index " << index << endl;
+ }
+ }
+ return index;
}
+FGTaxiNode *FGGroundNetwork::findNode(int idx)
+{
+ for (FGTaxiNodeVectorIterator
+ itr = nodes.begin();
+ itr != nodes.end(); itr++)
+ {
+ if (itr->getIndex() == idx)
+ return itr->getAddress();
+ }
+ return 0;
+}
+
+FGTaxiRoute FGGroundNetwork::findShortestRoute(int start, int end)
+{
+ foundRoute = false;
+ totalDistance = 0;
+ FGTaxiNode *firstNode = findNode(start);
+ FGTaxiNode *lastNode = findNode(end);
+ //prevNode = prevPrevNode = -1;
+ //prevNode = start;
+ routes.clear();
+ traceStack.clear();
+ trace(firstNode, end, 0, 0);
+ FGTaxiRoute empty;
+
+ if (!foundRoute)
+ {
+ cerr << "Failed to find route from waypoint " << start << " to " << end << endl;
+ exit(1);
+ }
+ sort(routes.begin(), routes.end());
+ //for (intVecIterator i = route.begin(); i != route.end(); i++)
+ // {
+ // rte->push_back(*i);
+ // }
+
+ if (routes.begin() != routes.end())
+ return *(routes.begin());
+ else
+ return empty;
+}
+
+
+void FGGroundNetwork::trace(FGTaxiNode *currNode, int end, int depth, double distance)
+{
+ traceStack.push_back(currNode->getIndex());
+ totalDistance += distance;
+ //cerr << "Starting trace " << depth << " total distance: " << totalDistance<< endl;
+ //<< currNode->getIndex() << endl;
+
+ // If the current route matches the required end point we found a valid route
+ // So we can add this to the routing table
+ if (currNode->getIndex() == end)
+ {
+ //cerr << "Found route : " << totalDistance << "" << " " << *(traceStack.end()-1) << endl;
+ routes.push_back(FGTaxiRoute(traceStack,totalDistance));
+ traceStack.pop_back();
+ if (!(foundRoute))
+ maxDistance = totalDistance;
+ else
+ if (totalDistance < maxDistance)
+ maxDistance = totalDistance;
+ foundRoute = true;
+ totalDistance -= distance;
+ return;
+ }
+
+
+ // search if the currentNode has been encountered before
+ // if so, we should step back one level, because it is
+ // rather rediculous to proceed further from here.
+ // if the current node has not been encountered before,
+ // i should point to traceStack.end()-1; and we can continue
+ // if i is not traceStack.end, the previous node was found,
+ // and we should return.
+ // This only works at trace levels of 1 or higher though
+ if (depth > 0) {
+ intVecIterator i = traceStack.begin();
+ while ((*i) != currNode->getIndex()) {
+ //cerr << "Route so far : " << (*i) << endl;
+ i++;
+ }
+ if (i != traceStack.end()-1) {
+ traceStack.pop_back();
+ totalDistance -= distance;
+ return;
+ }
+ // If the total distance from start to the current waypoint
+ // is longer than that of a route we can also stop this trace
+ // and go back one level.
+ if ((totalDistance > maxDistance) && foundRoute)
+ {
+ //cerr << "Stopping rediculously long trace: " << totalDistance << endl;
+ traceStack.pop_back();
+ totalDistance -= distance;
+ return;
+ }
+ }
+
+ //cerr << "2" << endl;
+ if (currNode->getBeginRoute() != currNode->getEndRoute())
+ {
+ //cerr << "3" << endl;
+ for (FGTaxiSegmentPointerVectorIterator
+ i = currNode->getBeginRoute();
+ i != currNode->getEndRoute();
+ i++)
+ {
+ //cerr << (*i)->getLenght() << endl;
+ trace((*i)->getEnd(), end, depth+1, (*i)->getLength());
+ // {
+ // // cerr << currNode -> getIndex() << " ";
+ // route.push_back(currNode->getIndex());
+ // return true;
+ // }
+ }
+ }
+ else
+ {
+ cerr << "4" << endl;
+ }
+ traceStack.pop_back();
+ totalDistance -= distance;
+ return;
+}
+
+
+
/******************************************************************************
* FGAirportList
*****************************************************************************/
+// Populates a list of subdirectories of $FG_ROOT/Airports/AI so that
+// the add() method doesn't have to try opening 2 XML files in each of
+// thousands of non-existent directories. FIXME: should probably add
+// code to free this list after parsing of apt.dat is finished;
+// non-issue at the moment, however, as there are no AI subdirectories
+// in the base package.
+FGAirportList::FGAirportList()
+{
+ ulDir* d;
+ ulDirEnt* dent;
+ SGPath aid( globals->get_fg_root() );
+ aid.append( "/Airports/AI" );
+ if((d = ulOpenDir(aid.c_str())) == NULL)
+ return;
+ while((dent = ulReadDir(d)) != NULL) {
+ cerr << "Dent: " << dent->d_name; // DEBUG
+ ai_dirs.insert(dent->d_name);
+ }
+ ulCloseDir(d);
+}
+
+
+FGAirportList::~FGAirportList( void ) {
+ for(unsigned int i = 0; i < airports_array.size(); ++i) {
+ delete airports_array[i];
+ }
+}
+
// add an entry to the list
-void FGAirportList::add( const string id, const double longitude,
+void FGAirportList::add( const string &id, const double longitude,
const double latitude, const double elevation,
- const string name, const bool has_metar )
+ const string &name, const bool has_metar )
{
- FGRunwayPreference rwyPrefs;
- FGAirport a(id, longitude, latitude, elevation, name, has_metar);
- //a._id = id;
- //a._longitude = longitude;
- //a._latitude = latitude;
- //a._elevation = elevation;
- //a._name = name;
- //a._has_metar = has_metar;
+ FGRunwayPreference rwyPrefs;
+ FGAirport* a = new FGAirport(id, longitude, latitude, elevation, name, has_metar);
SGPath parkpath( globals->get_fg_root() );
parkpath.append( "/Airports/AI/" );
parkpath.append(id);
rwyPrefPath.append( "/Airports/AI/" );
rwyPrefPath.append(id);
rwyPrefPath.append("rwyuse.xml");
- if (parkpath.exists())
- {
- try {
- readXML(parkpath.str(),a);
- }
- catch (const sg_exception &e) {
- //cerr << "unable to read " << parkpath.str() << endl;
- }
- }
- if (rwyPrefPath.exists())
- {
- try {
- readXML(rwyPrefPath.str(), rwyPrefs);
- a.setRwyUse(rwyPrefs);
- }
- catch (const sg_exception &e) {
- //cerr << "unable to read " << rwyPrefPath.str() << endl;
- //exit(1);
- }
- }
+ if (ai_dirs.find(id.c_str()) != ai_dirs.end()
+ && parkpath.exists())
+ {
+ try {
+ readXML(parkpath.str(),*a);
+ a->init();
+ }
+ catch (const sg_exception &e) {
+ //cerr << "unable to read " << parkpath.str() << endl;
+ }
+ }
+ if (ai_dirs.find(id.c_str()) != ai_dirs.end()
+ && rwyPrefPath.exists())
+ {
+ try {
+ readXML(rwyPrefPath.str(), rwyPrefs);
+ a->setRwyUse(rwyPrefs);
+ }
+ catch (const sg_exception &e) {
+ //cerr << "unable to read " << rwyPrefPath.str() << endl;
+ //exit(1);
+ }
+ }
- airports_by_id[a.getId()] = a;
+ airports_by_id[a->getId()] = a;
// try and read in an auxilary file
-
- airports_array.push_back( &airports_by_id[a.getId()] );
+
+ airports_array.push_back( a );
SG_LOG( SG_GENERAL, SG_BULK, "Adding " << id << " pos = " << longitude
<< ", " << latitude << " elev = " << elevation );
}
// search for the specified id
-FGAirport FGAirportList::search( const string& id) {
- return airports_by_id[id];
+FGAirport* FGAirportList::search( const string& id) {
+ airport_map_iterator itr = airports_by_id.find(id);
+ return(itr == airports_by_id.end() ? NULL : itr->second);
}
-// search for the specified id and return a pointer
-FGAirport* FGAirportList::search( const string& id, FGAirport *result) {
- FGAirport* retval = airports_by_id[id].getAddress();
- //cerr << "Returning Airport of string " << id << " results in " << retval->getId();
- return retval;
+
+// search for first subsequent alphabetically to supplied id
+const FGAirport* FGAirportList::findFirstById( const string& id, bool exact ) {
+ airport_map_iterator itr;
+ if(exact) {
+ itr = airports_by_id.find(id);
+ } else {
+ itr = airports_by_id.lower_bound(id);
+ }
+ if(itr == airports_by_id.end()) {
+ return(NULL);
+ } else {
+ return(itr->second);
+ }
}
+
// search for the airport nearest the specified position
-FGAirport FGAirportList::search( double lon_deg, double lat_deg,
+FGAirport* FGAirportList::search( double lon_deg, double lat_deg,
bool with_metar ) {
- int closest = 0;
+ int closest = -1;
double min_dist = 360.0;
unsigned int i;
for ( i = 0; i < airports_array.size(); ++i ) {
}
}
- return *airports_array[closest];
+ return ( closest > -1 ? airports_array[closest] : NULL );
}
-// Destructor
-FGAirportList::~FGAirportList( void ) {
-}
-
int
FGAirportList::size () const
{
return airports_array.size();
}
-const FGAirport *FGAirportList::getAirport( int index ) const
+const FGAirport *FGAirportList::getAirport( unsigned int index ) const
{
- return airports_array[index];
+ if(index < airports_array.size()) {
+ return(airports_array[index]);
+ } else {
+ return(NULL);
+ }
}
* Mark the specified airport record as not having metar
*/
void FGAirportList::no_metar( const string &id ) {
- airports_by_id[id].setMetar(false);
+ if(airports_by_id.find(id) != airports_by_id.end()) {
+ airports_by_id[id]->setMetar(false);
+ }
}
* Mark the specified airport record as (yes) having metar
*/
void FGAirportList::has_metar( const string &id ) {
- airports_by_id[id].setMetar(true);
+ if(airports_by_id.find(id) != airports_by_id.end()) {
+ airports_by_id[id]->setMetar(true);
+ }
}