Merge branches 'jmt/spatial', 'jmt/ref_ptr', 'jmt/navradio' and 'jmt/gps'
This commit is contained in:
commit
3836abcf4b
9 changed files with 623 additions and 397 deletions
|
@ -775,7 +775,11 @@ const char* FGRouteMgr::getDepartureName() const
|
|||
|
||||
void FGRouteMgr::setDepartureICAO(const char* aIdent)
|
||||
{
|
||||
_departure = FGAirport::findByIdent(aIdent);
|
||||
if ((aIdent == NULL) || (strlen(aIdent) < 4)) {
|
||||
_departure = NULL;
|
||||
} else {
|
||||
_departure = FGAirport::findByIdent(aIdent);
|
||||
}
|
||||
}
|
||||
|
||||
const char* FGRouteMgr::getDestinationICAO() const
|
||||
|
@ -798,6 +802,10 @@ const char* FGRouteMgr::getDestinationName() const
|
|||
|
||||
void FGRouteMgr::setDestinationICAO(const char* aIdent)
|
||||
{
|
||||
_destination = FGAirport::findByIdent(aIdent);
|
||||
if ((aIdent == NULL) || (strlen(aIdent) < 4)) {
|
||||
_destination = NULL;
|
||||
} else {
|
||||
_destination = FGAirport::findByIdent(aIdent);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -74,28 +74,6 @@ SGGeod SGGeodProperty::get() const
|
|||
}
|
||||
}
|
||||
|
||||
static void tieSGGeod(SGPropertyNode* aNode, SGGeod& aRef,
|
||||
const char* lonStr, const char* latStr, const char* altStr)
|
||||
{
|
||||
aNode->tie(lonStr, SGRawValueMethods<SGGeod, double>(aRef, &SGGeod::getLongitudeDeg, &SGGeod::setLongitudeDeg));
|
||||
aNode->tie(latStr, SGRawValueMethods<SGGeod, double>(aRef, &SGGeod::getLatitudeDeg, &SGGeod::setLatitudeDeg));
|
||||
|
||||
if (altStr) {
|
||||
aNode->tie(altStr, SGRawValueMethods<SGGeod, double>(aRef, &SGGeod::getElevationFt, &SGGeod::setElevationFt));
|
||||
}
|
||||
}
|
||||
|
||||
static void tieSGGeodReadOnly(SGPropertyNode* aNode, SGGeod& aRef,
|
||||
const char* lonStr, const char* latStr, const char* altStr)
|
||||
{
|
||||
aNode->tie(lonStr, SGRawValueMethods<SGGeod, double>(aRef, &SGGeod::getLongitudeDeg, NULL));
|
||||
aNode->tie(latStr, SGRawValueMethods<SGGeod, double>(aRef, &SGGeod::getLatitudeDeg, NULL));
|
||||
|
||||
if (altStr) {
|
||||
aNode->tie(altStr, SGRawValueMethods<SGGeod, double>(aRef, &SGGeod::getElevationFt, NULL));
|
||||
}
|
||||
}
|
||||
|
||||
static const char* makeTTWString(double TTW)
|
||||
{
|
||||
if ((TTW <= 0.0) || (TTW >= 356400.5)) { // 99 hours
|
||||
|
@ -220,20 +198,21 @@ GPS::Config::Config() :
|
|||
_extCourseSource = fgGetNode("/instrumentation/nav[0]/radials/selected-deg", true);
|
||||
}
|
||||
|
||||
void GPS::Config::init(SGPropertyNode* aCfgNode)
|
||||
void GPS::Config::bind(GPS* aOwner, SGPropertyNode* aCfg)
|
||||
{
|
||||
aCfgNode->tie("turn-rate-deg-sec", SGRawValuePointer<double>(&_turnRate));
|
||||
aCfgNode->tie("turn-anticipation", SGRawValuePointer<bool>(&_enableTurnAnticipation));
|
||||
aCfgNode->tie("wpt-alert-time", SGRawValuePointer<double>(&_waypointAlertTime));
|
||||
aCfgNode->tie("tune-nav-radio-to-ref-vor", SGRawValuePointer<bool>(&_tuneRadio1ToRefVor));
|
||||
aCfgNode->tie("min-runway-length-ft", SGRawValuePointer<double>(&_minRunwayLengthFt));
|
||||
aCfgNode->tie("hard-surface-runways-only", SGRawValuePointer<bool>(&_requireHardSurface));
|
||||
aOwner->tie(aCfg, "turn-rate-deg-sec", SGRawValuePointer<double>(&_turnRate));
|
||||
|
||||
aCfgNode->tie("course-source", SGRawValueMethods<GPS::Config, const char*>
|
||||
aOwner->tie(aCfg, "turn-anticipation", SGRawValuePointer<bool>(&_enableTurnAnticipation));
|
||||
aOwner->tie(aCfg, "wpt-alert-time", SGRawValuePointer<double>(&_waypointAlertTime));
|
||||
aOwner->tie(aCfg, "tune-nav-radio-to-ref-vor", SGRawValuePointer<bool>(&_tuneRadio1ToRefVor));
|
||||
aOwner->tie(aCfg, "min-runway-length-ft", SGRawValuePointer<double>(&_minRunwayLengthFt));
|
||||
aOwner->tie(aCfg, "hard-surface-runways-only", SGRawValuePointer<bool>(&_requireHardSurface));
|
||||
|
||||
aOwner->tie(aCfg, "course-source", SGRawValueMethods<GPS::Config, const char*>
|
||||
(*this, &GPS::Config::getCourseSource, &GPS::Config::setCourseSource));
|
||||
|
||||
aCfgNode->tie("cdi-max-deflection-nm", SGRawValuePointer<double>(&_cdiMaxDeflectionNm));
|
||||
aCfgNode->tie("drive-autopilot", SGRawValuePointer<bool>(&_driveAutopilot));
|
||||
aOwner->tie(aCfg, "cdi-max-deflection-nm", SGRawValuePointer<double>(&_cdiMaxDeflectionNm));
|
||||
aOwner->tie(aCfg, "drive-autopilot", SGRawValuePointer<bool>(&_driveAutopilot));
|
||||
}
|
||||
|
||||
const char*
|
||||
|
@ -291,6 +270,9 @@ GPS::GPS ( SGPropertyNode *node) :
|
|||
_anticipateTurn(false),
|
||||
_inTurn(false)
|
||||
{
|
||||
string branch = "/instrumentation/" + _name;
|
||||
_gpsNode = fgGetNode(branch.c_str(), _num, true );
|
||||
_scratchNode = _gpsNode->getChild("scratch", 0, true);
|
||||
}
|
||||
|
||||
GPS::~GPS ()
|
||||
|
@ -300,112 +282,39 @@ GPS::~GPS ()
|
|||
void
|
||||
GPS::init ()
|
||||
{
|
||||
_routeMgr = (FGRouteMgr*) globals->get_subsystem("route-manager");
|
||||
assert(_routeMgr);
|
||||
_routeMgr = (FGRouteMgr*) globals->get_subsystem("route-manager");
|
||||
assert(_routeMgr);
|
||||
|
||||
string branch;
|
||||
branch = "/instrumentation/" + _name;
|
||||
|
||||
SGPropertyNode *node = fgGetNode(branch.c_str(), _num, true );
|
||||
_config.init(node->getChild("config", 0, true));
|
||||
|
||||
_position.init("/position/longitude-deg", "/position/latitude-deg", "/position/altitude-ft");
|
||||
_magvar_node = fgGetNode("/environment/magnetic-variation-deg", true);
|
||||
_serviceable_node = node->getChild("serviceable", 0, true);
|
||||
_serviceable_node = _gpsNode->getChild("serviceable", 0, true);
|
||||
_serviceable_node->setBoolValue(true);
|
||||
_electrical_node = fgGetNode("/systems/electrical/outputs/gps", true);
|
||||
|
||||
// basic GPS outputs
|
||||
node->tie("selected-course-deg", SGRawValueMethods<GPS, double>(*this, &GPS::getSelectedCourse, NULL));
|
||||
_raim_node = _gpsNode->getChild("raim", 0, true);
|
||||
_odometer_node = _gpsNode->getChild("odometer", 0, true);
|
||||
_trip_odometer_node = _gpsNode->getChild("trip-odometer", 0, true);
|
||||
_true_bug_error_node = _gpsNode->getChild("true-bug-error-deg", 0, true);
|
||||
_magnetic_bug_error_node = _gpsNode->getChild("magnetic-bug-error-deg", 0, true);
|
||||
|
||||
_raim_node = node->getChild("raim", 0, true);
|
||||
|
||||
tieSGGeodReadOnly(node, _indicated_pos, "indicated-longitude-deg",
|
||||
"indicated-latitude-deg", "indicated-altitude-ft");
|
||||
|
||||
node->tie("indicated-vertical-speed", SGRawValueMethods<GPS, double>
|
||||
(*this, &GPS::getVerticalSpeed, NULL));
|
||||
node->tie("indicated-track-true-deg", SGRawValueMethods<GPS, double>
|
||||
(*this, &GPS::getTrueTrack, NULL));
|
||||
node->tie("indicated-track-magnetic-deg", SGRawValueMethods<GPS, double>
|
||||
(*this, &GPS::getMagTrack, NULL));
|
||||
node->tie("indicated-ground-speed-kt", SGRawValueMethods<GPS, double>
|
||||
(*this, &GPS::getGroundspeedKts, NULL));
|
||||
|
||||
_odometer_node = node->getChild("odometer", 0, true);
|
||||
_trip_odometer_node = node->getChild("trip-odometer", 0, true);
|
||||
_true_bug_error_node = node->getChild("true-bug-error-deg", 0, true);
|
||||
_magnetic_bug_error_node = node->getChild("magnetic-bug-error-deg", 0, true);
|
||||
|
||||
// command system
|
||||
node->tie("mode", SGRawValueMethods<GPS, const char*>(*this, &GPS::getMode, NULL));
|
||||
node->tie("command", SGRawValueMethods<GPS, const char*>(*this, &GPS::getCommand, &GPS::setCommand));
|
||||
|
||||
_scratchNode = node->getChild("scratch", 0, true);
|
||||
tieSGGeod(_scratchNode, _scratchPos, "longitude-deg", "latitude-deg", "altitude-ft");
|
||||
_scratchNode->tie("valid", SGRawValueMethods<GPS, bool>(*this, &GPS::getScratchValid, NULL));
|
||||
_scratchNode->tie("distance-nm", SGRawValueMethods<GPS, double>(*this, &GPS::getScratchDistance, NULL));
|
||||
_scratchNode->tie("true-bearing-deg", SGRawValueMethods<GPS, double>(*this, &GPS::getScratchTrueBearing, NULL));
|
||||
_scratchNode->tie("mag-bearing-deg", SGRawValueMethods<GPS, double>(*this, &GPS::getScratchMagBearing, NULL));
|
||||
_scratchNode->tie("has-next", SGRawValueMethods<GPS, bool>(*this, &GPS::getScratchHasNext, NULL));
|
||||
_scratchValid = false;
|
||||
|
||||
// waypoint data (including various historical things)
|
||||
SGPropertyNode *wp_node = node->getChild("wp", 0, true);
|
||||
SGPropertyNode *wp0_node = wp_node->getChild("wp", 0, true);
|
||||
// waypoints
|
||||
SGPropertyNode *wp_node = _gpsNode->getChild("wp", 0, true);
|
||||
SGPropertyNode *wp1_node = wp_node->getChild("wp", 1, true);
|
||||
|
||||
tieSGGeodReadOnly(wp0_node, _wp0_position, "longitude-deg", "latitude-deg", "altitude-ft");
|
||||
wp0_node->tie("ID", SGRawValueMethods<GPS, const char*>
|
||||
(*this, &GPS::getWP0Ident, NULL));
|
||||
wp0_node->tie("name", SGRawValueMethods<GPS, const char*>
|
||||
(*this, &GPS::getWP0Name, NULL));
|
||||
|
||||
tieSGGeodReadOnly(wp1_node, _wp1_position, "longitude-deg", "latitude-deg", "altitude-ft");
|
||||
wp1_node->tie("ID", SGRawValueMethods<GPS, const char*>
|
||||
(*this, &GPS::getWP1Ident, NULL));
|
||||
wp1_node->tie("name", SGRawValueMethods<GPS, const char*>
|
||||
(*this, &GPS::getWP1Name, NULL));
|
||||
|
||||
// for compatability, alias selected course down to wp/wp[1]/desired-course-deg
|
||||
SGPropertyNode* wp1Crs = wp1_node->getChild("desired-course-deg", 0, true);
|
||||
wp1Crs->alias(node->getChild("selected-course-deg"));
|
||||
wp1Crs->alias(_gpsNode->getChild("selected-course-deg"));
|
||||
|
||||
// _true_wp1_bearing_error_node =
|
||||
// wp1_node->getChild("true-bearing-error-deg", 0, true);
|
||||
// _magnetic_wp1_bearing_error_node =
|
||||
// wp1_node->getChild("magnetic-bearing-error-deg", 0, true);
|
||||
|
||||
wp1_node->tie("distance-nm", SGRawValueMethods<GPS, double>
|
||||
(*this, &GPS::getWP1Distance, NULL));
|
||||
wp1_node->tie("bearing-true-deg", SGRawValueMethods<GPS, double>
|
||||
(*this, &GPS::getWP1Bearing, NULL));
|
||||
wp1_node->tie("bearing-mag-deg", SGRawValueMethods<GPS, double>
|
||||
(*this, &GPS::getWP1MagBearing, NULL));
|
||||
wp1_node->tie("TTW-sec", SGRawValueMethods<GPS, double>
|
||||
(*this, &GPS::getWP1TTW, NULL));
|
||||
wp1_node->tie("TTW", SGRawValueMethods<GPS, const char*>
|
||||
(*this, &GPS::getWP1TTWString, NULL));
|
||||
|
||||
wp1_node->tie("course-deviation-deg", SGRawValueMethods<GPS, double>
|
||||
(*this, &GPS::getWP1CourseDeviation, NULL));
|
||||
wp1_node->tie("course-error-nm", SGRawValueMethods<GPS, double>
|
||||
(*this, &GPS::getWP1CourseErrorNm, NULL));
|
||||
wp1_node->tie("to-flag", SGRawValueMethods<GPS, bool>
|
||||
(*this, &GPS::getWP1ToFlag, NULL));
|
||||
wp1_node->tie("from-flag", SGRawValueMethods<GPS, bool>
|
||||
(*this, &GPS::getWP1FromFlag, NULL));
|
||||
|
||||
_tracking_bug_node = node->getChild("tracking-bug", 0, true);
|
||||
_tracking_bug_node = _gpsNode->getChild("tracking-bug", 0, true);
|
||||
|
||||
// leg properties (only valid in DTO/LEG modes, not OBS)
|
||||
wp_node->tie("leg-distance-nm", SGRawValueMethods<GPS, double>(*this, &GPS::getLegDistance, NULL));
|
||||
wp_node->tie("leg-true-course-deg", SGRawValueMethods<GPS, double>(*this, &GPS::getLegCourse, NULL));
|
||||
wp_node->tie("leg-mag-course-deg", SGRawValueMethods<GPS, double>(*this, &GPS::getLegMagCourse, NULL));
|
||||
wp_node->tie("alt-dist-ratio", SGRawValueMethods<GPS, double>(*this, &GPS::getAltDistanceRatio, NULL));
|
||||
|
||||
// reference navid
|
||||
SGPropertyNode_ptr ref_navaid = node->getChild("ref-navaid", 0, true);
|
||||
SGPropertyNode_ptr ref_navaid = _gpsNode->getChild("ref-navaid", 0, true);
|
||||
_ref_navaid_id_node = ref_navaid->getChild("id", 0, true);
|
||||
_ref_navaid_name_node = ref_navaid->getChild("name", 0, true);
|
||||
_ref_navaid_bearing_node = ref_navaid->getChild("bearing-deg", 0, true);
|
||||
|
@ -417,8 +326,8 @@ GPS::init ()
|
|||
|
||||
// route properties
|
||||
// should these move to the route manager?
|
||||
_routeDistanceNm = node->getChild("route-distance-nm", 0, true);
|
||||
_routeETE = node->getChild("ETE", 0, true);
|
||||
_routeDistanceNm = _gpsNode->getChild("route-distance-nm", 0, true);
|
||||
_routeETE = _gpsNode->getChild("ETE", 0, true);
|
||||
_routeEditedSignal = fgGetNode("/autopilot/route-manager/signals/edited", true);
|
||||
_routeFinishedSignal = fgGetNode("/autopilot/route-manager/signals/finished", true);
|
||||
|
||||
|
@ -433,16 +342,12 @@ GPS::init ()
|
|||
_routeFinishedSignal->addChangeListener(_listener);
|
||||
|
||||
// navradio slaving properties
|
||||
node->tie("cdi-deflection", SGRawValueMethods<GPS,double>
|
||||
(*this, &GPS::getCDIDeflection));
|
||||
|
||||
SGPropertyNode* toFlag = node->getChild("to-flag", 0, true);
|
||||
SGPropertyNode* toFlag = _gpsNode->getChild("to-flag", 0, true);
|
||||
toFlag->alias(wp1_node->getChild("to-flag"));
|
||||
|
||||
SGPropertyNode* fromFlag = node->getChild("from-flag", 0, true);
|
||||
SGPropertyNode* fromFlag = _gpsNode->getChild("from-flag", 0, true);
|
||||
fromFlag->alias(wp1_node->getChild("from-flag"));
|
||||
|
||||
|
||||
// autopilot drive properties
|
||||
_apTrueHeading = fgGetNode("/autopilot/settings/true-heading-deg",true);
|
||||
_apTargetAltitudeFt = fgGetNode("/autopilot/settings/target-altitude-ft", true);
|
||||
|
@ -455,11 +360,101 @@ GPS::init ()
|
|||
}
|
||||
|
||||
// last thing, add the deprecated prop watcher
|
||||
new DeprecatedPropListener(node);
|
||||
new DeprecatedPropListener(_gpsNode);
|
||||
|
||||
clearOutput();
|
||||
}
|
||||
|
||||
void
|
||||
GPS::bind()
|
||||
{
|
||||
_config.bind(this, _gpsNode->getChild("config", 0, true));
|
||||
// basic GPS outputs
|
||||
tie(_gpsNode, "selected-course-deg", SGRawValueMethods<GPS, double>
|
||||
(*this, &GPS::getSelectedCourse, NULL));
|
||||
|
||||
|
||||
tieSGGeodReadOnly(_gpsNode, _indicated_pos, "indicated-longitude-deg",
|
||||
"indicated-latitude-deg", "indicated-altitude-ft");
|
||||
|
||||
tie(_gpsNode, "indicated-vertical-speed", SGRawValueMethods<GPS, double>
|
||||
(*this, &GPS::getVerticalSpeed, NULL));
|
||||
tie(_gpsNode, "indicated-track-true-deg", SGRawValueMethods<GPS, double>
|
||||
(*this, &GPS::getTrueTrack, NULL));
|
||||
tie(_gpsNode, "indicated-track-magnetic-deg", SGRawValueMethods<GPS, double>
|
||||
(*this, &GPS::getMagTrack, NULL));
|
||||
tie(_gpsNode, "indicated-ground-speed-kt", SGRawValueMethods<GPS, double>
|
||||
(*this, &GPS::getGroundspeedKts, NULL));
|
||||
|
||||
// command system
|
||||
tie(_gpsNode, "mode", SGRawValueMethods<GPS, const char*>(*this, &GPS::getMode, NULL));
|
||||
tie(_gpsNode, "command", SGRawValueMethods<GPS, const char*>(*this, &GPS::getCommand, &GPS::setCommand));
|
||||
|
||||
tieSGGeod(_scratchNode, _scratchPos, "longitude-deg", "latitude-deg", "altitude-ft");
|
||||
tie(_scratchNode, "valid", SGRawValueMethods<GPS, bool>(*this, &GPS::getScratchValid, NULL));
|
||||
tie(_scratchNode, "distance-nm", SGRawValueMethods<GPS, double>(*this, &GPS::getScratchDistance, NULL));
|
||||
tie(_scratchNode, "true-bearing-deg", SGRawValueMethods<GPS, double>(*this, &GPS::getScratchTrueBearing, NULL));
|
||||
tie(_scratchNode, "mag-bearing-deg", SGRawValueMethods<GPS, double>(*this, &GPS::getScratchMagBearing, NULL));
|
||||
tie(_scratchNode, "has-next", SGRawValueMethods<GPS, bool>(*this, &GPS::getScratchHasNext, NULL));
|
||||
_scratchValid = false;
|
||||
|
||||
// waypoint data (including various historical things)
|
||||
SGPropertyNode *wp_node = _gpsNode->getChild("wp", 0, true);
|
||||
SGPropertyNode *wp0_node = wp_node->getChild("wp", 0, true);
|
||||
SGPropertyNode *wp1_node = wp_node->getChild("wp", 1, true);
|
||||
|
||||
tieSGGeodReadOnly(wp0_node, _wp0_position, "longitude-deg", "latitude-deg", "altitude-ft");
|
||||
tie(wp0_node, "ID", SGRawValueMethods<GPS, const char*>
|
||||
(*this, &GPS::getWP0Ident, NULL));
|
||||
tie(wp0_node, "name", SGRawValueMethods<GPS, const char*>
|
||||
(*this, &GPS::getWP0Name, NULL));
|
||||
|
||||
tieSGGeodReadOnly(wp1_node, _wp1_position, "longitude-deg", "latitude-deg", "altitude-ft");
|
||||
tie(wp1_node, "ID", SGRawValueMethods<GPS, const char*>
|
||||
(*this, &GPS::getWP1Ident, NULL));
|
||||
tie(wp1_node, "name", SGRawValueMethods<GPS, const char*>
|
||||
(*this, &GPS::getWP1Name, NULL));
|
||||
|
||||
tie(wp1_node, "distance-nm", SGRawValueMethods<GPS, double>
|
||||
(*this, &GPS::getWP1Distance, NULL));
|
||||
tie(wp1_node, "bearing-true-deg", SGRawValueMethods<GPS, double>
|
||||
(*this, &GPS::getWP1Bearing, NULL));
|
||||
tie(wp1_node, "bearing-mag-deg", SGRawValueMethods<GPS, double>
|
||||
(*this, &GPS::getWP1MagBearing, NULL));
|
||||
tie(wp1_node, "TTW-sec", SGRawValueMethods<GPS, double>
|
||||
(*this, &GPS::getWP1TTW, NULL));
|
||||
tie(wp1_node, "TTW", SGRawValueMethods<GPS, const char*>
|
||||
(*this, &GPS::getWP1TTWString, NULL));
|
||||
|
||||
tie(wp1_node, "course-deviation-deg", SGRawValueMethods<GPS, double>
|
||||
(*this, &GPS::getWP1CourseDeviation, NULL));
|
||||
tie(wp1_node, "course-error-nm", SGRawValueMethods<GPS, double>
|
||||
(*this, &GPS::getWP1CourseErrorNm, NULL));
|
||||
tie(wp1_node, "to-flag", SGRawValueMethods<GPS, bool>
|
||||
(*this, &GPS::getWP1ToFlag, NULL));
|
||||
tie(wp1_node, "from-flag", SGRawValueMethods<GPS, bool>
|
||||
(*this, &GPS::getWP1FromFlag, NULL));
|
||||
|
||||
// leg properties (only valid in DTO/LEG modes, not OBS)
|
||||
tie(wp_node, "leg-distance-nm", SGRawValueMethods<GPS, double>(*this, &GPS::getLegDistance, NULL));
|
||||
tie(wp_node, "leg-true-course-deg", SGRawValueMethods<GPS, double>(*this, &GPS::getLegCourse, NULL));
|
||||
tie(wp_node, "leg-mag-course-deg", SGRawValueMethods<GPS, double>(*this, &GPS::getLegMagCourse, NULL));
|
||||
tie(wp_node, "alt-dist-ratio", SGRawValueMethods<GPS, double>(*this, &GPS::getAltDistanceRatio, NULL));
|
||||
|
||||
// navradio slaving properties
|
||||
tie(_gpsNode, "cdi-deflection", SGRawValueMethods<GPS,double>
|
||||
(*this, &GPS::getCDIDeflection));
|
||||
}
|
||||
|
||||
void
|
||||
GPS::unbind()
|
||||
{
|
||||
for (unsigned int t=0; t<_tiedNodes.size(); ++t) {
|
||||
_tiedNodes[t]->untie();
|
||||
}
|
||||
_tiedNodes.clear();
|
||||
}
|
||||
|
||||
void
|
||||
GPS::clearOutput()
|
||||
{
|
||||
|
@ -1008,8 +1003,12 @@ void GPS::driveAutopilot()
|
|||
return;
|
||||
}
|
||||
|
||||
// FIXME: we want to set desired track, not heading, here
|
||||
_apTrueHeading->setDoubleValue(getWP1Bearing());
|
||||
// compatability feature - allow the route-manager / GPS to drive the
|
||||
// generic autopilot heading hold *in leg mode only*
|
||||
if (_mode == "leg") {
|
||||
// FIXME: we want to set desired track, not heading, here
|
||||
_apTrueHeading->setDoubleValue(getWP1Bearing());
|
||||
}
|
||||
}
|
||||
|
||||
void GPS::wp1Changed()
|
||||
|
@ -1386,7 +1385,7 @@ void GPS::loadRouteWaypoint()
|
|||
int index = _scratchNode->getIntValue("index", -9999);
|
||||
clearScratch();
|
||||
|
||||
if (index == -9999) { // no index supplied, use current wp
|
||||
if ((index < 0) || (index >= _routeMgr->size())) { // no index supplied, use current wp
|
||||
index = _routeMgr->currentWaypoint();
|
||||
}
|
||||
|
||||
|
@ -1428,10 +1427,15 @@ void GPS::loadNearest()
|
|||
int limitCount = _scratchNode->getIntValue("max-results", 1);
|
||||
double cutoffDistance = _scratchNode->getDoubleValue("cutoff-nm", 400.0);
|
||||
|
||||
clearScratch(); // clear now, regardless of whether we find a match or not
|
||||
SGGeod searchPos = _indicated_pos;
|
||||
if (isScratchPositionValid()) {
|
||||
searchPos = _scratchPos;
|
||||
}
|
||||
|
||||
clearScratch(); // clear now, regardless of whether we find a match or not
|
||||
|
||||
_searchResults =
|
||||
FGPositioned::findClosestN(_indicated_pos, limitCount, cutoffDistance, f.get());
|
||||
FGPositioned::findClosestN(searchPos, limitCount, cutoffDistance, f.get());
|
||||
_searchResultsCached = true;
|
||||
_searchResultIndex = 0;
|
||||
_searchIsRoute = false;
|
||||
|
@ -1763,4 +1767,26 @@ void GPS::removeWaypointAtIndex(int aIndex)
|
|||
_routeMgr->pop_waypoint(aIndex);
|
||||
}
|
||||
|
||||
void GPS::tieSGGeod(SGPropertyNode* aNode, SGGeod& aRef,
|
||||
const char* lonStr, const char* latStr, const char* altStr)
|
||||
{
|
||||
tie(aNode, lonStr, SGRawValueMethods<SGGeod, double>(aRef, &SGGeod::getLongitudeDeg, &SGGeod::setLongitudeDeg));
|
||||
tie(aNode, latStr, SGRawValueMethods<SGGeod, double>(aRef, &SGGeod::getLatitudeDeg, &SGGeod::setLatitudeDeg));
|
||||
|
||||
if (altStr) {
|
||||
tie(aNode, altStr, SGRawValueMethods<SGGeod, double>(aRef, &SGGeod::getElevationFt, &SGGeod::setElevationFt));
|
||||
}
|
||||
}
|
||||
|
||||
void GPS::tieSGGeodReadOnly(SGPropertyNode* aNode, SGGeod& aRef,
|
||||
const char* lonStr, const char* latStr, const char* altStr)
|
||||
{
|
||||
tie(aNode, lonStr, SGRawValueMethods<SGGeod, double>(aRef, &SGGeod::getLongitudeDeg, NULL));
|
||||
tie(aNode, latStr, SGRawValueMethods<SGGeod, double>(aRef, &SGGeod::getLatitudeDeg, NULL));
|
||||
|
||||
if (altStr) {
|
||||
tie(aNode, altStr, SGRawValueMethods<SGGeod, double>(aRef, &SGGeod::getElevationFt, NULL));
|
||||
}
|
||||
}
|
||||
|
||||
// end of gps.cxx
|
||||
|
|
|
@ -82,7 +82,9 @@ public:
|
|||
|
||||
virtual void init ();
|
||||
virtual void update (double delta_time_sec);
|
||||
|
||||
|
||||
virtual void bind();
|
||||
virtual void unbind();
|
||||
private:
|
||||
friend class GPSListener;
|
||||
friend class SearchFilter;
|
||||
|
@ -94,8 +96,8 @@ private:
|
|||
{
|
||||
public:
|
||||
Config();
|
||||
|
||||
void init(SGPropertyNode*);
|
||||
|
||||
void bind(GPS* aOwner, SGPropertyNode* aCfg);
|
||||
|
||||
bool turnAnticipationEnabled() const
|
||||
{ return _enableTurnAnticipation; }
|
||||
|
@ -307,9 +309,28 @@ private:
|
|||
|
||||
// true-bearing-error and mag-bearing-error
|
||||
|
||||
|
||||
/**
|
||||
* Tied-properties helper, record nodes which are tied for easy un-tie-ing
|
||||
*/
|
||||
template <typename T>
|
||||
void tie(SGPropertyNode* aNode, const char* aRelPath, const SGRawValue<T>& aRawValue)
|
||||
{
|
||||
SGPropertyNode* nd = aNode->getNode(aRelPath, true);
|
||||
_tiedNodes.push_back(nd);
|
||||
nd->tie(aRawValue);
|
||||
}
|
||||
|
||||
/// helper, tie the lat/lon/elev of a SGGeod to the named children of aNode
|
||||
void tieSGGeod(SGPropertyNode* aNode, SGGeod& aRef,
|
||||
const char* lonStr, const char* latStr, const char* altStr);
|
||||
|
||||
|
||||
/// helper, tie a SGGeod to proeprties, but read-only
|
||||
void tieSGGeodReadOnly(SGPropertyNode* aNode, SGGeod& aRef,
|
||||
const char* lonStr, const char* latStr, const char* altStr);
|
||||
|
||||
// members
|
||||
SGPropertyNode_ptr _gpsNode;
|
||||
SGPropertyNode_ptr _magvar_node;
|
||||
SGPropertyNode_ptr _serviceable_node;
|
||||
SGPropertyNode_ptr _electrical_node;
|
||||
|
@ -397,6 +418,8 @@ private:
|
|||
SGPropertyNode_ptr _apTrueHeading;
|
||||
SGPropertyNode_ptr _apTargetAltitudeFt;
|
||||
SGPropertyNode_ptr _apAltitudeLock;
|
||||
|
||||
std::vector<SGPropertyNode*> _tiedNodes;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -197,7 +197,7 @@ osg::Geometry *GroundRadar::addPavementGeometry(const FGPavement* aPavement, dou
|
|||
geo_inverse_wgs_84(aTowerLat, aTowerLon, (*loopBegin)->mPos.getLatitudeDeg(), (*loopBegin)->mPos.getLongitudeDeg(), &az1, &az2, &dist_m);
|
||||
osg::Vec3 p1 = fromPolar(az1, dist_m * aScale) + osg::Vec3(TextureHalfSize, TextureHalfSize, 0);
|
||||
pts->push_back( p1 );
|
||||
polygon->setVertexArray( pts );
|
||||
polygon->setVertexArray( pts.get() );
|
||||
|
||||
polygon->addPrimitiveSet( new osg::DrawArrays( osg::PrimitiveSet::POLYGON, 0, pts->size() ) );
|
||||
|
||||
|
|
|
@ -94,56 +94,6 @@ FGNavRadio::FGNavRadio(SGPropertyNode *node) :
|
|||
lon_node(fgGetNode("/position/longitude-deg", true)),
|
||||
lat_node(fgGetNode("/position/latitude-deg", true)),
|
||||
alt_node(fgGetNode("/position/altitude-ft", true)),
|
||||
is_valid_node(NULL),
|
||||
power_btn_node(NULL),
|
||||
freq_node(NULL),
|
||||
alt_freq_node(NULL),
|
||||
sel_radial_node(NULL),
|
||||
vol_btn_node(NULL),
|
||||
ident_btn_node(NULL),
|
||||
audio_btn_node(NULL),
|
||||
backcourse_node(NULL),
|
||||
nav_serviceable_node(NULL),
|
||||
cdi_serviceable_node(NULL),
|
||||
gs_serviceable_node(NULL),
|
||||
tofrom_serviceable_node(NULL),
|
||||
dme_serviceable_node(NULL),
|
||||
fmt_freq_node(NULL),
|
||||
fmt_alt_freq_node(NULL),
|
||||
heading_node(NULL),
|
||||
radial_node(NULL),
|
||||
recip_radial_node(NULL),
|
||||
target_radial_true_node(NULL),
|
||||
target_auto_hdg_node(NULL),
|
||||
time_to_intercept(NULL),
|
||||
to_flag_node(NULL),
|
||||
from_flag_node(NULL),
|
||||
inrange_node(NULL),
|
||||
signal_quality_norm_node(NULL),
|
||||
cdi_deflection_node(NULL),
|
||||
cdi_deflection_norm_node(NULL),
|
||||
cdi_xtrack_error_node(NULL),
|
||||
cdi_xtrack_hdg_err_node(NULL),
|
||||
has_gs_node(NULL),
|
||||
loc_node(NULL),
|
||||
loc_dist_node(NULL),
|
||||
gs_deflection_node(NULL),
|
||||
gs_deflection_deg_node(NULL),
|
||||
gs_deflection_norm_node(NULL),
|
||||
gs_rate_of_climb_node(NULL),
|
||||
gs_dist_node(NULL),
|
||||
gs_inrange_node(NULL),
|
||||
nav_id_node(NULL),
|
||||
id_c1_node(NULL),
|
||||
id_c2_node(NULL),
|
||||
id_c3_node(NULL),
|
||||
id_c4_node(NULL),
|
||||
nav_slaved_to_gps_node(NULL),
|
||||
gps_cdi_deflection_node(NULL),
|
||||
gps_to_flag_node(NULL),
|
||||
gps_from_flag_node(NULL),
|
||||
gps_has_gs_node(NULL),
|
||||
gps_xtrack_error_nm_node(NULL),
|
||||
play_count(0),
|
||||
last_time(0),
|
||||
target_radial(0.0),
|
||||
|
@ -156,7 +106,6 @@ FGNavRadio::FGNavRadio(SGPropertyNode *node) :
|
|||
_name(node->getStringValue("name", "nav")),
|
||||
_num(node->getIntValue("number", 0)),
|
||||
_time_before_search_sec(-1.0),
|
||||
_falseCoursesEnabled(true),
|
||||
_sgr(NULL)
|
||||
{
|
||||
SGPath path( globals->get_fg_root() );
|
||||
|
@ -170,6 +119,10 @@ FGNavRadio::FGNavRadio(SGPropertyNode *node) :
|
|||
term_tbl = new SGInterpTable( term.str() );
|
||||
low_tbl = new SGInterpTable( low.str() );
|
||||
high_tbl = new SGInterpTable( high.str() );
|
||||
|
||||
|
||||
string branch("/instrumentation/" + _name);
|
||||
_radio_node = fgGetNode(branch.c_str(), _num, true);
|
||||
}
|
||||
|
||||
|
||||
|
@ -191,11 +144,7 @@ FGNavRadio::init ()
|
|||
|
||||
morse.init();
|
||||
|
||||
string branch;
|
||||
branch = "/instrumentation/" + _name;
|
||||
|
||||
SGPropertyNode *node = fgGetNode(branch.c_str(), _num, true );
|
||||
|
||||
SGPropertyNode* node = _radio_node.get();
|
||||
bus_power_node =
|
||||
fgGetNode(("/systems/electrical/outputs/" + _name).c_str(), true);
|
||||
|
||||
|
@ -217,9 +166,14 @@ FGNavRadio::init ()
|
|||
tofrom_serviceable_node = createServiceableProp(node, "to-from");
|
||||
dme_serviceable_node = createServiceableProp(node, "dme");
|
||||
|
||||
globals->get_props()->tie("sim/realism/false-radio-courses-enabled",
|
||||
SGRawValuePointer<bool>(&_falseCoursesEnabled));
|
||||
|
||||
falseCoursesEnabledNode =
|
||||
fgGetNode("/sim/realism/false-radio-courses-enabled");
|
||||
if (!falseCoursesEnabledNode) {
|
||||
falseCoursesEnabledNode =
|
||||
fgGetNode("/sim/realism/false-radio-courses-enabled", true);
|
||||
falseCoursesEnabledNode->setBoolValue(true);
|
||||
}
|
||||
|
||||
// frequencies
|
||||
SGPropertyNode *subnode = node->getChild("frequencies", 0, true);
|
||||
freq_node = subnode->getChild("selected-mhz", 0, true);
|
||||
|
@ -263,8 +217,6 @@ FGNavRadio::init ()
|
|||
id_c3_node = node->getChild("nav-id_asc3", 0, true);
|
||||
id_c4_node = node->getChild("nav-id_asc4", 0, true);
|
||||
|
||||
node->tie("dme-in-range", SGRawValuePointer<bool>(&_dmeInRange));
|
||||
|
||||
// gps slaving support
|
||||
nav_slaved_to_gps_node = node->getChild("slaved-to-gps", 0, true);
|
||||
gps_cdi_deflection_node = fgGetNode("/instrumentation/gps/cdi-deflection", true);
|
||||
|
@ -285,13 +237,18 @@ FGNavRadio::init ()
|
|||
void
|
||||
FGNavRadio::bind ()
|
||||
{
|
||||
|
||||
tie("dme-in-range", SGRawValuePointer<bool>(&_dmeInRange));
|
||||
tie("operable", SGRawValueMethods<FGNavRadio, bool>(*this, &FGNavRadio::isOperable, NULL));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
FGNavRadio::unbind ()
|
||||
{
|
||||
for (unsigned int t=0; t<_tiedNodes.size(); ++t) {
|
||||
_tiedNodes[t]->untie();
|
||||
}
|
||||
_tiedNodes.clear();
|
||||
}
|
||||
|
||||
|
||||
|
@ -383,7 +340,8 @@ FGNavRadio::update(double dt)
|
|||
if (power_btn_node->getBoolValue()
|
||||
&& (bus_power_node->getDoubleValue() > 1.0)
|
||||
&& nav_serviceable_node->getBoolValue() )
|
||||
{
|
||||
{
|
||||
_operable = true;
|
||||
if (nav_slaved_to_gps_node->getBoolValue()) {
|
||||
updateGPSSlaved();
|
||||
} else {
|
||||
|
@ -415,6 +373,7 @@ void FGNavRadio::clearOutputs()
|
|||
from_flag_node->setBoolValue( false );
|
||||
|
||||
_dmeInRange = false;
|
||||
_operable = false;
|
||||
}
|
||||
|
||||
void FGNavRadio::updateReceiver(double dt)
|
||||
|
@ -532,7 +491,7 @@ void FGNavRadio::updateReceiver(double dt)
|
|||
SG_NORMALIZE_RANGE(r, -180.0, 180.0);
|
||||
|
||||
if ( is_loc ) {
|
||||
if (_falseCoursesEnabled) {
|
||||
if (falseCoursesEnabledNode->getBoolValue()) {
|
||||
// The factor of 30.0 gives a period of 120 which gives us 3 cycles and six
|
||||
// zeros i.e. six courses: one front course, one back course, and four
|
||||
// false courses. Three of the six are reverse sensing.
|
||||
|
@ -604,7 +563,7 @@ void FGNavRadio::updateGlideSlope(double dt, const SGVec3d& aircraft, double sig
|
|||
double angle = atan2(dot_v, dot_h) * SGD_RADIANS_TO_DEGREES;
|
||||
double deflectionAngle = target_gs - angle;
|
||||
|
||||
if (_falseCoursesEnabled) {
|
||||
if (falseCoursesEnabledNode->getBoolValue()) {
|
||||
// Construct false glideslopes. The scale factor of 1.5
|
||||
// in the sawtooth gives a period of 6 degrees.
|
||||
// There will be zeros at 3, 6r, 9, 12r et cetera
|
||||
|
|
|
@ -47,6 +47,7 @@ class FGNavRadio : public SGSubsystem
|
|||
SGInterpTable *low_tbl;
|
||||
SGInterpTable *high_tbl;
|
||||
|
||||
SGPropertyNode_ptr _radio_node;
|
||||
SGPropertyNode_ptr lon_node;
|
||||
SGPropertyNode_ptr lat_node;
|
||||
SGPropertyNode_ptr alt_node;
|
||||
|
@ -120,8 +121,12 @@ class FGNavRadio : public SGSubsystem
|
|||
SGPropertyNode_ptr gps_xtrack_error_nm_node;
|
||||
SGPropertyNode_ptr _magvarNode;
|
||||
|
||||
// realism setting, are false courses and GS lobes enabled?
|
||||
SGPropertyNode_ptr falseCoursesEnabledNode;
|
||||
|
||||
// internal (private) values
|
||||
|
||||
bool _operable; ///< is the unit serviceable, on, powered, etc
|
||||
int play_count;
|
||||
time_t last_time;
|
||||
FGNavRecordPtr _navaid;
|
||||
|
@ -161,10 +166,8 @@ class FGNavRadio : public SGSubsystem
|
|||
double _gsNeedleDeflection;
|
||||
double _gsNeedleDeflectionNorm;
|
||||
|
||||
// realism setting, are false courses and GS lobes enabled?
|
||||
bool _falseCoursesEnabled;
|
||||
|
||||
SGSharedPtr<SGSampleGroup> _sgr;
|
||||
std::vector<SGPropertyNode*> _tiedNodes;
|
||||
|
||||
bool updateWithPower(double aDt);
|
||||
|
||||
|
@ -193,6 +196,21 @@ class FGNavRadio : public SGSubsystem
|
|||
*/
|
||||
double localizerWidth(FGNavRecord* aLOC);
|
||||
FGNavRecord* findPrimaryNavaid(const SGGeod& aPos, double aFreqMHz);
|
||||
|
||||
/// accessor for tied, read-only 'operable' property
|
||||
bool isOperable() const
|
||||
{ return _operable; }
|
||||
|
||||
/**
|
||||
* Tied-properties helper, record nodes which are tied for easy un-tie-ing
|
||||
*/
|
||||
template <typename T>
|
||||
void tie(const char* aRelPath, const SGRawValue<T>& aRawValue)
|
||||
{
|
||||
SGPropertyNode* nd = _radio_node->getNode(aRelPath, true);
|
||||
_tiedNodes.push_back(nd);
|
||||
nd->tie(aRawValue);
|
||||
}
|
||||
public:
|
||||
|
||||
FGNavRadio(SGPropertyNode *node);
|
||||
|
|
|
@ -72,6 +72,21 @@ int main(int argc, char* argv[])
|
|||
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, "hello world!");
|
||||
|
||||
const FGAirport* egph = fgFindAirportID("EGPH");
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, "egph: cart location:" << egph->cart());
|
||||
|
||||
FGAirport::AirportFilter af;
|
||||
FGPositioned::List l = FGPositioned::findClosestN(egph->geod(), 20, 2000.0, &af);
|
||||
for (unsigned int i=0; i<l.size(); ++i) {
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, "\t" << l[i]->ident() << "/" << l[i]->name());
|
||||
}
|
||||
|
||||
//l = FGPositioned::findWithinRange(egph->geod(), 500.0, &af);
|
||||
//for (unsigned int i=0; i<l.size(); ++i) {
|
||||
// SG_LOG(SG_GENERAL, SG_ALERT, "\t" << l[i]->ident() << "/" << l[i]->name());
|
||||
//}
|
||||
|
||||
|
||||
FGRouteMgr* rm = new FGRouteMgr;
|
||||
globals->add_subsystem( "route-manager", rm );
|
||||
|
||||
|
@ -84,7 +99,7 @@ int main(int argc, char* argv[])
|
|||
GPS* gps = new GPS(nd);
|
||||
globals->add_subsystem("gps", gps);
|
||||
|
||||
const FGAirport* egph = fgFindAirportID("EGPH");
|
||||
|
||||
testSetPosition(egph->geod());
|
||||
|
||||
// startup the route manager
|
||||
|
|
|
@ -25,9 +25,7 @@
|
|||
#include <map>
|
||||
#include <set>
|
||||
#include <algorithm> // for sort
|
||||
#include <locale> // for char-traits toupper
|
||||
|
||||
#include <iostream>
|
||||
#include <queue>
|
||||
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
@ -36,6 +34,7 @@
|
|||
#include <simgear/timing/timestamp.hxx>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/structure/exception.hxx>
|
||||
#include <simgear/math/SGBox.hxx>
|
||||
|
||||
#include "positioned.hxx"
|
||||
|
||||
|
@ -45,62 +44,351 @@ typedef std::pair<NamedPositionedIndex::const_iterator, NamedPositionedIndex::co
|
|||
using std::lower_bound;
|
||||
using std::upper_bound;
|
||||
|
||||
/**
|
||||
* Order positioned elements by type, then pointer address. This allows us to
|
||||
* use range searches (lower_ and upper_bound) to grab items of a particular
|
||||
* type out of bucket efficently.
|
||||
*/
|
||||
class OrderByType
|
||||
{
|
||||
public:
|
||||
bool operator()(const FGPositioned* a, const FGPositioned* b) const
|
||||
{
|
||||
if (a->type() == b->type()) return a < b;
|
||||
return a->type() < b->type();
|
||||
}
|
||||
};
|
||||
|
||||
class LowerLimitOfType
|
||||
{
|
||||
public:
|
||||
bool operator()(const FGPositioned* a, const FGPositioned::Type b) const
|
||||
{
|
||||
return a->type() < b;
|
||||
}
|
||||
|
||||
bool operator()(const FGPositioned::Type a, const FGPositioned* b) const
|
||||
{
|
||||
return a < b->type();
|
||||
}
|
||||
|
||||
// The operator below is required by VS2005 in debug mode
|
||||
bool operator()(const FGPositioned* a, const FGPositioned* b) const
|
||||
{
|
||||
return a->type() < b->type();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
typedef std::set<FGPositioned*, OrderByType> BucketEntry;
|
||||
typedef std::map<long int, BucketEntry> SpatialPositionedIndex;
|
||||
|
||||
static NamedPositionedIndex global_identIndex;
|
||||
static NamedPositionedIndex global_nameIndex;
|
||||
static SpatialPositionedIndex global_spatialIndex;
|
||||
|
||||
SpatialPositionedIndex::iterator
|
||||
bucketEntryForPositioned(FGPositioned* aPos)
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Octree
|
||||
{
|
||||
int bucketIndex = aPos->bucket().gen_index();
|
||||
SpatialPositionedIndex::iterator it = global_spatialIndex.find(bucketIndex);
|
||||
if (it != global_spatialIndex.end()) {
|
||||
return it;
|
||||
}
|
||||
|
||||
// create a new BucketEntry
|
||||
return global_spatialIndex.insert(it, std::make_pair(bucketIndex, BucketEntry()));
|
||||
|
||||
const double LEAF_SIZE = SG_NM_TO_METER * 8.0;
|
||||
const double LEAF_SIZE_SQR = LEAF_SIZE * LEAF_SIZE;
|
||||
|
||||
typedef SGBox<double> SGBoxd;
|
||||
|
||||
template<typename T1, typename T2>
|
||||
inline bool
|
||||
intersects(const SGVec3<T1>& v, const SGBox<T2>& box)
|
||||
{
|
||||
if (v[0] < box.getMin()[0])
|
||||
return false;
|
||||
if (box.getMax()[0] < v[0])
|
||||
return false;
|
||||
if (v[1] < box.getMin()[1])
|
||||
return false;
|
||||
if (box.getMax()[1] < v[1])
|
||||
return false;
|
||||
if (v[2] < box.getMin()[2])
|
||||
return false;
|
||||
if (box.getMax()[2] < v[2])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decorate an object with a double value, and use that value to order
|
||||
* items, for the purpoises of the STL algorithms
|
||||
*/
|
||||
template <class T>
|
||||
class Ordered
|
||||
{
|
||||
public:
|
||||
Ordered(const T& v, double x) :
|
||||
_order(x),
|
||||
_inner(v)
|
||||
{
|
||||
}
|
||||
|
||||
Ordered(const Ordered<T>& a) :
|
||||
_order(a._order),
|
||||
_inner(a._inner)
|
||||
{
|
||||
}
|
||||
|
||||
Ordered<T>& operator=(const Ordered<T>& a)
|
||||
{
|
||||
_order = a._order;
|
||||
_inner = a._inner;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator<(const Ordered<T>& other) const
|
||||
{
|
||||
return _order < other._order;
|
||||
}
|
||||
|
||||
bool operator>(const Ordered<T>& other) const
|
||||
{
|
||||
return _order > other._order;
|
||||
}
|
||||
|
||||
const T& get() const
|
||||
{ return _inner; }
|
||||
|
||||
double order() const
|
||||
{ return _order; }
|
||||
|
||||
private:
|
||||
double _order;
|
||||
T _inner;
|
||||
};
|
||||
|
||||
class Node;
|
||||
typedef Ordered<Node*> OrderedNode;
|
||||
typedef std::greater<OrderedNode> FNPQCompare;
|
||||
|
||||
/**
|
||||
* the priority queue is fundamental to our search algorithm. When searching,
|
||||
* we know the front of the queue is the nearest unexpanded node (to the search
|
||||
* location). The default STL pqueue returns the 'largest' item from top(), so
|
||||
* to get the smallest, we need to replace the default Compare functor (less<>)
|
||||
* with greater<>.
|
||||
*/
|
||||
typedef std::priority_queue<OrderedNode, std::vector<OrderedNode>, FNPQCompare> FindNearestPQueue;
|
||||
|
||||
typedef Ordered<FGPositioned*> OrderedPositioned;
|
||||
typedef std::vector<OrderedPositioned> FindNearestResults;
|
||||
|
||||
Node* global_spatialOctree = NULL;
|
||||
|
||||
/**
|
||||
* Octree node base class, tracks its bounding box and provides various
|
||||
* queries relating to it
|
||||
*/
|
||||
class Node
|
||||
{
|
||||
public:
|
||||
bool contains(const SGVec3d& aPos) const
|
||||
{
|
||||
return intersects(aPos, _box);
|
||||
}
|
||||
|
||||
double distSqrToNearest(const SGVec3d& aPos) const
|
||||
{
|
||||
return distSqr(aPos, getClosestPoint(aPos));
|
||||
}
|
||||
|
||||
virtual void insert(FGPositioned* aP) = 0;
|
||||
|
||||
SGVec3d getClosestPoint(const SGVec3d& aPos) const
|
||||
{
|
||||
SGVec3d r;
|
||||
|
||||
for (unsigned int i=0;i<3; ++i) {
|
||||
if (aPos[i] < _box.getMin()[i]) {
|
||||
r[i] = _box.getMin()[i];
|
||||
} else if (aPos[i] > _box.getMax()[i]) {
|
||||
r[i] = _box.getMax()[i];
|
||||
} else {
|
||||
r[i] = aPos[i];
|
||||
}
|
||||
} // of axis iteration
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
virtual void visit(const SGVec3d& aPos, double aCutoff,
|
||||
FGPositioned::Filter* aFilter,
|
||||
FindNearestResults& aResults, FindNearestPQueue&) = 0;
|
||||
protected:
|
||||
Node(const SGBoxd &aBox) :
|
||||
_box(aBox)
|
||||
{
|
||||
}
|
||||
|
||||
const SGBoxd _box;
|
||||
};
|
||||
|
||||
class Leaf : public Node
|
||||
{
|
||||
public:
|
||||
Leaf(const SGBoxd& aBox) :
|
||||
Node(aBox)
|
||||
{
|
||||
}
|
||||
|
||||
const FGPositioned::List& members() const
|
||||
{ return _members; }
|
||||
|
||||
virtual void insert(FGPositioned* aP)
|
||||
{
|
||||
_members.push_back(aP);
|
||||
}
|
||||
|
||||
virtual void visit(const SGVec3d& aPos, double aCutoff,
|
||||
FGPositioned::Filter* aFilter,
|
||||
FindNearestResults& aResults, FindNearestPQueue&)
|
||||
{
|
||||
int previousResultsSize = aResults.size();
|
||||
int addedCount = 0;
|
||||
|
||||
for (unsigned int i=0; i<_members.size(); ++i) {
|
||||
FGPositioned* p = _members[i];
|
||||
double d2 = distSqr(aPos, p->cart());
|
||||
if (d2 > aCutoff) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (aFilter) {
|
||||
if (aFilter->hasTypeRange() && !aFilter->passType(p->type())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!aFilter->pass(p)) {
|
||||
continue;
|
||||
}
|
||||
} // of have a filter
|
||||
|
||||
++addedCount;
|
||||
aResults.push_back(OrderedPositioned(p, d2));
|
||||
}
|
||||
|
||||
if (addedCount == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// keep aResults sorted
|
||||
// sort the new items, usually just one or two items
|
||||
std::sort(aResults.begin() + previousResultsSize, aResults.end());
|
||||
|
||||
// merge the two sorted ranges together - in linear time
|
||||
std::inplace_merge(aResults.begin(),
|
||||
aResults.begin() + previousResultsSize, aResults.end());
|
||||
}
|
||||
private:
|
||||
FGPositioned::List _members;
|
||||
};
|
||||
|
||||
class Branch : public Node
|
||||
{
|
||||
public:
|
||||
Branch(const SGBoxd& aBox) :
|
||||
Node(aBox)
|
||||
{
|
||||
memset(children, 0, sizeof(Node*) * 8);
|
||||
}
|
||||
|
||||
virtual void insert(FGPositioned* aP)
|
||||
{
|
||||
SGVec3d cart(aP->cart());
|
||||
assert(contains(cart));
|
||||
int childIndex = 0;
|
||||
|
||||
SGVec3d center(_box.getCenter());
|
||||
// tests must match indices in SGbox::getCorner
|
||||
if (cart.x() < center.x()) {
|
||||
childIndex += 1;
|
||||
}
|
||||
|
||||
if (cart.y() < center.y()) {
|
||||
childIndex += 2;
|
||||
}
|
||||
|
||||
if (cart.z() < center.z()) {
|
||||
childIndex += 4;
|
||||
}
|
||||
|
||||
Node* child = children[childIndex];
|
||||
if (!child) { // lazy building of children
|
||||
SGBoxd cb(boxForChild(childIndex));
|
||||
double d2 = dot(cb.getSize(), cb.getSize());
|
||||
if (d2 < LEAF_SIZE_SQR) {
|
||||
child = new Leaf(cb);
|
||||
} else {
|
||||
child = new Branch(cb);
|
||||
}
|
||||
|
||||
children[childIndex] = child;
|
||||
}
|
||||
|
||||
child->insert(aP);
|
||||
}
|
||||
|
||||
virtual void visit(const SGVec3d& aPos, double aCutoff,
|
||||
FGPositioned::Filter*,
|
||||
FindNearestResults&, FindNearestPQueue& aQ)
|
||||
{
|
||||
for (unsigned int i=0; i<8; ++i) {
|
||||
if (!children[i]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
double d2 = children[i]->distSqrToNearest(aPos);
|
||||
if (d2 > aCutoff) {
|
||||
continue; // exceeded cutoff
|
||||
}
|
||||
|
||||
aQ.push(Ordered<Node*>(children[i], d2));
|
||||
} // of child iteration
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
/**
|
||||
* Return the box for a child touching the specified corner
|
||||
*/
|
||||
SGBoxd boxForChild(unsigned int aCorner) const
|
||||
{
|
||||
SGBoxd r(_box.getCenter());
|
||||
r.expandBy(_box.getCorner(aCorner));
|
||||
return r;
|
||||
}
|
||||
|
||||
Node* children[8];
|
||||
};
|
||||
|
||||
void findNearestN(const SGVec3d& aPos, unsigned int aN, double aCutoffM, FGPositioned::Filter* aFilter, FGPositioned::List& aResults)
|
||||
{
|
||||
aResults.clear();
|
||||
FindNearestPQueue pq;
|
||||
FindNearestResults results;
|
||||
pq.push(Ordered<Node*>(global_spatialOctree, 0));
|
||||
double cut = aCutoffM * aCutoffM;
|
||||
|
||||
while (!pq.empty()) {
|
||||
if (!results.empty()) {
|
||||
// terminate the search if we have sufficent results, and we are
|
||||
// sure no node still on the queue contains a closer match
|
||||
double furthestResultOrder = results.back().order();
|
||||
if ((results.size() >= aN) && (furthestResultOrder < pq.top().order())) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Node* nd = pq.top().get();
|
||||
pq.pop();
|
||||
|
||||
nd->visit(aPos, cut, aFilter, results, pq);
|
||||
} // of queue iteration
|
||||
|
||||
// depending on leaf population, we may have (slighty) more results
|
||||
// than requested
|
||||
unsigned int numResults = std::min((unsigned int) results.size(), aN);
|
||||
// copy results out
|
||||
aResults.resize(numResults);
|
||||
for (unsigned int r=0; r<numResults; ++r) {
|
||||
aResults[r] = results[r].get();
|
||||
}
|
||||
}
|
||||
|
||||
void findAllWithinRange(const SGVec3d& aPos, double aRangeM, FGPositioned::Filter* aFilter, FGPositioned::List& aResults)
|
||||
{
|
||||
aResults.clear();
|
||||
FindNearestPQueue pq;
|
||||
FindNearestResults results;
|
||||
pq.push(Ordered<Node*>(global_spatialOctree, 0));
|
||||
double rng = aRangeM * aRangeM;
|
||||
|
||||
while (!pq.empty()) {
|
||||
Node* nd = pq.top().get();
|
||||
pq.pop();
|
||||
|
||||
nd->visit(aPos, rng, aFilter, results, pq);
|
||||
} // of queue iteration
|
||||
|
||||
unsigned int numResults = results.size();
|
||||
// copy results out
|
||||
aResults.resize(numResults);
|
||||
for (unsigned int r=0; r<numResults; ++r) {
|
||||
aResults[r] = results[r].get();
|
||||
}
|
||||
}
|
||||
|
||||
} // of namespace Octree
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void
|
||||
addToIndices(FGPositioned* aPos)
|
||||
{
|
||||
|
@ -115,9 +403,12 @@ addToIndices(FGPositioned* aPos)
|
|||
std::make_pair(aPos->name(), aPos));
|
||||
}
|
||||
|
||||
|
||||
SpatialPositionedIndex::iterator it = bucketEntryForPositioned(aPos);
|
||||
it->second.insert(aPos);
|
||||
if (!Octree::global_spatialOctree) {
|
||||
double RADIUS_EARTH_M = 7000 * 1000.0; // 7000km is plenty
|
||||
SGVec3d earthExtent(RADIUS_EARTH_M, RADIUS_EARTH_M, RADIUS_EARTH_M);
|
||||
Octree::global_spatialOctree = new Octree::Branch(SGBox<double>(-earthExtent, earthExtent));
|
||||
}
|
||||
Octree::global_spatialOctree->insert(aPos);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -148,88 +439,6 @@ removeFromIndices(FGPositioned* aPos)
|
|||
++it;
|
||||
} // of multimap walk
|
||||
}
|
||||
|
||||
SpatialPositionedIndex::iterator sit = bucketEntryForPositioned(aPos);
|
||||
sit->second.erase(aPos);
|
||||
}
|
||||
|
||||
static void
|
||||
spatialFilterInBucket(const SGBucket& aBucket, FGPositioned::Filter* aFilter, FGPositioned::List& aResult)
|
||||
{
|
||||
SpatialPositionedIndex::const_iterator it;
|
||||
it = global_spatialIndex.find(aBucket.gen_index());
|
||||
if (it == global_spatialIndex.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
BucketEntry::const_iterator l = it->second.begin();
|
||||
BucketEntry::const_iterator u = it->second.end();
|
||||
|
||||
if (!aFilter) { // pass everything
|
||||
aResult.insert(aResult.end(), l, u);
|
||||
return;
|
||||
}
|
||||
|
||||
if (aFilter->hasTypeRange()) {
|
||||
// avoid many calls to the filter hook
|
||||
l = lower_bound(it->second.begin(), it->second.end(), aFilter->minType(), LowerLimitOfType());
|
||||
u = upper_bound(l, it->second.end(), aFilter->maxType(), LowerLimitOfType());
|
||||
}
|
||||
|
||||
for ( ; l != u; ++l) {
|
||||
if ((*aFilter)(*l)) {
|
||||
aResult.push_back(*l);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
spatialFind(const SGGeod& aPos, double aRange,
|
||||
FGPositioned::Filter* aFilter, FGPositioned::List& aResult)
|
||||
{
|
||||
SGBucket buck(aPos);
|
||||
double lat = aPos.getLatitudeDeg(),
|
||||
lon = aPos.getLongitudeDeg();
|
||||
|
||||
int bx = (int)( aRange*SG_NM_TO_METER / buck.get_width_m() / 2);
|
||||
int by = (int)( aRange*SG_NM_TO_METER / buck.get_height_m() / 2 );
|
||||
|
||||
// loop over bucket range
|
||||
for ( int i=-bx; i<=bx; i++) {
|
||||
for ( int j=-by; j<=by; j++) {
|
||||
spatialFilterInBucket(sgBucketOffset(lon, lat, i, j), aFilter, aResult);
|
||||
} // of j-iteration
|
||||
} // of i-iteration
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
class RangePredictate
|
||||
{
|
||||
public:
|
||||
RangePredictate(const SGGeod& aOrigin, double aRange) :
|
||||
mOrigin(SGVec3d::fromGeod(aOrigin)),
|
||||
mRangeSqr(aRange * aRange)
|
||||
{ ; }
|
||||
|
||||
bool operator()(const FGPositionedRef& aPos)
|
||||
{
|
||||
double dSqr = distSqr(aPos->cart(), mOrigin);
|
||||
return (dSqr > mRangeSqr);
|
||||
}
|
||||
|
||||
private:
|
||||
SGVec3d mOrigin;
|
||||
double mRangeSqr;
|
||||
};
|
||||
|
||||
static void
|
||||
filterListByRange(const SGGeod& aPos, double aRange, FGPositioned::List& aResult)
|
||||
{
|
||||
RangePredictate pred(aPos, aRange * SG_NM_TO_METER);
|
||||
FGPositioned::List::iterator newEnd;
|
||||
newEnd = std::remove_if(aResult.begin(), aResult.end(), pred);
|
||||
aResult.erase(newEnd, aResult.end());
|
||||
}
|
||||
|
||||
class DistanceOrdering
|
||||
|
@ -317,51 +526,6 @@ namedFindClosest(const NamedPositionedIndex& aIndex, const std::string& aName,
|
|||
return result;
|
||||
}
|
||||
|
||||
static FGPositioned::List
|
||||
spatialGetClosest(const SGGeod& aPos, unsigned int aN, double aCutoffNm, FGPositioned::Filter* aFilter)
|
||||
{
|
||||
FGPositioned::List result;
|
||||
int radius = 1; // start at 1, radius 0 is handled explicitly
|
||||
SGBucket buck;
|
||||
double lat = aPos.getLatitudeDeg(),
|
||||
lon = aPos.getLongitudeDeg();
|
||||
// final cutoff is in metres, and scaled to account for testing the corners
|
||||
// of the 'box' instead of the centre of each edge
|
||||
double cutoffM = aCutoffNm * SG_NM_TO_METER * 1.5;
|
||||
|
||||
// base case, simplifes loop to do it seperately here
|
||||
spatialFilterInBucket(sgBucketOffset(lon, lat, 0, 0), aFilter, result);
|
||||
|
||||
for (;result.size() < aN; ++radius) {
|
||||
// cutoff check
|
||||
double az1, az2, d1, d2;
|
||||
SGGeodesy::inverse(aPos, sgBucketOffset(lon, lat, -radius, -radius).get_center(), az1, az2, d1);
|
||||
SGGeodesy::inverse(aPos, sgBucketOffset(lon, lat, radius, radius).get_center(), az1, az2, d2);
|
||||
|
||||
if ((d1 > cutoffM) && (d2 > cutoffM)) {
|
||||
//std::cerr << "spatialGetClosest terminating due to range cutoff" << std::endl;
|
||||
break;
|
||||
}
|
||||
|
||||
FGPositioned::List hits;
|
||||
for ( int i=-radius; i<=radius; i++) {
|
||||
spatialFilterInBucket(sgBucketOffset(lon, lat, i, -radius), aFilter, hits);
|
||||
spatialFilterInBucket(sgBucketOffset(lon, lat, -radius, i), aFilter, hits);
|
||||
spatialFilterInBucket(sgBucketOffset(lon, lat, i, radius), aFilter, hits);
|
||||
spatialFilterInBucket(sgBucketOffset(lon, lat, radius, i), aFilter, hits);
|
||||
}
|
||||
|
||||
result.insert(result.end(), hits.begin(), hits.end()); // append
|
||||
} // of outer loop
|
||||
|
||||
sortByDistance(aPos, result);
|
||||
if (result.size() > aN) {
|
||||
result.resize(aN); // truncate at requested number of matches
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class OrderByName
|
||||
|
@ -598,6 +762,7 @@ FGPositioned::Type FGPositioned::typeFromName(const std::string& aName)
|
|||
// aliases
|
||||
{"waypoint", WAYPOINT},
|
||||
{"apt", AIRPORT},
|
||||
{"arpt", AIRPORT},
|
||||
{"any", INVALID},
|
||||
{"all", INVALID},
|
||||
|
||||
|
@ -656,8 +821,8 @@ FGPositioned::List
|
|||
FGPositioned::findWithinRange(const SGGeod& aPos, double aRangeNm, Filter* aFilter)
|
||||
{
|
||||
List result;
|
||||
spatialFind(aPos, aRangeNm, aFilter, result);
|
||||
filterListByRange(aPos, aRangeNm, result);
|
||||
Octree::findAllWithinRange(SGVec3d::fromGeod(aPos),
|
||||
aRangeNm * SG_NM_TO_METER, aFilter, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -676,7 +841,7 @@ FGPositioned::findAllWithNameSortedByRange(const std::string& aName, const SGGeo
|
|||
FGPositionedRef
|
||||
FGPositioned::findClosest(const SGGeod& aPos, double aCutoffNm, Filter* aFilter)
|
||||
{
|
||||
FGPositioned::List l(spatialGetClosest(aPos, 1, aCutoffNm, aFilter));
|
||||
List l(findClosestN(aPos, 1, aCutoffNm, aFilter));
|
||||
if (l.empty()) {
|
||||
return NULL;
|
||||
}
|
||||
|
@ -688,12 +853,18 @@ FGPositioned::findClosest(const SGGeod& aPos, double aCutoffNm, Filter* aFilter)
|
|||
FGPositioned::List
|
||||
FGPositioned::findClosestN(const SGGeod& aPos, unsigned int aN, double aCutoffNm, Filter* aFilter)
|
||||
{
|
||||
return spatialGetClosest(aPos, aN, aCutoffNm, aFilter);
|
||||
List result;
|
||||
Octree::findNearestN(SGVec3d::fromGeod(aPos), aN, aCutoffNm * SG_NM_TO_METER, aFilter, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
FGPositionedRef
|
||||
FGPositioned::findNextWithPartialId(FGPositionedRef aCur, const std::string& aId, Filter* aFilter)
|
||||
{
|
||||
if (aId.empty()) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
std::string id(boost::to_upper_copy(aId));
|
||||
|
||||
// It is essential to bound our search, to avoid iterating all the way to the end of the database.
|
||||
|
@ -785,8 +956,8 @@ findClosestWithPartial(const SGGeod& aPos, FGPositioned::Filter* aFilter, int aO
|
|||
{
|
||||
// why aOffset +2 ? at offset=3, we want the fourth search result, but also
|
||||
// to know if the fifth result exists (to set aNext flag for iterative APIs)
|
||||
FGPositioned::List matches =
|
||||
spatialGetClosest(aPos, aOffset + 2, 1000.0, aFilter);
|
||||
FGPositioned::List matches;
|
||||
Octree::findNearestN(SGVec3d::fromGeod(aPos), aOffset + 2, 1000 * SG_NM_TO_METER, aFilter, matches);
|
||||
|
||||
if ((int) matches.size() <= aOffset) {
|
||||
SG_LOG(SG_GENERAL, SG_INFO, "findClosestWithPartial, couldn't match enough with prefix");
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include <Main/fg_props.hxx>
|
||||
#include <Main/util.hxx>
|
||||
#include <Scenery/scenery.hxx>
|
||||
#include <Navaids/navrecord.hxx>
|
||||
|
||||
#include "NasalSys.hxx"
|
||||
|
||||
|
@ -593,6 +594,11 @@ static naRef f_airportinfo(naContext c, naRef me, int argc, naRef* args)
|
|||
HASHSET("width", 5, naNum(rwy->widthM()));
|
||||
HASHSET("threshold", 9, naNum(rwy->displacedThresholdM()));
|
||||
HASHSET("stopway", 7, naNum(rwy->stopwayM()));
|
||||
|
||||
if (rwy->ILS()) {
|
||||
HASHSET("ils_frequency_mhz", 17, naNum(rwy->ILS()->get_freq() / 100.0));
|
||||
}
|
||||
|
||||
#undef HASHSET
|
||||
naHash_set(rwys, rwyid, rwydata);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue