X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;ds=sidebyside;f=src%2FAIModel%2FAIManager.cxx;h=9813b9dc25f768542f0378e344e5e2850d34c564;hb=b5c46a8d59120f18b0bc268af72ecb6d3a75b3e3;hp=2a4b7dde591b8514d4cf6b2f75154c4d7a724e0a;hpb=c537267f96a959cdbb29d89066a388b4247ec8d2;p=flightgear.git diff --git a/src/AIModel/AIManager.cxx b/src/AIModel/AIManager.cxx index 2a4b7dde5..9813b9dc2 100644 --- a/src/AIModel/AIManager.cxx +++ b/src/AIModel/AIManager.cxx @@ -16,19 +16,17 @@ // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software -// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include +#include +#include -#include -#include
#include
#include -#include -#include #include -#include - #include "AIManager.hxx" #include "AIAircraft.hxx" #include "AIShip.hxx" @@ -36,353 +34,428 @@ #include "AIStorm.hxx" #include "AIThermal.hxx" #include "AICarrier.hxx" - -SG_USING_STD(list); - +#include "AIStatic.hxx" +#include "AIMultiplayer.hxx" +#include "AITanker.hxx" +#include "AIWingman.hxx" +#include "AIGroundVehicle.hxx" FGAIManager::FGAIManager() { - initDone = false; - for (int i=0; i < FGAIBase::MAX_OBJECTS; i++) - numObjects[i] = 0; - _dt = 0.0; - dt_count = 9; - scenario_filename = ""; - ai_list.clear(); + _dt = 0.0; + mNumAiModels = 0; + + for (unsigned i = 0; i < FGAIBase::MAX_OBJECTS; ++i) + mNumAiTypeModels[i] = 0; } FGAIManager::~FGAIManager() { - ai_list_itr = ai_list.begin(); - while(ai_list_itr != ai_list.end()) { - (*ai_list_itr)->unbind(); - delete (*ai_list_itr); - ++ai_list_itr; - } - ai_list.clear(); - ModelVecIterator i = loadedModels.begin(); - while (i != loadedModels.end()) - { - i->getModelId()->deRef(); + ai_list_iterator ai_list_itr = ai_list.begin(); + + while(ai_list_itr != ai_list.end()) { + (*ai_list_itr)->unbind(); + ++ai_list_itr; } } +void +FGAIManager::init() { + root = fgGetNode("sim/ai", true); + + enabled = root->getNode("enabled", true)->getBoolValue(); + + if (!enabled) + return; -void FGAIManager::init() { - root = fgGetNode("sim/ai", true); + thermal_lift_node = fgGetNode("/environment/thermal-lift-fps", true); + wind_from_east_node = fgGetNode("/environment/wind-from-east-fps",true); + wind_from_north_node = fgGetNode("/environment/wind-from-north-fps",true); - enabled = root->getNode("enabled", true)->getBoolValue(); - if (!enabled) - return; + user_latitude_node = fgGetNode("/position/latitude-deg", true); + user_longitude_node = fgGetNode("/position/longitude-deg", true); + user_altitude_node = fgGetNode("/position/altitude-ft", true); + user_heading_node = fgGetNode("/orientation/heading-deg", true); + user_pitch_node = fgGetNode("/orientation/pitch-deg", true); + user_yaw_node = fgGetNode("/orientation/side-slip-deg", true); + user_roll_node = fgGetNode("/orientation/roll-deg", true); + user_speed_node = fgGetNode("/velocities/uBody-fps", true); +} - wind_from_down_node = fgGetNode("/environment/wind-from-down-fps", true); - - scenario_filename = root->getNode("scenario", true)->getStringValue(); - if (scenario_filename != "") processScenario( scenario_filename ); +void +FGAIManager::postinit() { + // postinit, so that it can access the Nasal subsystem + map scenarios; + for (int i = 0 ; i < root->nChildren() ; i++) { + SGPropertyNode *n = root->getChild(i); + if (strcmp(n->getName(), "scenario")) + continue; + + string name = n->getStringValue(); + if (name.empty()) + continue; + + if (scenarios.find(name) != scenarios.end()) { + SG_LOG(SG_GENERAL, SG_WARN, "won't load scenario '" << name << "' twice"); + continue; + } - initDone = true; + SG_LOG(SG_GENERAL, SG_ALERT, "loading scenario '" << name << '\''); + processScenario(name); + scenarios[name] = true; + } } +void +FGAIManager::reinit() { + update(0.0); + ai_list_iterator ai_list_itr = ai_list.begin(); -void FGAIManager::bind() { - root = globals->get_props()->getNode("ai/models", true); - root->tie("count", SGRawValuePointer(&numObjects[0])); + while(ai_list_itr != ai_list.end()) { + (*ai_list_itr)->reinit(); + ++ai_list_itr; + } } +void +FGAIManager::bind() { + root = globals->get_props()->getNode("ai/models", true); + root->tie("count", SGRawValueMethods(*this, + &FGAIManager::getNumAiObjects)); +} -void FGAIManager::unbind() { +void +FGAIManager::unbind() { root->untie("count"); } +void +FGAIManager::update(double dt) { + // initialize these for finding nearest thermals + range_nearest = 10000.0; + strength = 0.0; -void FGAIManager::update(double dt) { - - // initialize these for finding nearest thermals - range_nearest = 10000.0; - strength = 0.0; - FGTrafficManager *tmgr = (FGTrafficManager*) globals->get_subsystem("Traffic Manager"); - - if (!enabled) - return; - - _dt = dt; - - ai_list_itr = ai_list.begin(); - while(ai_list_itr != ai_list.end()) { - if ((*ai_list_itr)->getDie()) { - tmgr->release((*ai_list_itr)->getID()); - --numObjects[(*ai_list_itr)->getType()]; - --numObjects[0]; - (*ai_list_itr)->unbind(); - delete (*ai_list_itr); - if ( ai_list_itr == ai_list.begin() ) { - ai_list.erase(ai_list_itr); - ai_list_itr = ai_list.begin(); - continue; - } else { - ai_list.erase(ai_list_itr--); - } - } else { - fetchUserState(); - if ((*ai_list_itr)->isa(FGAIBase::otThermal)) { - processThermal((FGAIThermal*)*ai_list_itr); - } else { - (*ai_list_itr)->update(_dt); - } - } - ++ai_list_itr; - } - wind_from_down_node->setDoubleValue( strength ); + if (!enabled) + return; -} + FGTrafficManager *tmgr = (FGTrafficManager*) globals->get_subsystem("Traffic Manager"); + _dt = dt; + ai_list_iterator ai_list_itr = ai_list.begin(); -void* -FGAIManager::createAircraft( FGAIModelEntity *entity, FGAISchedule *ref) { - - FGAIAircraft* ai_plane = new FGAIAircraft(this, ref); - ai_list.push_back(ai_plane); - ++numObjects[0]; - ++numObjects[FGAIBase::otAircraft]; - if (entity->m_class == "light") { - ai_plane->SetPerformance(&FGAIAircraft::settings[FGAIAircraft::LIGHT]); - } else if (entity->m_class == "ww2_fighter") { - ai_plane->SetPerformance(&FGAIAircraft::settings[FGAIAircraft::WW2_FIGHTER]); - } else if (entity->m_class == "jet_transport") { - ai_plane->SetPerformance(&FGAIAircraft::settings[FGAIAircraft::JET_TRANSPORT]); - } else if (entity->m_class == "jet_fighter") { - ai_plane->SetPerformance(&FGAIAircraft::settings[FGAIAircraft::JET_FIGHTER]); - } else if (entity->m_class == "tanker") { - ai_plane->SetPerformance(&FGAIAircraft::settings[FGAIAircraft::JET_TRANSPORT]); - ai_plane->SetTanker(true); + while(ai_list_itr != ai_list.end()) { + + if ((*ai_list_itr)->getDie()) { + tmgr->release((*ai_list_itr)->getID()); + --mNumAiModels; + --(mNumAiTypeModels[(*ai_list_itr)->getType()]); + FGAIBase *base = (*ai_list_itr).get(); + SGPropertyNode *props = base->_getProps(); + + props->setBoolValue("valid", false); + base->unbind(); + + // for backward compatibility reset properties, so that aircraft, + // which don't know the property, keep working + // TODO: remove after a while + props->setIntValue("id", -1); + props->setBoolValue("radar/in-range", false); + props->setIntValue("refuel/tanker", false); + + ai_list_itr = ai_list.erase(ai_list_itr); } else { - ai_plane->SetPerformance(&FGAIAircraft::settings[FGAIAircraft::JET_TRANSPORT]); - } - ai_plane->setHeading(entity->heading); - ai_plane->setSpeed(entity->speed); - ai_plane->setPath(entity->path.c_str()); - ai_plane->setAltitude(entity->altitude); - ai_plane->setLongitude(entity->longitude); - ai_plane->setLatitude(entity->latitude); - ai_plane->setBank(entity->roll); - - if ( entity->fp ) { - ai_plane->SetFlightPlan(entity->fp); + fetchUserState(); + if ((*ai_list_itr)->isa(FGAIBase::otThermal)) { + FGAIBase *base = (*ai_list_itr).get(); + processThermal((FGAIThermal*)base); + } else { + (*ai_list_itr)->update(_dt); + } + ++ai_list_itr; } + } - ai_plane->init(); - ai_plane->bind(); - return ai_plane; + thermal_lift_node->setDoubleValue( strength ); // for thermals } -void* -FGAIManager::createShip( FGAIModelEntity *entity ) { - - //cout << "creating ship" << endl; - - FGAIShip* ai_ship = new FGAIShip(this); - ai_list.push_back(ai_ship); - ++numObjects[0]; - ++numObjects[FGAIBase::otShip]; - ai_ship->setHeading(entity->heading); - ai_ship->setSpeed(entity->speed); - ai_ship->setPath(entity->path.c_str()); - ai_ship->setAltitude(entity->altitude); - ai_ship->setLongitude(entity->longitude); - ai_ship->setLatitude(entity->latitude); - ai_ship->setBank(entity->rudder); - - if ( entity->fp ) { - ai_ship->setFlightPlan(entity->fp); +void +FGAIManager::attach(FGAIBase *model) +{ + //unsigned idx = mNumAiTypeModels[model->getType()]; + const char* typeString = model->getTypeString(); + SGPropertyNode* root = globals->get_props()->getNode("ai/models", true); + SGPropertyNode* p; + int i; + + // find free index in the property tree, if we have + // more than 10000 mp-aircrafts in the property tree we should optimize the mp-server + for (i = 0; i < 10000; i++) { + p = root->getNode(typeString, i, false); + + if (!p || !p->getBoolValue("valid", false)) + break; + + if (p->getIntValue("id",-1)==model->getID()) { + p->setStringValue("callsign","***invalid node***"); //debug only, should never set! } + } - ai_ship->init(); - ai_ship->bind(); - return ai_ship; + p = root->getNode(typeString, i, true); + model->setManager(this, p); + ai_list.push_back(model); + ++mNumAiModels; + ++(mNumAiTypeModels[model->getType()]); + model->init(model->getType()==FGAIBase::otAircraft + || model->getType()==FGAIBase::otMultiplayer + || model->getType()==FGAIBase::otStatic); + model->bind(); + p->setBoolValue("valid", true); } -void* -FGAIManager::createCarrier( FGAIModelEntity *entity ) { - - //cout << "creating carrier" << endl; - - FGAICarrier* ai_carrier = new FGAICarrier(this); - ai_list.push_back(ai_carrier); - ++numObjects[0]; - ++numObjects[FGAIBase::otShip]; - ai_carrier->setHeading(entity->heading); - ai_carrier->setSpeed(entity->speed); - ai_carrier->setPath(entity->path.c_str()); - ai_carrier->setAltitude(entity->altitude); - ai_carrier->setLongitude(entity->longitude); - ai_carrier->setLatitude(entity->latitude); - ai_carrier->setBank(entity->rudder); - ai_carrier->setSolidObjects(entity->solid_objects); - ai_carrier->setWireObjects(entity->wire_objects); - ai_carrier->setCatapultObjects(entity->catapult_objects); - ai_carrier->setRadius(entity->radius); - - if ( entity->fp ) { - ai_carrier->setFlightPlan(entity->fp); - } +void +FGAIManager::destroyObject( int ID ) { + ai_list_iterator ai_list_itr = ai_list.begin(); - ai_carrier->init(); - ai_carrier->bind(); - return ai_carrier; -} + while(ai_list_itr != ai_list.end()) { -void* -FGAIManager::createBallistic( FGAIModelEntity *entity ) { - - FGAIBallistic* ai_ballistic = new FGAIBallistic(this); - ai_list.push_back(ai_ballistic); - ++numObjects[0]; - ++numObjects[FGAIBase::otBallistic]; - ai_ballistic->setAzimuth(entity->azimuth); - ai_ballistic->setElevation(entity->elevation); - ai_ballistic->setSpeed(entity->speed); - ai_ballistic->setPath(entity->path.c_str()); - ai_ballistic->setAltitude(entity->altitude); - ai_ballistic->setLongitude(entity->longitude); - ai_ballistic->setLatitude(entity->latitude); - ai_ballistic->setDragArea(entity->eda); - ai_ballistic->setLife(entity->life); - ai_ballistic->setBuoyancy(entity->buoyancy); - ai_ballistic->setWind_from_east(entity->wind_from_east); - ai_ballistic->setWind_from_north(entity->wind_from_north); - ai_ballistic->setWind(entity->wind); - ai_ballistic->setRoll(entity->roll); - ai_ballistic->setCd(entity->cd); - ai_ballistic->setMass(entity->mass); - ai_ballistic->setStabilisation(entity->aero_stabilised); - ai_ballistic->init(); - ai_ballistic->bind(); - return ai_ballistic; -} + if ((*ai_list_itr)->getID() == ID) { + --mNumAiModels; + --(mNumAiTypeModels[(*ai_list_itr)->getType()]); + (*ai_list_itr)->unbind(); + ai_list_itr = ai_list.erase(ai_list_itr); + } else + ++ai_list_itr; + } -void* -FGAIManager::createStorm( FGAIModelEntity *entity ) { - - FGAIStorm* ai_storm = new FGAIStorm(this); - ++numObjects[0]; - ++numObjects[FGAIBase::otStorm]; - ai_storm->setHeading(entity->heading); - ai_storm->setSpeed(entity->speed); - ai_storm->setPath(entity->path.c_str()); - ai_storm->setAltitude(entity->altitude); - ai_storm->setLongitude(entity->longitude); - ai_storm->setLatitude(entity->latitude); - ai_storm->init(); - ai_storm->bind(); - ai_list.push_back(ai_storm); - return ai_storm; } -void* -FGAIManager::createThermal( FGAIModelEntity *entity ) { - - FGAIThermal* ai_thermal = new FGAIThermal(this); - ++numObjects[0]; - ++numObjects[FGAIBase::otThermal]; - ai_thermal->setLongitude(entity->longitude); - ai_thermal->setLatitude(entity->latitude); - ai_thermal->setMaxStrength(entity->strength); - ai_thermal->setDiameter(entity->diameter / 6076.11549); - ai_thermal->init(); - ai_thermal->bind(); - ai_list.push_back(ai_thermal); - return ai_thermal; +int +FGAIManager::getNumAiObjects(void) const +{ + return mNumAiModels; } -void FGAIManager::destroyObject( void* ID ) { - ai_list_itr = ai_list.begin(); - while(ai_list_itr != ai_list.end()) { - if ((*ai_list_itr)->getID() == ID) { - --numObjects[0]; - --numObjects[(*ai_list_itr)->getType()]; - (*ai_list_itr)->unbind(); - delete (*ai_list_itr); - ai_list.erase(ai_list_itr); - - break; - } - ++ai_list_itr; - } +void +FGAIManager::fetchUserState( void ) { + user_latitude = user_latitude_node->getDoubleValue(); + user_longitude = user_longitude_node->getDoubleValue(); + user_altitude = user_altitude_node->getDoubleValue(); + user_heading = user_heading_node->getDoubleValue(); + user_pitch = user_pitch_node->getDoubleValue(); + user_yaw = user_yaw_node->getDoubleValue(); + user_speed = user_speed_node->getDoubleValue() * 0.592484; + user_roll = user_roll_node->getDoubleValue(); + wind_from_east = wind_from_east_node->getDoubleValue(); + wind_from_north = wind_from_north_node->getDoubleValue(); } -// fetch the user's state every 10 sim cycles -void FGAIManager::fetchUserState( void ) { - ++dt_count; - if (dt_count == 10) { - user_latitude = fgGetDouble("/position/latitude-deg"); - user_longitude = fgGetDouble("/position/longitude-deg"); - user_altitude = fgGetDouble("/position/altitude-ft"); - user_heading = fgGetDouble("/orientation/heading-deg"); - user_pitch = fgGetDouble("/orientation/pitch-deg"); - user_yaw = fgGetDouble("/orientation/side-slip-deg"); - user_speed = fgGetDouble("/velocities/uBody-fps") * 0.592484; - dt_count = 0; - } -} +// only keep the results from the nearest thermal +void +FGAIManager::processThermal( FGAIThermal* thermal ) { + thermal->update(_dt); + if ( thermal->_getRange() < range_nearest ) { + range_nearest = thermal->_getRange(); + strength = thermal->getStrength(); + } -// only keep the results from the nearest thermal -void FGAIManager::processThermal( FGAIThermal* thermal ) { - thermal->update(_dt); - if ( thermal->_getRange() < range_nearest ) { - range_nearest = thermal->_getRange(); - strength = thermal->getStrength(); - } } -void FGAIManager::processScenario( string &filename ) { - FGAIScenario* s = new FGAIScenario( filename ); - for (int i=0;inEntries();i++) { - FGAIModelEntity* en = s->getNextEntry(); - if (en) { - if ( en->m_type == "aircraft") { - createAircraft( en ); +void +FGAIManager::processScenario( const string &filename ) { + + SGPropertyNode_ptr scenarioTop = loadScenarioFile(filename); + + if (!scenarioTop) + return; + + SGPropertyNode* scenarios = scenarioTop->getChild("scenario"); + + if (!scenarios) + return; + + for (int i = 0; i < scenarios->nChildren(); i++) { + SGPropertyNode* scEntry = scenarios->getChild(i); + + if (strcmp(scEntry->getName(), "entry")) + continue; + std::string type = scEntry->getStringValue("type", "aircraft"); + + if (type == "tanker") { // refueling scenarios + FGAITanker* tanker = new FGAITanker; + tanker->readFromScenario(scEntry); + attach(tanker); + + } else if (type == "wingman") { + FGAIWingman* wingman = new FGAIWingman; + wingman->readFromScenario(scEntry); + attach(wingman); + + } else if (type == "aircraft") { + FGAIAircraft* aircraft = new FGAIAircraft; + aircraft->readFromScenario(scEntry); + attach(aircraft); + + } else if (type == "ship") { + FGAIShip* ship = new FGAIShip; + ship->readFromScenario(scEntry); + attach(ship); + + } else if (type == "carrier") { + FGAICarrier* carrier = new FGAICarrier; + carrier->readFromScenario(scEntry); + attach(carrier); - } else if ( en->m_type == "ship") { - createShip( en ); + } else if (type == "groundvehicle") { + FGAIGroundVehicle* groundvehicle = new FGAIGroundVehicle; + groundvehicle->readFromScenario(scEntry); + attach(groundvehicle); - } else if ( en->m_type == "carrier") { - createCarrier( en ); + } else if (type == "thunderstorm") { + FGAIStorm* storm = new FGAIStorm; + storm->readFromScenario(scEntry); + attach(storm); - } else if ( en->m_type == "thunderstorm") { - createStorm( en ); + } else if (type == "thermal") { + FGAIThermal* thermal = new FGAIThermal; + thermal->readFromScenario(scEntry); + attach(thermal); - } else if ( en->m_type == "thermal") { - createThermal( en ); + } else if (type == "ballistic") { + FGAIBallistic* ballistic = new FGAIBallistic; + ballistic->readFromScenario(scEntry); + attach(ballistic); + + } else if (type == "static") { + FGAIStatic* aistatic = new FGAIStatic; + aistatic->readFromScenario(scEntry); + attach(aistatic); + } - } else if ( en->m_type == "ballistic") { - createBallistic( en ); - } } - } - delete s; } -// This code keeps track of models that have already been loaded -// Eventually we'd prbably need to find a way to keep track of models -// that are unloaded again -ssgBranch * FGAIManager::getModel(const string& path) +SGPropertyNode_ptr +FGAIManager::loadScenarioFile(const std::string& filename) { - ModelVecIterator i = loadedModels.begin(); - while (i != loadedModels.end()) - { - if (i->getPath() == path) - return i->getModelId(); - i++; + SGPath path(globals->get_fg_root()); + path.append("AI/" + filename + ".xml"); + try { + SGPropertyNode_ptr root = new SGPropertyNode; + readProperties(path.str(), root); + return root; + } catch (const sg_exception &) { + SG_LOG(SG_GENERAL, SG_DEBUG, "Incorrect path specified for AI " + "scenario: \"" << path.str() << "\""); + return 0; } - return 0; } -void FGAIManager::setModel(const string& path, ssgBranch *model) +bool +FGAIManager::getStartPosition(const string& id, const string& pid, + SGGeod& geodPos, double& hdng, SGVec3d& uvw) { - loadedModels.push_back(FGModelID(path,model)); + bool found = false; + SGPropertyNode* root = fgGetNode("sim/ai", true); + if (!root->getNode("enabled", true)->getBoolValue()) + return found; + + for (int i = 0 ; (!found) && i < root->nChildren() ; i++) { + SGPropertyNode *aiEntry = root->getChild( i ); + if ( !strcmp( aiEntry->getName(), "scenario" ) ) { + string filename = aiEntry->getStringValue(); + SGPropertyNode_ptr scenarioTop = loadScenarioFile(filename); + if (scenarioTop) { + SGPropertyNode* scenarios = scenarioTop->getChild("scenario"); + if (scenarios) { + for (int i = 0; i < scenarios->nChildren(); i++) { + SGPropertyNode* scEntry = scenarios->getChild(i); + std::string type = scEntry->getStringValue("type"); + std::string pnumber = scEntry->getStringValue("pennant-number"); + std::string name = scEntry->getStringValue("name"); + if (type == "carrier" && (pnumber == id || name == id)) { + osg::ref_ptr carrier = new FGAICarrier; + carrier->readFromScenario(scEntry); + + if (carrier->getParkPosition(pid, geodPos, hdng, uvw)) { + found = true; + break; + } + } + } + } + } + } + } + return found; } +const FGAIBase * +FGAIManager::calcCollision(double alt, double lat, double lon, double fuse_range) +{ + // we specify tgt extent (ft) according to the AIObject type + double tgt_ht[] = {0, 50 ,100, 250, 0, 100, 0, 0, 50, 50, 50}; + double tgt_length[] = {0, 100, 200, 750, 0, 50, 0, 0, 200, 100, 100}; + ai_list_iterator ai_list_itr = ai_list.begin(); + ai_list_iterator end = ai_list.end(); + + while (ai_list_itr != end) { + double tgt_alt = (*ai_list_itr)->_getAltitude(); + int type = (*ai_list_itr)->getType(); + tgt_ht[type] += fuse_range; + + if (fabs(tgt_alt - alt) > tgt_ht[type] || type == FGAIBase::otBallistic + || type == FGAIBase::otStorm || type == FGAIBase::otThermal ) { + SG_LOG(SG_GENERAL, SG_DEBUG, "AIManager: skipping " + << fabs(tgt_alt - alt) + << " " + << type + ); + ++ai_list_itr; + continue; + } + + double tgt_lat = (*ai_list_itr)->_getLatitude(); + double tgt_lon = (*ai_list_itr)->_getLongitude(); + int id = (*ai_list_itr)->getID(); + + double range = calcRange(lat, lon, tgt_lat, tgt_lon); + + SG_LOG(SG_GENERAL, SG_DEBUG, "AIManager: AI list size " + << ai_list.size() + << " type " << type + << " ID " << id + << " range " << range + //<< " bearing " << bearing + << " alt " << tgt_alt + ); + + tgt_length[type] += fuse_range; + + if (range < tgt_length[type]){ + SG_LOG(SG_GENERAL, SG_DEBUG, "AIManager: HIT! " + << " type " << type + << " ID " << id + << " range " << range + << " alt " << tgt_alt + ); + return (*ai_list_itr).get(); + } + ++ai_list_itr; + } + return 0; +} + +double +FGAIManager::calcRange(double lat, double lon, double lat2, double lon2) const +{ + double course, az2, distance; + + //calculate the bearing and range of the second pos from the first + geo_inverse_wgs_84(lat, lon, lat2, lon2, &course, &az2, &distance); + distance *= SG_METER_TO_FEET; + return distance; +} //end AIManager.cxx