#include <simgear/constants.h>
#include <simgear/misc/sg_path.hxx>
-#include <simgear/environment/visual_enviro.hxx>
#include <simgear/scene/model/model.hxx>
#include <simgear/structure/exception.hxx>
#include <simgear/misc/sg_path.hxx>
#include <sstream>
#include <iomanip>
+
using std::stringstream;
using std::endl;
using std::setprecision;
using std::fixed;
using std::setw;
using std::setfill;
+using std::string;
#include <Main/fg_props.hxx>
#include <Main/globals.hxx>
#include <Cockpit/panel.hxx>
-#include <Cockpit/hud.hxx>
#include "instrument_mgr.hxx"
#include "od_gauge.hxx"
static const float UNIT = 1.0f / 8.0f; // 8 symbols in a row/column in the texture
static const char *DEFAULT_FONT = "typewriter.txf";
-
wxRadarBg::wxRadarBg(SGPropertyNode *node) :
_name(node->getStringValue("name", "radar")),
_num(node->getIntValue("number", 0)),
_time(0.0),
_interval(node->getDoubleValue("update-interval-sec", 1.0)),
- _sim_init_done(false),
+ _elapsed_time(0),
+ _persistance(0),
_odg(0),
- _last_switchKnob("off"),
+ _range_nm(0),
+ _scale(0),
+ _angle_offset(0),
+ _view_heading(0),
+ _x_offset(0),
+ _y_offset(0),
+ _radar_ref_rng(0),
+ _lat(0),
+ _lon(0),
_antenna_ht(node->getDoubleValue("antenna-ht-ft", 0.0)),
_resultTexture(0),
- _wxEcho(0)
+ _wxEcho(0),
+ _font_size(0),
+ _font_spacing(0)
{
string branch;
branch = "/instrumentation/" + _name;
wxRadarBg::~wxRadarBg ()
{
_font_node->removeChangeListener(this);
+ delete _odg;
}
wxRadarBg::init ()
{
_serviceable_node = _Instrument->getNode("serviceable", true);
+ _sceneryLoaded = fgGetNode("/sim/sceneryloaded", true);
// texture name to use in 2D and 3D instruments
_texture_path = _Instrument->getStringValue("radar-texture-path",
"Aircraft/Instruments/Textures/od_wxradar.rgb");
_resultTexture = FGTextureManager::createTexture(_texture_path.c_str(), false);
- SGPath tpath(globals->get_fg_root());
string path = _Instrument->getStringValue("echo-texture-path",
"Aircraft/Instruments/Textures/wxecho.rgb");
- tpath.append(path);
+ SGPath tpath = globals->resolve_aircraft_path(path);
// no mipmap or else alpha will mix with pixels on the border of shapes, ruining the effect
- _wxEcho = SGLoadTexture2D(tpath, false, false);
+ _wxEcho = SGLoadTexture2D(tpath, NULL, false, false);
_Instrument->setFloatValue("trk", 0.0);
// input range = n nm (20/40/80)
// input display-mode = arc | rose | map | plan
- FGInstrumentMgr *imgr = (FGInstrumentMgr *)globals->get_subsystem("instrumentation");
- _odg = (FGODGauge *)imgr->get_subsystem("od_gauge");
+ _odg = new FGODGauge;
_odg->setSize(512);
_ai_enabled_node = fgGetNode("/sim/ai/enabled", true);
_radar_symbol_node = n->getNode("symbol", true);
_radar_centre_node = n->getNode("centre", true);
_radar_rotate_node = n->getNode("rotate", true);
+ _radar_tcas_node = n->getNode("tcas", true);
+ _radar_absalt_node = n->getNode("abs-altitude", true);
_radar_centre_node->setBoolValue(false);
if (!_radar_coverage_node->hasValue())
osg::Camera *camera = _odg->getCamera();
camera->addChild(_radarGeode.get());
camera->addChild(_textGeode.get());
+
+ updateFont();
}
void
wxRadarBg::update (double delta_time_sec)
{
- if (!_sim_init_done) {
- if (!fgGetBool("sim/sceneryloaded", false))
- return;
+ if (!_sceneryLoaded->getBoolValue())
+ return;
- _sim_init_done = true;
- }
if (!_odg || !_serviceable_node->getBoolValue()) {
_Instrument->setStringValue("status", "");
return;
}
+
_time += delta_time_sec;
- if (_time < _interval){
-// cout << "WXradar update too soon " << _time << endl;
+ if (_time < _interval)
return;
- }
-// cout << "WXradar updating" << _time<< endl;
_time = 0.0;
}
string switchKnob = _Instrument->getStringValue("switch", "on");
- if (_last_switchKnob != switchKnob) {
- // since 3D models don't share textures with the rest of the world
- // we must locate them and replace their handle by hand
- // only do that when the instrument is turned on
- //if (_last_switchKnob == "off")
- //_odg->set_texture(_texture_path.c_str(), _resultTexture->getHandle());
-
- _last_switchKnob = switchKnob;
- }
-
if (switchKnob == "off") {
_Instrument->setStringValue("status", "");
} else if (switchKnob == "stby") {
}
_radar_ref_rng = _radar_ref_rng_node->getDoubleValue();
- _view_heading = get_heading() * SG_DEGREES_TO_RADIANS;
+ _view_heading = fgGetDouble("/orientation/heading-deg") * SG_DEGREES_TO_RADIANS;
_centerTrans.makeTranslate(0.0f, 0.0f, 0.0f);
_scale = 200.0 / _range_nm;
_radar_centre_node->setBoolValue(false);
}
- //SG_LOG(SG_GENERAL, SG_DEBUG, "Radar: displacement "
+ //SG_LOG(SG_INSTR, SG_DEBUG, "Radar: displacement "
// << _x_offset <<", "<<_y_offset
// << " user_speed_east_fps * SG_FPS_TO_KT "
// << user_speed_east_fps * SG_FPS_TO_KT
_texCoords->clear();
_textGeode->removeDrawables(0, _textGeode->getNumDrawables());
+#if 0
+ //TODO FIXME Mask below (only used for ARC mode) isn't properly aligned, i.e.
+ // it assumes the a/c position at the center of the display - though it's somewhere at
+ // bottom part for ARC mode.
+ // The mask hadn't worked at all for a while (probably since the OSG port) due to
+ // another bug (which is fixed now). Now, the mask is disabled completely until s.o.
+ // adapted the coordinates below. And the mask is only really useful to limit displayed
+ // weather blobs (not support yet).
+ // Aircraft echos are already limited properly through wxradar's "limit-deg" property.
+ {
+ osg::DrawArrays *maskPSet
+ = static_cast<osg::DrawArrays*>(_geom->getPrimitiveSet(1));
+ osg::DrawArrays *trimaskPSet
+ = static_cast<osg::DrawArrays*>(_geom->getPrimitiveSet(2));
+
+ if (_display_mode == ARC) {
+ // erase what is out of sight of antenna
+ /*
+ |\ /|
+ | \ / |
+ | \ / |
+ ---------
+ | |
+ | |
+ ---------
+ */
+ float xOffset = 256.0f;
+ float yOffset = 200.0f;
+
+ int firstQuadVert = _vertices->size();
+ _texCoords->push_back(osg::Vec2f(0.5f, 0.25f));
+ _vertices->push_back(osg::Vec2f(-xOffset, 0.0 + yOffset));
+ _texCoords->push_back(osg::Vec2f(1.0f, 0.25f));
+ _vertices->push_back(osg::Vec2f(xOffset, 0.0 + yOffset));
+ _texCoords->push_back(osg::Vec2f(1.0f, 0.5f));
+ _vertices->push_back(osg::Vec2f(xOffset, 256.0 + yOffset));
+ _texCoords->push_back(osg::Vec2f(0.5f, 0.5f));
+ _vertices->push_back(osg::Vec2f(-xOffset, 256.0 + yOffset));
+ maskPSet->set(osg::PrimitiveSet::QUADS, firstQuadVert, 4);
+ firstQuadVert += 4;
+
+ // The triangles aren't supposed to be textured, but there's
+ // no need to set up a different Geometry, switch modes,
+ // etc. I happen to know that there's a white pixel in the
+ // texture at 1.0, 0.0 :)
+ float centerY = tan(30 * SG_DEGREES_TO_RADIANS);
+ _vertices->push_back(osg::Vec2f(0.0, 0.0));
+ _vertices->push_back(osg::Vec2f(-256.0, 0.0));
+ _vertices->push_back(osg::Vec2f(-256.0, 256.0 * centerY));
+
+ _vertices->push_back(osg::Vec2f(0.0, 0.0));
+ _vertices->push_back(osg::Vec2f(256.0, 0.0));
+ _vertices->push_back(osg::Vec2f(256.0, 256.0 * centerY));
+
+ _vertices->push_back(osg::Vec2f(-256, 0.0));
+ _vertices->push_back(osg::Vec2f(256.0, 0.0));
+ _vertices->push_back(osg::Vec2f(-256.0, -256.0));
+
+ _vertices->push_back(osg::Vec2f(256, 0.0));
+ _vertices->push_back(osg::Vec2f(256.0, -256.0));
+ _vertices->push_back(osg::Vec2f(-256.0, -256.0));
+
+ const osg::Vec2f whiteSpot(1.0f, 0.0f);
+ for (int i = 0; i < 3 * 4; i++)
+ _texCoords->push_back(whiteSpot);
+
+ trimaskPSet->set(osg::PrimitiveSet::TRIANGLES, firstQuadVert, 3 * 4);
+
+ } else
+ {
+ maskPSet->set(osg::PrimitiveSet::QUADS, 0, 0);
+ trimaskPSet->set(osg::PrimitiveSet::TRIANGLES, 0, 0);
+ }
- update_weather();
+ maskPSet->dirty();
+ trimaskPSet->dirty();
+ }
+#endif
+
+ // remember index of next vertex
+ int vIndex = _vertices->size();
+ update_weather();
osg::DrawArrays *quadPSet
= static_cast<osg::DrawArrays*>(_geom->getPrimitiveSet(0));
- quadPSet->set(osg::PrimitiveSet::QUADS, 0, _vertices->size());
- quadPSet->dirty();
-
- // erase what is out of sight of antenna
- /*
- |\ /|
- | \ / |
- | \ / |
- ---------
- | |
- | |
- ---------
- */
-
- osg::DrawArrays *maskPSet
- = static_cast<osg::DrawArrays*>(_geom->getPrimitiveSet(1));
- osg::DrawArrays *trimaskPSet
- = static_cast<osg::DrawArrays*>(_geom->getPrimitiveSet(2));
-
- if (_display_mode == ARC) {
- float xOffset = 256.0f;
- float yOffset = 200.0f;
-
- int firstQuadVert = _vertices->size();
- _texCoords->push_back(osg::Vec2f(0.5f, 0.25f));
- _vertices->push_back(osg::Vec2f(-xOffset, 0.0 + yOffset));
- _texCoords->push_back(osg::Vec2f(1.0f, 0.25f));
- _vertices->push_back(osg::Vec2f(xOffset, 0.0 + yOffset));
- _texCoords->push_back(osg::Vec2f(1.0f, 0.5f));
- _vertices->push_back(osg::Vec2f(xOffset, 256.0 + yOffset));
- _texCoords->push_back(osg::Vec2f(0.5f, 0.5f));
- _vertices->push_back(osg::Vec2f(-xOffset, 256.0 + yOffset));
- maskPSet->set(osg::PrimitiveSet::QUADS, firstQuadVert, 4);
-
- // The triangles aren't supposed to be textured, but there's
- // no need to set up a different Geometry, switch modes,
- // etc. I happen to know that there's a white pixel in the
- // texture at 1.0, 0.0 :)
- float centerY = tan(30 * SG_DEGREES_TO_RADIANS);
- _vertices->push_back(osg::Vec2f(0.0, 0.0));
- _vertices->push_back(osg::Vec2f(-256.0, 0.0));
- _vertices->push_back(osg::Vec2f(-256.0, 256.0 * centerY));
-
- _vertices->push_back(osg::Vec2f(0.0, 0.0));
- _vertices->push_back(osg::Vec2f(256.0, 0.0));
- _vertices->push_back(osg::Vec2f(256.0, 256.0 * centerY));
-
- _vertices->push_back(osg::Vec2f(-256, 0.0));
- _vertices->push_back(osg::Vec2f(256.0, 0.0));
- _vertices->push_back(osg::Vec2f(-256.0, -256.0));
-
- _vertices->push_back(osg::Vec2f(256, 0.0));
- _vertices->push_back(osg::Vec2f(256.0, -256.0));
- _vertices->push_back(osg::Vec2f(-256.0, -256.0));
-
- const osg::Vec2f whiteSpot(1.0f, 0.0f);
- for (int i = 0; i < 3 * 4; i++)
- _texCoords->push_back(whiteSpot);
-
- trimaskPSet->set(osg::PrimitiveSet::TRIANGLES, firstQuadVert + 4, 3 * 4);
-
- } else {
- maskPSet->set(osg::PrimitiveSet::QUADS, 0, 0);
- trimaskPSet->set(osg::PrimitiveSet::TRIANGLES, 0, 0);
- }
-
- maskPSet->dirty();
- trimaskPSet->dirty();
-
- // draw without mask
- _vertices->clear();
- _texCoords->clear();
update_aircraft();
update_tacan();
update_heading_marker();
- quadPSet->set(osg::PrimitiveSet::QUADS, 0, _vertices->size());
+ // draw all new vertices are quads
+ quadPSet->set(osg::PrimitiveSet::QUADS, vIndex, _vertices->size()-vIndex);
quadPSet->dirty();
}
}
wxRadarBg::update_weather()
{
string modeButton = _Instrument->getStringValue("mode", "WX");
- _radarEchoBuffer = *sgEnviro.get_radar_echo();
+// FIXME: implementation of radar echoes missing
+// _radarEchoBuffer = *sgEnviro.get_radar_echo();
// pretend we have a scan angle bigger then the FOV
// TODO:check real fov, enlarge if < nn, and do clipping if > mm
// const float fovFactor = 1.45f;
_Instrument->setStringValue("status", modeButton.c_str());
+// FIXME: implementation of radar echoes missing
+#if 0
list_of_SGWxRadarEcho *radarEcho = &_radarEchoBuffer;
list_of_SGWxRadarEcho::iterator iradarEcho, end = radarEcho->end();
const float LWClevel[] = { 0.1f, 0.5f, 2.1f };
if (iradarEcho->lightning || lwc < LWClevel[level])
continue;
- float radius = sgSqrt(iradarEcho->dist) * SG_METER_TO_NM * _scale;
+ float radius = sqrt(iradarEcho->dist) * SG_METER_TO_NM * _scale;
float size = iradarEcho->radius * 2.0 * SG_METER_TO_NM * _scale;
if (radius - size > 180)
* wxRotate(angle) * _centerTrans);
addQuad(_vertices, _texCoords, m, texBase);
- //SG_LOG(SG_GENERAL, SG_DEBUG, "Radar: drawing clouds"
+ //SG_LOG(SG_INSTR, SG_DEBUG, "Radar: drawing clouds"
// << " ID=" << cloudId
// << " x=" << x
// << " y="<< y
addQuad(_vertices, _texCoords, m, texBase);
}
}
+#endif
}
diff = _elapsed_time - (*ground_echoes_iterator)->elapsed_time;
if( diff > _persistance) {
- ground_echoes.erase(ground_echoes_iterator);
+ ground_echoes.erase(ground_echoes_iterator++);
} else {
// double test_brg = (*ground_echoes_iterator)->bearing;
// double bearing = test_brg * SG_DEGREES_TO_RADIANS;
// float angle = calcRelBearing(bearing, _view_heading);
double bumpinessFactor = (*ground_echoes_iterator)->bumpiness;
- float heading = get_heading();
+ float heading = fgGetDouble("/orientation/heading-deg");
if ( _display_mode == BSCAN ){
test_rng = (*ground_echoes_iterator)->elevation * 6;
test_brg = (*ground_echoes_iterator)->bearing;
if (!_ai_enabled_node->getBoolValue())
return;
- bool draw_echoes = _radar_position_node->getBoolValue();
- bool draw_symbols = _radar_symbol_node->getBoolValue();
- bool draw_data = _radar_data_node->getBoolValue();
+ bool draw_tcas = _radar_tcas_node->getBoolValue();
+ bool draw_absolute = _radar_absalt_node->getBoolValue();
+ bool draw_echoes = _radar_position_node->getBoolValue();
+ bool draw_symbols = _radar_symbol_node->getBoolValue();
+ bool draw_data = _radar_data_node->getBoolValue();
if (!draw_echoes && !draw_symbols && !draw_data)
return;
model = ai->getChild(i);
if (!model->nChildren())
continue;
- if (model->getIntValue("id") == selected_id) {
+ if ((model->getIntValue("id") == selected_id)&&
+ (!draw_tcas)) {
selected_ac = model; // save selected model for last iteration
continue;
}
bearing += _angle_offset;
heading += _angle_offset;
+ bool is_tcas_contact = false;
+ if (draw_tcas)
+ {
+ is_tcas_contact = update_tcas(model,range,user_alt,alt,bearing,radius,draw_absolute);
+ }
+
// pos mode
- if (draw_echoes) {
+ if (draw_echoes && (!is_tcas_contact)) {
float size = echo_radius * 120 * UNIT;
const osg::Vec2f texBase(3 * UNIT, 3 * UNIT);
}
// data mode
- if (draw_symbols) {
+ if (draw_symbols && (!draw_tcas)) {
const osg::Vec2f texBase(0, 3 * UNIT);
float size = 600 * UNIT;
osg::Matrixf m(osg::Matrixf::scale(size, size, 1.0f)
addQuad(_vertices, _texCoords, m, texBase);
}
- if (draw_data || i < 0) // selected one (i == -1) is always drawn
+ if ((draw_data || i < 0)&& // selected one (i == -1) is always drawn
+ ((!draw_tcas)||(is_tcas_contact)||(draw_echoes)))
update_data(model, alt, heading, radius, bearing, i < 0);
}
}
+/** Update TCAS display.
+ * Return true when processed as TCAS contact, false otherwise. */
+bool
+wxRadarBg::update_tcas(const SGPropertyNode *model,double range,double user_alt,double alt,
+ double bearing,double radius,bool absMode)
+{
+ int threatLevel=0;
+ {
+ // update TCAS symbol
+ osg::Vec2f texBase;
+ threatLevel = model->getIntValue("tcas/threat-level",-1);
+ if (threatLevel == -1)
+ {
+ // no TCAS information (i.e. no transponder) => not visible to TCAS
+ return false;
+ }
+ int row = 7 - threatLevel;
+ int col = 4;
+ double vspeed = model->getDoubleValue("velocities/vertical-speed-fps");
+ if (vspeed < -3.0) // descending
+ col+=1;
+ else
+ if (vspeed > 3.0) // climbing
+ col+=2;
+ texBase = osg::Vec2f(col*UNIT,row * UNIT);
+ float size = 200 * UNIT;
+ osg::Matrixf m(osg::Matrixf::scale(size, size, 1.0f)
+ * wxRotate(-bearing)
+ * osg::Matrixf::translate(0.0f, radius, 0.0f)
+ * wxRotate(bearing) * _centerTrans);
+ addQuad(_vertices, _texCoords, m, texBase);
+ }
+
+ {
+ // update TCAS data
+ osgText::Text *altStr = new osgText::Text;
+ altStr->setFont(_font.get());
+ altStr->setFontResolution(12, 12);
+ altStr->setCharacterSize(_font_size);
+ altStr->setColor(_tcas_colors[threatLevel]);
+ osg::Matrixf m(wxRotate(-bearing)
+ * osg::Matrixf::translate(0.0f, radius, 0.0f)
+ * wxRotate(bearing) * _centerTrans);
+
+ osg::Vec3 pos = m.preMult(osg::Vec3(16, 16, 0));
+ // cast to int's, otherwise text comes out ugly
+ altStr->setLineSpacing(_font_spacing);
+
+ stringstream text;
+ altStr->setAlignment(osgText::Text::LEFT_CENTER);
+ int altDif = (alt-user_alt+50)/100;
+ char sign = 0;
+ int dy=0;
+ if (altDif>=0)
+ {
+ sign='+';
+ dy=2;
+ }
+ else
+ if (altDif<0)
+ {
+ sign='-';
+ altDif = -altDif;
+ dy=-30;
+ }
+ altStr->setPosition(osg::Vec3((int)pos.x()-30, (int)pos.y()+dy, 0));
+ if (absMode)
+ {
+ // absolute altitude display
+ text << setprecision(0) << fixed
+ << setw(3) << setfill('0') << alt/100 << endl;
+ }
+ else // relative altitude display
+ if (sign)
+ {
+ text << sign
+ << setprecision(0) << fixed
+ << setw(2) << setfill('0') << altDif << endl;
+ }
+
+ altStr->setText(text.str());
+ _textGeode->addDrawable(altStr);
+ }
+
+ return true;
+}
void
wxRadarBg::update_tacan()
* wxRotate(angle) * _centerTrans);
addQuad(_vertices, _texCoords, m, texBase);
- //SG_LOG(SG_GENERAL, SG_DEBUG, "Radar: drawing TACAN"
+ //SG_LOG(SG_INSTR, SG_DEBUG, "Radar: drawing TACAN"
// << " dist=" << radius
// << " view_heading=" << _view_heading * SG_RADIANS_TO_DEGREES
// << " bearing=" << angle * SG_RADIANS_TO_DEGREES
m *= _centerTrans;
addQuad(_vertices, _texCoords, m, texBase);
- //SG_LOG(SG_GENERAL, SG_DEBUG, "Radar: drawing heading marker"
+ //SG_LOG(SG_INSTR, SG_DEBUG, "Radar: drawing heading marker"
// << " x,y " << x <<","<< y
// << " dist" << dist
// << " view_heading" << _view_heading * SG_RADIANS_TO_DEGREES
alt = 0; // to allow some vertical extent of target
double radarhorizon = 1.23 * (sqrt(alt) + sqrt(user_alt));
-// SG_LOG(SG_GENERAL, SG_ALERT, "Radar: radar horizon " << radarhorizon);
+// SG_LOG(SG_INSTR, SG_ALERT, "Radar: radar horizon " << radarhorizon);
return radarhorizon >= range_nm;
}
constant = 35;
double maxrange = constant * pow(sigma, 0.25);
- //SG_LOG(SG_GENERAL, SG_DEBUG, "Radar: max range " << maxrange);
+ //SG_LOG(SG_INSTR, SG_DEBUG, "Radar: max range " << maxrange);
return maxrange >= range_nm;
}
_font->setGlyphImageMargin(0);
_font->setGlyphImageMarginRatio(0);
}
+
+ for (int i=0;i<4;i++)
+ {
+ const float defaultColors[4][3] = {{0,1,1},{0,1,1},{1,0.5,0},{1,0,0}};
+ SGPropertyNode_ptr color_node = _font_node->getNode("tcas/color",i,true);
+ float red = color_node->getFloatValue("red",defaultColors[i][0]);
+ float green = color_node->getFloatValue("green",defaultColors[i][1]);
+ float blue = color_node->getFloatValue("blue",defaultColors[i][2]);
+ float alpha = color_node->getFloatValue("alpha",1);
+ _tcas_colors[i]=osg::Vec4(red, green, blue, alpha);
+ }
}
void
wxRadarBg::valueChanged(SGPropertyNode*)
{
updateFont();
+ _time = _interval;
}