From c453d1a0cc3010b7169889ce76ad42a0938b64ea Mon Sep 17 00:00:00 2001 From: jmt Date: Wed, 9 Dec 2009 18:16:36 +0000 Subject: [PATCH 01/13] Fix reset crash, thanks to Joe: make findNextWithPartial, and the route-manager, robust about people setting NULL / empty airport idents. --- src/Autopilot/route_mgr.cxx | 12 ++++++++++-- src/Navaids/positioned.cxx | 4 ++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/Autopilot/route_mgr.cxx b/src/Autopilot/route_mgr.cxx index 313cd33ed..bbd2db96b 100644 --- a/src/Autopilot/route_mgr.cxx +++ b/src/Autopilot/route_mgr.cxx @@ -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); + } } diff --git a/src/Navaids/positioned.cxx b/src/Navaids/positioned.cxx index f3e683fb9..8130b54b9 100644 --- a/src/Navaids/positioned.cxx +++ b/src/Navaids/positioned.cxx @@ -694,6 +694,10 @@ FGPositioned::findClosestN(const SGGeod& aPos, unsigned int aN, double aCutoffNm 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. From 35cb89626e75d2bf5b80cf746b11bb83b3deeb52 Mon Sep 17 00:00:00 2001 From: jmt Date: Wed, 9 Dec 2009 18:18:35 +0000 Subject: [PATCH 02/13] As discussed on the mailing list, make the 'nearest' GPS command use scratch lat/lon as the search origin if they are valid. --- src/Instrumentation/gps.cxx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Instrumentation/gps.cxx b/src/Instrumentation/gps.cxx index 4477dd4ed..57b67b455 100644 --- a/src/Instrumentation/gps.cxx +++ b/src/Instrumentation/gps.cxx @@ -1428,10 +1428,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; From 34bbc6d974423aac314f4576b3a8a015b045e4ff Mon Sep 17 00:00:00 2001 From: James Turner Date: Thu, 17 Dec 2009 12:36:51 +0000 Subject: [PATCH 03/13] Rewrite the spatial index to use a sparse octree on the cartesian coordinates of items. * Fixes errors selecting 'nearest' items, including duplicates * Stable behaviour around the poles and dateline * Decently efficient, even for 'everything within 500nm' queries --- src/Instrumentation/testgps.cxx | 17 +- src/Navaids/positioned.cxx | 531 ++++++++++++++++++++------------ 2 files changed, 358 insertions(+), 190 deletions(-) diff --git a/src/Instrumentation/testgps.cxx b/src/Instrumentation/testgps.cxx index 64060041c..cc73bded0 100644 --- a/src/Instrumentation/testgps.cxx +++ b/src/Instrumentation/testgps.cxx @@ -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; iident() << "/" << l[i]->name()); + } + + //l = FGPositioned::findWithinRange(egph->geod(), 500.0, &af); + //for (unsigned int i=0; iident() << "/" << 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 diff --git a/src/Navaids/positioned.cxx b/src/Navaids/positioned.cxx index 8130b54b9..bc08c4ed1 100644 --- a/src/Navaids/positioned.cxx +++ b/src/Navaids/positioned.cxx @@ -25,9 +25,7 @@ #include #include #include // for sort -#include // for char-traits toupper - -#include +#include #include #include @@ -36,6 +34,7 @@ #include #include #include +#include #include "positioned.hxx" @@ -45,62 +44,337 @@ typedef std::pairtype() == 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 BucketEntry; -typedef std::map 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 SGBoxd; + +template +inline bool +intersects(const SGVec3& v, const SGBox& 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 Ordered +{ +public: + Ordered(const T& v, double x) : + _order(x), + _inner(v) + { + } + + Ordered(const Ordered& a) : + _order(a._order), + _inner(a._inner) + { + } + + Ordered& operator=(const Ordered& a) + { + _order = a._order; + _inner = a._inner; + return *this; + } + + bool operator<(const Ordered& other) const + { + return _order < other._order; + } + + bool operator>(const Ordered& 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 OrderedNode; +typedef std::greater 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, FNPQCompare> FindNearestPQueue; + +typedef Ordered OrderedPositioned; +typedef std::vector 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&) + { + std::vector > results; + 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 + + aResults.push_back(OrderedPositioned(p, d2)); + } + } +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(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(global_spatialOctree, 0)); + double cut = aCutoffM * aCutoffM; + + while (aResults.size() < aN) { + if (pq.empty()) { + break; + } + + Node* nd = pq.top().get(); + pq.pop(); + + nd->visit(aPos, cut, aFilter, results, pq); + } // of queue iteration + + // sort by distance + std::sort(results.begin(), results.end()); + // 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(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 + + // sort by distance + std::sort(results.begin(), results.end()); + unsigned int numResults = results.size(); + + // copy results out + aResults.resize(numResults); + for (unsigned int r=0; rname(), 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(-earthExtent, earthExtent)); + } + Octree::global_spatialOctree->insert(aPos); } static void @@ -148,88 +425,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 +512,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 +748,7 @@ FGPositioned::Type FGPositioned::typeFromName(const std::string& aName) // aliases {"waypoint", WAYPOINT}, {"apt", AIRPORT}, + {"arpt", AIRPORT}, {"any", INVALID}, {"all", INVALID}, @@ -656,8 +807,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 +827,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,7 +839,9 @@ 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 @@ -789,8 +942,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"); From 0637cba24a25d01572778fa81745bf62afcedf9e Mon Sep 17 00:00:00 2001 From: James Turner Date: Thu, 17 Dec 2009 18:19:14 +0000 Subject: [PATCH 04/13] Fix findNearest termination - ensure we expand all octree nodes that might contain closer results than the current list. --- src/Navaids/positioned.cxx | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/src/Navaids/positioned.cxx b/src/Navaids/positioned.cxx index bc08c4ed1..bbd044306 100644 --- a/src/Navaids/positioned.cxx +++ b/src/Navaids/positioned.cxx @@ -210,7 +210,9 @@ public: FGPositioned::Filter* aFilter, FindNearestResults& aResults, FindNearestPQueue&) { - std::vector > results; + 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()); @@ -228,8 +230,21 @@ public: } } // 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; @@ -321,9 +336,14 @@ void findNearestN(const SGVec3d& aPos, unsigned int aN, double aCutoffM, FGPosit pq.push(Ordered(global_spatialOctree, 0)); double cut = aCutoffM * aCutoffM; - while (aResults.size() < aN) { - if (pq.empty()) { + 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(); @@ -332,12 +352,9 @@ void findNearestN(const SGVec3d& aPos, unsigned int aN, double aCutoffM, FGPosit nd->visit(aPos, cut, aFilter, results, pq); } // of queue iteration - // sort by distance - std::sort(results.begin(), results.end()); // 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; rvisit(aPos, rng, aFilter, results, pq); } // of queue iteration - // sort by distance - std::sort(results.begin(), results.end()); unsigned int numResults = results.size(); - // copy results out aResults.resize(numResults); for (unsigned int r=0; r Date: Sat, 19 Dec 2009 18:12:00 +0000 Subject: [PATCH 05/13] Add runway ILS frequency to Nasal airportinfo() query, as discussed on the list. --- src/Scripting/NasalSys.cxx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Scripting/NasalSys.cxx b/src/Scripting/NasalSys.cxx index 502a406b2..c10c4d058 100644 --- a/src/Scripting/NasalSys.cxx +++ b/src/Scripting/NasalSys.cxx @@ -32,6 +32,7 @@ #include
#include
#include +#include #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", 3, naNum(rwy->ILS()->get_freq() / 100.0)); + } + #undef HASHSET naHash_set(rwys, rwyid, rwydata); } From 88dc75695b62481fd656d6f1b41a36251845e420 Mon Sep 17 00:00:00 2001 From: jmt Date: Sat, 19 Dec 2009 18:51:48 +0000 Subject: [PATCH 06/13] Probably best if the string and its length agree. :) --- src/Scripting/NasalSys.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Scripting/NasalSys.cxx b/src/Scripting/NasalSys.cxx index c10c4d058..fcde853a8 100644 --- a/src/Scripting/NasalSys.cxx +++ b/src/Scripting/NasalSys.cxx @@ -596,7 +596,7 @@ static naRef f_airportinfo(naContext c, naRef me, int argc, naRef* args) HASHSET("stopway", 7, naNum(rwy->stopwayM())); if (rwy->ILS()) { - HASHSET("ils-frequency-mhz", 3, naNum(rwy->ILS()->get_freq() / 100.0)); + HASHSET("ils-frequency-mhz", 17, naNum(rwy->ILS()->get_freq() / 100.0)); } #undef HASHSET From 45d0e14cad43042825b991ab2f96530d20417f3f Mon Sep 17 00:00:00 2001 From: jmt Date: Sun, 20 Dec 2009 16:13:20 +0000 Subject: [PATCH 07/13] JSD: Make false courses work for more than one instance of navradio. --- src/Instrumentation/navradio.cxx | 16 ++++++++++------ src/Instrumentation/navradio.hxx | 6 +++--- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/Instrumentation/navradio.cxx b/src/Instrumentation/navradio.cxx index 40a33844b..c8c959937 100644 --- a/src/Instrumentation/navradio.cxx +++ b/src/Instrumentation/navradio.cxx @@ -156,7 +156,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() ); @@ -217,9 +216,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(&_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); @@ -532,7 +536,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 +608,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 diff --git a/src/Instrumentation/navradio.hxx b/src/Instrumentation/navradio.hxx index 88f67685a..82e1498bd 100644 --- a/src/Instrumentation/navradio.hxx +++ b/src/Instrumentation/navradio.hxx @@ -120,6 +120,9 @@ 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 int play_count; @@ -161,9 +164,6 @@ class FGNavRadio : public SGSubsystem double _gsNeedleDeflection; double _gsNeedleDeflectionNorm; - // realism setting, are false courses and GS lobes enabled? - bool _falseCoursesEnabled; - SGSharedPtr _sgr; bool updateWithPower(double aDt); From d3d17d9ec0e238d5550af7e8cac5e73e3d2c18cc Mon Sep 17 00:00:00 2001 From: jmt Date: Sun, 20 Dec 2009 18:08:57 +0000 Subject: [PATCH 08/13] Fix GPS SGPropertyNode tie() handling, as suggested by John Denker. --- src/Instrumentation/gps.cxx | 271 +++++++++++++++++++----------------- src/Instrumentation/gps.hxx | 31 ++++- 2 files changed, 171 insertions(+), 131 deletions(-) diff --git a/src/Instrumentation/gps.cxx b/src/Instrumentation/gps.cxx index 57b67b455..0343a19d2 100644 --- a/src/Instrumentation/gps.cxx +++ b/src/Instrumentation/gps.cxx @@ -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(aRef, &SGGeod::getLongitudeDeg, &SGGeod::setLongitudeDeg)); - aNode->tie(latStr, SGRawValueMethods(aRef, &SGGeod::getLatitudeDeg, &SGGeod::setLatitudeDeg)); - - if (altStr) { - aNode->tie(altStr, SGRawValueMethods(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(aRef, &SGGeod::getLongitudeDeg, NULL)); - aNode->tie(latStr, SGRawValueMethods(aRef, &SGGeod::getLatitudeDeg, NULL)); - - if (altStr) { - aNode->tie(altStr, SGRawValueMethods(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(&_turnRate)); - aCfgNode->tie("turn-anticipation", SGRawValuePointer(&_enableTurnAnticipation)); - aCfgNode->tie("wpt-alert-time", SGRawValuePointer(&_waypointAlertTime)); - aCfgNode->tie("tune-nav-radio-to-ref-vor", SGRawValuePointer(&_tuneRadio1ToRefVor)); - aCfgNode->tie("min-runway-length-ft", SGRawValuePointer(&_minRunwayLengthFt)); - aCfgNode->tie("hard-surface-runways-only", SGRawValuePointer(&_requireHardSurface)); + aOwner->tie(aCfg, "turn-rate-deg-sec", SGRawValuePointer(&_turnRate)); - aCfgNode->tie("course-source", SGRawValueMethods + aOwner->tie(aCfg, "turn-anticipation", SGRawValuePointer(&_enableTurnAnticipation)); + aOwner->tie(aCfg, "wpt-alert-time", SGRawValuePointer(&_waypointAlertTime)); + aOwner->tie(aCfg, "tune-nav-radio-to-ref-vor", SGRawValuePointer(&_tuneRadio1ToRefVor)); + aOwner->tie(aCfg, "min-runway-length-ft", SGRawValuePointer(&_minRunwayLengthFt)); + aOwner->tie(aCfg, "hard-surface-runways-only", SGRawValuePointer(&_requireHardSurface)); + + aOwner->tie(aCfg, "course-source", SGRawValueMethods (*this, &GPS::Config::getCourseSource, &GPS::Config::setCourseSource)); - aCfgNode->tie("cdi-max-deflection-nm", SGRawValuePointer(&_cdiMaxDeflectionNm)); - aCfgNode->tie("drive-autopilot", SGRawValuePointer(&_driveAutopilot)); + aOwner->tie(aCfg, "cdi-max-deflection-nm", SGRawValuePointer(&_cdiMaxDeflectionNm)); + aOwner->tie(aCfg, "drive-autopilot", SGRawValuePointer(&_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(*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 - (*this, &GPS::getVerticalSpeed, NULL)); - node->tie("indicated-track-true-deg", SGRawValueMethods - (*this, &GPS::getTrueTrack, NULL)); - node->tie("indicated-track-magnetic-deg", SGRawValueMethods - (*this, &GPS::getMagTrack, NULL)); - node->tie("indicated-ground-speed-kt", SGRawValueMethods - (*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(*this, &GPS::getMode, NULL)); - node->tie("command", SGRawValueMethods(*this, &GPS::getCommand, &GPS::setCommand)); - - _scratchNode = node->getChild("scratch", 0, true); - tieSGGeod(_scratchNode, _scratchPos, "longitude-deg", "latitude-deg", "altitude-ft"); - _scratchNode->tie("valid", SGRawValueMethods(*this, &GPS::getScratchValid, NULL)); - _scratchNode->tie("distance-nm", SGRawValueMethods(*this, &GPS::getScratchDistance, NULL)); - _scratchNode->tie("true-bearing-deg", SGRawValueMethods(*this, &GPS::getScratchTrueBearing, NULL)); - _scratchNode->tie("mag-bearing-deg", SGRawValueMethods(*this, &GPS::getScratchMagBearing, NULL)); - _scratchNode->tie("has-next", SGRawValueMethods(*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 - (*this, &GPS::getWP0Ident, NULL)); - wp0_node->tie("name", SGRawValueMethods - (*this, &GPS::getWP0Name, NULL)); - - tieSGGeodReadOnly(wp1_node, _wp1_position, "longitude-deg", "latitude-deg", "altitude-ft"); - wp1_node->tie("ID", SGRawValueMethods - (*this, &GPS::getWP1Ident, NULL)); - wp1_node->tie("name", SGRawValueMethods - (*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 - (*this, &GPS::getWP1Distance, NULL)); - wp1_node->tie("bearing-true-deg", SGRawValueMethods - (*this, &GPS::getWP1Bearing, NULL)); - wp1_node->tie("bearing-mag-deg", SGRawValueMethods - (*this, &GPS::getWP1MagBearing, NULL)); - wp1_node->tie("TTW-sec", SGRawValueMethods - (*this, &GPS::getWP1TTW, NULL)); - wp1_node->tie("TTW", SGRawValueMethods - (*this, &GPS::getWP1TTWString, NULL)); - - wp1_node->tie("course-deviation-deg", SGRawValueMethods - (*this, &GPS::getWP1CourseDeviation, NULL)); - wp1_node->tie("course-error-nm", SGRawValueMethods - (*this, &GPS::getWP1CourseErrorNm, NULL)); - wp1_node->tie("to-flag", SGRawValueMethods - (*this, &GPS::getWP1ToFlag, NULL)); - wp1_node->tie("from-flag", SGRawValueMethods - (*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(*this, &GPS::getLegDistance, NULL)); - wp_node->tie("leg-true-course-deg", SGRawValueMethods(*this, &GPS::getLegCourse, NULL)); - wp_node->tie("leg-mag-course-deg", SGRawValueMethods(*this, &GPS::getLegMagCourse, NULL)); - wp_node->tie("alt-dist-ratio", SGRawValueMethods(*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 - (*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 + (*this, &GPS::getSelectedCourse, NULL)); + + + tieSGGeodReadOnly(_gpsNode, _indicated_pos, "indicated-longitude-deg", + "indicated-latitude-deg", "indicated-altitude-ft"); + + tie(_gpsNode, "indicated-vertical-speed", SGRawValueMethods + (*this, &GPS::getVerticalSpeed, NULL)); + tie(_gpsNode, "indicated-track-true-deg", SGRawValueMethods + (*this, &GPS::getTrueTrack, NULL)); + tie(_gpsNode, "indicated-track-magnetic-deg", SGRawValueMethods + (*this, &GPS::getMagTrack, NULL)); + tie(_gpsNode, "indicated-ground-speed-kt", SGRawValueMethods + (*this, &GPS::getGroundspeedKts, NULL)); + +// command system + tie(_gpsNode, "mode", SGRawValueMethods(*this, &GPS::getMode, NULL)); + tie(_gpsNode, "command", SGRawValueMethods(*this, &GPS::getCommand, &GPS::setCommand)); + + tieSGGeod(_scratchNode, _scratchPos, "longitude-deg", "latitude-deg", "altitude-ft"); + tie(_scratchNode, "valid", SGRawValueMethods(*this, &GPS::getScratchValid, NULL)); + tie(_scratchNode, "distance-nm", SGRawValueMethods(*this, &GPS::getScratchDistance, NULL)); + tie(_scratchNode, "true-bearing-deg", SGRawValueMethods(*this, &GPS::getScratchTrueBearing, NULL)); + tie(_scratchNode, "mag-bearing-deg", SGRawValueMethods(*this, &GPS::getScratchMagBearing, NULL)); + tie(_scratchNode, "has-next", SGRawValueMethods(*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 + (*this, &GPS::getWP0Ident, NULL)); + tie(wp0_node, "name", SGRawValueMethods + (*this, &GPS::getWP0Name, NULL)); + + tieSGGeodReadOnly(wp1_node, _wp1_position, "longitude-deg", "latitude-deg", "altitude-ft"); + tie(wp1_node, "ID", SGRawValueMethods + (*this, &GPS::getWP1Ident, NULL)); + tie(wp1_node, "name", SGRawValueMethods + (*this, &GPS::getWP1Name, NULL)); + + tie(wp1_node, "distance-nm", SGRawValueMethods + (*this, &GPS::getWP1Distance, NULL)); + tie(wp1_node, "bearing-true-deg", SGRawValueMethods + (*this, &GPS::getWP1Bearing, NULL)); + tie(wp1_node, "bearing-mag-deg", SGRawValueMethods + (*this, &GPS::getWP1MagBearing, NULL)); + tie(wp1_node, "TTW-sec", SGRawValueMethods + (*this, &GPS::getWP1TTW, NULL)); + tie(wp1_node, "TTW", SGRawValueMethods + (*this, &GPS::getWP1TTWString, NULL)); + + tie(wp1_node, "course-deviation-deg", SGRawValueMethods + (*this, &GPS::getWP1CourseDeviation, NULL)); + tie(wp1_node, "course-error-nm", SGRawValueMethods + (*this, &GPS::getWP1CourseErrorNm, NULL)); + tie(wp1_node, "to-flag", SGRawValueMethods + (*this, &GPS::getWP1ToFlag, NULL)); + tie(wp1_node, "from-flag", SGRawValueMethods + (*this, &GPS::getWP1FromFlag, NULL)); + +// leg properties (only valid in DTO/LEG modes, not OBS) + tie(wp_node, "leg-distance-nm", SGRawValueMethods(*this, &GPS::getLegDistance, NULL)); + tie(wp_node, "leg-true-course-deg", SGRawValueMethods(*this, &GPS::getLegCourse, NULL)); + tie(wp_node, "leg-mag-course-deg", SGRawValueMethods(*this, &GPS::getLegMagCourse, NULL)); + tie(wp_node, "alt-dist-ratio", SGRawValueMethods(*this, &GPS::getAltDistanceRatio, NULL)); + +// navradio slaving properties + tie(_gpsNode, "cdi-deflection", SGRawValueMethods + (*this, &GPS::getCDIDeflection)); +} + +void +GPS::unbind() +{ + for (unsigned int t=0; t<_tiedNodes.size(); ++t) { + _tiedNodes[t]->untie(); + } + _tiedNodes.clear(); +} + void GPS::clearOutput() { @@ -1768,4 +1763,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(aRef, &SGGeod::getLongitudeDeg, &SGGeod::setLongitudeDeg)); + tie(aNode, latStr, SGRawValueMethods(aRef, &SGGeod::getLatitudeDeg, &SGGeod::setLatitudeDeg)); + + if (altStr) { + tie(aNode, altStr, SGRawValueMethods(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(aRef, &SGGeod::getLongitudeDeg, NULL)); + tie(aNode, latStr, SGRawValueMethods(aRef, &SGGeod::getLatitudeDeg, NULL)); + + if (altStr) { + tie(aNode, altStr, SGRawValueMethods(aRef, &SGGeod::getElevationFt, NULL)); + } +} + // end of gps.cxx diff --git a/src/Instrumentation/gps.hxx b/src/Instrumentation/gps.hxx index e745418d6..76596773a 100644 --- a/src/Instrumentation/gps.hxx +++ b/src/Instrumentation/gps.hxx @@ -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 + void tie(SGPropertyNode* aNode, const char* aRelPath, const SGRawValue& 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 _tiedNodes; }; From 52905875f914898be0e6ec8cd61aff1f660988e4 Mon Sep 17 00:00:00 2001 From: jmt Date: Sun, 20 Dec 2009 18:20:07 +0000 Subject: [PATCH 09/13] Fix airportinfo() ILS frequency listing to use underscores. --- src/Scripting/NasalSys.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Scripting/NasalSys.cxx b/src/Scripting/NasalSys.cxx index fcde853a8..3b1d99529 100644 --- a/src/Scripting/NasalSys.cxx +++ b/src/Scripting/NasalSys.cxx @@ -596,7 +596,7 @@ static naRef f_airportinfo(naContext c, naRef me, int argc, naRef* args) HASHSET("stopway", 7, naNum(rwy->stopwayM())); if (rwy->ILS()) { - HASHSET("ils-frequency-mhz", 17, naNum(rwy->ILS()->get_freq() / 100.0)); + HASHSET("ils_frequency_mhz", 17, naNum(rwy->ILS()->get_freq() / 100.0)); } #undef HASHSET From 053d405a5cead6c160d9037ddfde3bbffb24d7e9 Mon Sep 17 00:00:00 2001 From: jmt Date: Sun, 20 Dec 2009 22:19:32 +0000 Subject: [PATCH 10/13] GPS: make 'loadRouteWaypoint' robust about bad scratch/index values. --- src/Instrumentation/gps.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Instrumentation/gps.cxx b/src/Instrumentation/gps.cxx index 0343a19d2..1dd72d9fc 100644 --- a/src/Instrumentation/gps.cxx +++ b/src/Instrumentation/gps.cxx @@ -1381,7 +1381,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(); } From 8cfdfb21a5dde213d5abe3de56ec122ec774ee3c Mon Sep 17 00:00:00 2001 From: jmt Date: Tue, 22 Dec 2009 12:05:52 +0000 Subject: [PATCH 11/13] GPS / route-manager: only drive autopilot true-heading in LEG mode, for compatibility with the old behaviour. --- src/Instrumentation/gps.cxx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Instrumentation/gps.cxx b/src/Instrumentation/gps.cxx index 1dd72d9fc..3303b669c 100644 --- a/src/Instrumentation/gps.cxx +++ b/src/Instrumentation/gps.cxx @@ -1003,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() From 7a007d9638761132cf81743f41b8fdd1aa5f7ee2 Mon Sep 17 00:00:00 2001 From: jmt Date: Thu, 24 Dec 2009 16:11:39 +0000 Subject: [PATCH 12/13] OSG ref_ptr fix: use .get() explicitly. --- src/Instrumentation/groundradar.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Instrumentation/groundradar.cxx b/src/Instrumentation/groundradar.cxx index 937fa2614..56c7d0902 100644 --- a/src/Instrumentation/groundradar.cxx +++ b/src/Instrumentation/groundradar.cxx @@ -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() ) ); From 38e76a175e310984da6eced54a0ad9bb7ad8c35f Mon Sep 17 00:00:00 2001 From: jmt Date: Sat, 26 Dec 2009 14:08:37 +0000 Subject: [PATCH 13/13] Navradio: constructor cleanup, tie/untie helper, and a new 'operable' property to make it clear when the radio is functioning. --- src/Instrumentation/navradio.cxx | 73 ++++++-------------------------- src/Instrumentation/navradio.hxx | 18 ++++++++ 2 files changed, 32 insertions(+), 59 deletions(-) diff --git a/src/Instrumentation/navradio.cxx b/src/Instrumentation/navradio.cxx index c8c959937..7a8215f3e 100644 --- a/src/Instrumentation/navradio.cxx +++ b/src/Instrumentation/navradio.cxx @@ -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), @@ -169,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); } @@ -190,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); @@ -267,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(&_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); @@ -289,13 +237,18 @@ FGNavRadio::init () void FGNavRadio::bind () { - + tie("dme-in-range", SGRawValuePointer(&_dmeInRange)); + tie("operable", SGRawValueMethods(*this, &FGNavRadio::isOperable, NULL)); } void FGNavRadio::unbind () { + for (unsigned int t=0; t<_tiedNodes.size(); ++t) { + _tiedNodes[t]->untie(); + } + _tiedNodes.clear(); } @@ -387,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 { @@ -419,6 +373,7 @@ void FGNavRadio::clearOutputs() from_flag_node->setBoolValue( false ); _dmeInRange = false; + _operable = false; } void FGNavRadio::updateReceiver(double dt) diff --git a/src/Instrumentation/navradio.hxx b/src/Instrumentation/navradio.hxx index 82e1498bd..fe724a3bd 100644 --- a/src/Instrumentation/navradio.hxx +++ b/src/Instrumentation/navradio.hxx @@ -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; @@ -125,6 +126,7 @@ class FGNavRadio : public SGSubsystem // internal (private) values + bool _operable; ///< is the unit serviceable, on, powered, etc int play_count; time_t last_time; FGNavRecordPtr _navaid; @@ -165,6 +167,7 @@ class FGNavRadio : public SGSubsystem double _gsNeedleDeflectionNorm; SGSharedPtr _sgr; + std::vector _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 + void tie(const char* aRelPath, const SGRawValue& aRawValue) + { + SGPropertyNode* nd = _radio_node->getNode(aRelPath, true); + _tiedNodes.push_back(nd); + nd->tie(aRawValue); + } public: FGNavRadio(SGPropertyNode *node);