#include <simgear/scene/model/model.hxx>
#include <simgear/structure/exception.hxx>
#include <simgear/misc/sg_path.hxx>
+#include <simgear/misc/strutils.hxx>
#include <simgear/math/sg_geodesy.hxx>
#include <sstream>
return osg::Vec2(r.x(), r.y());
}
+class NavDisplay::CacheListener : public SGPropertyChangeListener
+{
+public:
+ CacheListener(NavDisplay *nd) :
+ _nd(nd)
+ {}
+
+ virtual void valueChanged (SGPropertyNode * prop)
+ {
+ _nd->invalidatePositionedCache();
+ SG_LOG(SG_INSTR, SG_INFO, "invalidating NavDisplay cache");
+ }
+private:
+ NavDisplay* _nd;
+};
+
///////////////////////////////////////////////////////////////////
class SymbolDef
void
NavDisplay::init ()
{
+ _cachedItemsValid = false;
+ _cacheListener.reset(new CacheListener(this));
+
_serviceable_node = _Instrument->getNode("serviceable", true);
-
+ _rangeNode = _Instrument->getNode("range", true);
+ _rangeNode->setDoubleValue(40.0);
+ _rangeNode->addChangeListener(_cacheListener.get());
+
// texture name to use in 2D and 3D instruments
_texture_path = _Instrument->getStringValue("radar-texture-path",
"Aircraft/Instruments/Textures/od_wxradar.rgb");
_odg = (FGODGauge *)imgr->get_subsystem("od_gauge");
_odg->setSize(_Instrument->getIntValue("texture-size", 512));
-
- _user_lat_node = fgGetNode("/position/latitude-deg", true);
- _user_lon_node = fgGetNode("/position/longitude-deg", true);
- _user_alt_node = fgGetNode("/position/altitude-ft", true);
-
_route = static_cast<FGRouteMgr*>(globals->get_subsystem("route-manager"));
_navRadio1Node = fgGetNode("/instrumentation/nav[0]", true);
_excessDataNode = _Instrument->getChild("excess-data", 0, true);
_excessDataNode->setBoolValue(false);
+ _testModeNode = _Instrument->getChild("test-mode", 0, true);
+ _testModeNode->setBoolValue(false);
// OSG geometry setup
_radarGeode = new osg::Geode;
}
_time -= _updateInterval;
- _rangeNm = _Instrument->getFloatValue("range", 40.0);
- if (_Instrument->getBoolValue("aircraft-heading-up", true)) {
+ _rangeNm = _rangeNode->getFloatValue();
+ if (_testModeNode->getBoolValue()) {
+ _view_heading = 90;
+ } else if (_Instrument->getBoolValue("aircraft-heading-up", true)) {
_view_heading = fgGetDouble("/orientation/heading-deg");
} else {
_view_heading = _Instrument->getFloatValue("heading-up-deg", 0.0);
}
- _scale = _odg->size() / _rangeNm;
-
double xCenterFrac = _Instrument->getDoubleValue("x-center", 0.5);
double yCenterFrac = _Instrument->getDoubleValue("y-center", 0.5);
- _centerTrans = osg::Matrixf::translate(xCenterFrac * _odg->size(),
- yCenterFrac * _odg->size(), 0.0);
+ int pixelSize = _odg->size();
+
+ int rangePixels = _Instrument->getIntValue("range-pixels", -1);
+ if (rangePixels < 0) {
+ // hacky - assume (as is very common) that x-frac doesn't vary, and
+ // y-frac is used to position the center at either the top or bottom of
+ // the pixel area. Measure from the center to the furthest edge (top or bottom)
+ rangePixels = pixelSize * std::max(fabs(1.0 - yCenterFrac), fabs(yCenterFrac));
+ }
+
+ _scale = rangePixels / _rangeNm;
+ _Instrument->setDoubleValue("scale", _scale);
+
+
+ _centerTrans = osg::Matrixf::translate(xCenterFrac * pixelSize,
+ yCenterFrac * pixelSize, 0.0);
// scale from nm to display units, rotate so aircraft heading is up
// (as opposed to north), and compensate for centering
_projectMat = osg::Matrixf::scale(_scale, _scale, 1.0) *
degRotation(-_view_heading) * _centerTrans;
- _pos = SGGeod::fromDegFt(_user_lon_node->getDoubleValue(),
- _user_lat_node->getDoubleValue(),
- _user_alt_node->getDoubleValue());
+ _pos = globals->get_aircraft_position();
+
+ // invalidate the cache of positioned items, if we travelled more than 1nm
+ if (_cachedItemsValid) {
+ SGVec3d cartNow(SGVec3d::fromGeod(_pos));
+ double movedNm = dist(_cachedPos, cartNow) * SG_METER_TO_NM;
+ _cachedItemsValid = (movedNm < 1.0);
+ if (!_cachedItemsValid) {
+ SG_LOG(SG_INSTR, SG_INFO, "invalidating NavDisplay cache due to moving: " << movedNm);
+ }
+ }
_vertices->clear();
_lineVertices->clear();
_texCoords->clear();
_textGeode->removeDrawables(0, _textGeode->getNumDrawables());
+ BOOST_FOREACH(SymbolInstance* si, _symbols) {
+ delete si;
+ }
+ _symbols.clear();
+
BOOST_FOREACH(SymbolDef* def, _rules) {
if (def->enable) {
def->enabled = def->enable->test();
}
}
- processRoute();
- processNavRadios();
- processAI();
- findItems();
- limitDisplayedSymbols();
+ if (_testModeNode->getBoolValue()) {
+ addTestSymbols();
+ } else {
+ processRoute();
+ processNavRadios();
+ processAI();
+ findItems();
+ limitDisplayedSymbols();
+ }
+
addSymbolsToScene();
_symbolPrimSet->set(osg::PrimitiveSet::QUADS, 0, _vertices->size());
void NavDisplay::findItems()
{
- Filter filt;
- filt.minRunwayLengthFt = 2000;
-
- FGPositioned::List items =
- FGPositioned::findWithinRange(_pos, _rangeNm, &filt);
-
- FGPositioned::List::const_iterator it;
- for (it = items.begin(); it != items.end(); ++it) {
- foundPositionedItem(*it);
+ if (!_cachedItemsValid) {
+ SG_LOG(SG_INSTR, SG_INFO, "re-validating NavDisplay cache");
+ Filter filt;
+ filt.minRunwayLengthFt = 2000;
+ _itemsInRange = FGPositioned::findWithinRange(_pos, _rangeNm, &filt);
+ _cachedItemsValid = true;
+ _cachedPos = SGVec3d::fromGeod(_pos);
+ }
+
+ BOOST_FOREACH(FGPositioned* pos, _itemsInRange) {
+ foundPositionedItem(pos);
}
}
int threatLevel = ai->getIntValue("tcas/threat-level",-1);
if (threatLevel >= 0) {
states.insert("tcas");
- // states.insert("tcas-threat-level-" + itoa(threatLevel));
+
+ std::ostringstream os;
+ os << "tcas-threat-level-" << threatLevel;
+ states.insert(os.str());
}
double vspeed = ai->getDoubleValue("velocities/vertical-speed-fps");
}
}
-void NavDisplay::addSymbolInstance(const osg::Vec2& proj, double heading, SymbolDef* def, SGPropertyNode* vars)
+bool NavDisplay::addSymbolInstance(const osg::Vec2& proj, double heading, SymbolDef* def, SGPropertyNode* vars)
{
+ if (isProjectedClipped(proj)) {
+ return false;
+ }
+
SymbolInstance* sym = new SymbolInstance(proj, heading, def, vars);
_symbols.push_back(sym);
+ return true;
+}
+
+bool NavDisplay::isProjectedClipped(const osg::Vec2& projected) const
+{
+ double size = _odg->size();
+ return (projected.x() < 0.0) ||
+ (projected.y() < 0.0) ||
+ (projected.x() >= size) ||
+ (projected.y() >= size);
+}
+
+void NavDisplay::addTestSymbol(const std::string& type, const std::string& states, const SGGeod& pos, double heading, SGPropertyNode* vars)
+{
+ string_set stateSet;
+ BOOST_FOREACH(std::string s, simgear::strutils::split(states, ",")) {
+ stateSet.insert(s);
+ }
+
+ SymbolDefVector rules;
+ findRules(type, stateSet, rules);
+ if (rules.empty()) {
+ return; // no rules matched, we can skip this item
+ }
+
+ osg::Vec2 projected = projectGeod(pos);
+ BOOST_FOREACH(SymbolDef* r, rules) {
+ addSymbolInstance(projected, heading, r, vars);
+ }
}
+void NavDisplay::addTestSymbols()
+{
+ _pos = SGGeod::fromDeg(-122.3748889, 37.6189722); // KSFO
+
+ SGGeod a1;
+ double dummy;
+ SGGeodesy::direct(_pos, 45.0, 20.0 * SG_NM_TO_METER, a1, dummy);
+
+ addTestSymbol("airport", "", a1, 0.0, NULL);
+
+ SGGeodesy::direct(_pos, 95.0, 40.0 * SG_NM_TO_METER, a1, dummy);
+
+ addTestSymbol("vor", "", a1, 0.0, NULL);
+
+ SGGeodesy::direct(_pos, 120, 80.0 * SG_NM_TO_METER, a1, dummy);
+
+ addTestSymbol("airport", "destination", a1, 0.0, NULL);
+
+ SGGeodesy::direct(_pos, 80.0, 20.0 * SG_NM_TO_METER, a1, dummy);
+ addTestSymbol("fix", "", a1, 0.0, NULL);
+
+
+ SGGeodesy::direct(_pos, 140.0, 20.0 * SG_NM_TO_METER, a1, dummy);
+ addTestSymbol("fix", "", a1, 0.0, NULL);
+
+ SGGeodesy::direct(_pos, 110.0, 10.0 * SG_NM_TO_METER, a1, dummy);
+ addTestSymbol("fix", "", a1, 0.0, NULL);
+
+ SGGeodesy::direct(_pos, 110.0, 5.0 * SG_NM_TO_METER, a1, dummy);
+ addTestSymbol("fix", "", a1, 0.0, NULL);
+}