From 7ad0813990173698af00678e29215bf88233e606 Mon Sep 17 00:00:00 2001 From: timoore Date: Sun, 9 Sep 2007 23:22:14 +0000 Subject: [PATCH] Ground radar and tower control panel for ATC. This piggybacks on the weather radar for a cool ATC display. --- src/Instrumentation/Makefile.am | 3 +- src/Instrumentation/groundradar.cxx | 157 +++++++++++++++++++++++++ src/Instrumentation/groundradar.hxx | 48 ++++++++ src/Instrumentation/instrument_mgr.cxx | 5 +- src/Instrumentation/wxradar.cxx | 131 +++++++++++++++++++-- src/Instrumentation/wxradar.hxx | 11 +- 6 files changed, 341 insertions(+), 14 deletions(-) create mode 100644 src/Instrumentation/groundradar.cxx create mode 100644 src/Instrumentation/groundradar.hxx diff --git a/src/Instrumentation/Makefile.am b/src/Instrumentation/Makefile.am index b88113aa7..37a1a888b 100644 --- a/src/Instrumentation/Makefile.am +++ b/src/Instrumentation/Makefile.am @@ -30,6 +30,7 @@ libInstrumentation_a_SOURCES = \ od_gauge.hxx od_gauge.cxx wxradar.hxx wxradar.cxx \ tacan.cxx tacan.hxx mk_viii.cxx mk_viii.hxx \ dclgps.cxx dclgps.hxx \ - render_area_2d.cxx render_area_2d.hxx + render_area_2d.cxx render_area_2d.hxx \ + groundradar.cxx groundradar.hxx INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/src -I$(top_builddir)/src diff --git a/src/Instrumentation/groundradar.cxx b/src/Instrumentation/groundradar.cxx new file mode 100644 index 000000000..76933ec1b --- /dev/null +++ b/src/Instrumentation/groundradar.cxx @@ -0,0 +1,157 @@ +// groundradar.cxx - Background layer for the ATC radar. +// +// Written by Csaba Halasz. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include
+#include
+#include
+#include +#include +#include +#include + +#include "groundradar.hxx" + +static const char* airport_node_name = "/sim/tower/airport-id"; +static const char* texture_name = "Aircraft/Instruments/Textures/od_groundradar.rgb"; +static const char* radar_node_name = "/instrumentation/radar/range"; + +struct SingleFrameCallback : public osg::Camera::DrawCallback +{ + virtual void operator () (const osg::Camera& camera) const + { + const_cast(camera).setNodeMask(0); + } +}; + +GroundRadar::GroundRadar(SGPropertyNode *node) +{ + _airport_node = fgGetNode(airport_node_name, true); + _radar_node = fgGetNode(radar_node_name, true); + createTexture(); + updateTexture(); + _airport_node->addChangeListener(this); + _radar_node->addChangeListener(this); +} + +GroundRadar::~GroundRadar() +{ + _airport_node->removeChangeListener(this); + _radar_node->removeChangeListener(this); +} + +void GroundRadar::valueChanged(SGPropertyNode*) +{ + updateTexture(); +} + +inline static osg::Vec3 fromPolar(double fi, double r) +{ + return osg::Vec3(sin(fi * SGD_DEGREES_TO_RADIANS) * r, cos(fi * SGD_DEGREES_TO_RADIANS) * r, 0); +} + +void GroundRadar::createTexture() +{ + setSize(512); + allocRT(); + + osg::Vec4Array* colors = new osg::Vec4Array; + colors->push_back(osg::Vec4(0.0f,0.5f,0.0f,1.0f)); + colors->push_back(osg::Vec4(0.0f,0.5f,0.5f,1.0f)); + + _geom = new osg::Geometry(); + _geom->setColorArray(colors); + _geom->setColorBinding(osg::Geometry::BIND_PER_PRIMITIVE_SET); + _geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, 0)); + _geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, 0)); + + osg::Geode* geode = new osg::Geode(); + osg::StateSet* stateset = geode->getOrCreateStateSet(); + stateset->setMode(GL_BLEND, osg::StateAttribute::OFF); + geode->addDrawable(_geom.get()); + + osg::Camera* camera = getCamera(); + camera->setPostDrawCallback(new SingleFrameCallback()); + camera->addChild(geode); + camera->setNodeMask(0); + camera->setProjectionMatrixAsOrtho2D(0, getTexture()->getTextureWidth(), 0, getTexture()->getTextureHeight()); + + // Texture in the 2D panel system + FGTextureManager::addTexture(texture_name, getTexture()); +} + +void GroundRadar::updateTexture() +{ + osg::ref_ptr rwy_vertices = new osg::Vec3Array; + osg::ref_ptr taxi_vertices = new osg::Vec3Array; + + const string airport_name = _airport_node->getStringValue(); + FGRunwayList* runways = globals->get_runways(); + + const FGAirport* airport = fgFindAirportID(airport_name); + const SGGeod& tower_location = airport->getTowerLocation(); + const double tower_lat = tower_location.getLatitudeDeg(); + const double tower_lon = tower_location.getLongitudeDeg(); + double scale = SG_METER_TO_NM * 200 / _radar_node->getDoubleValue(); + + for(FGRunway runway = runways->search(airport_name); runway._id == airport_name; runway = runways->next()) + { + double az1, az2, dist_m; + geo_inverse_wgs_84(tower_lat, tower_lon, runway._lat, runway._lon, &az1, &az2, &dist_m); + + osg::Vec3 center = fromPolar(az1, dist_m * scale) + osg::Vec3(256, 256, 0); + osg::Vec3 leftcenter = fromPolar(runway._heading, runway._length * SG_FEET_TO_METER * scale / 2) + center; + osg::Vec3 lefttop = fromPolar(runway._heading - 90, runway._width * SG_FEET_TO_METER * scale / 2) + leftcenter; + osg::Vec3 leftbottom = leftcenter * 2 - lefttop; + osg::Vec3 rightbottom = center * 2 - lefttop; + osg::Vec3 righttop = center * 2 - leftbottom; + + osg::Vec3Array* vertices = runway._rwy_no[0] == 'x' ? taxi_vertices.get() : rwy_vertices.get(); + vertices->push_back(lefttop); + vertices->push_back(leftbottom); + vertices->push_back(rightbottom); + vertices->push_back(righttop); + } + + osg::Vec3Array* vertices = new osg::Vec3Array(*taxi_vertices.get()); + vertices->insert(vertices->end(), rwy_vertices->begin(), rwy_vertices->end()); + _geom->setVertexArray(vertices); + osg::DrawArrays* taxi = dynamic_cast(_geom->getPrimitiveSet(0)); + osg::DrawArrays* rwy = dynamic_cast(_geom->getPrimitiveSet(1)); + taxi->setCount(taxi_vertices->size()); + rwy->setFirst(taxi_vertices->size()); + rwy->setCount(rwy_vertices->size()); + + getCamera()->setNodeMask(0xffffffff); +} + +// end of GroundRadar.cxx diff --git a/src/Instrumentation/groundradar.hxx b/src/Instrumentation/groundradar.hxx new file mode 100644 index 000000000..ad8aa3371 --- /dev/null +++ b/src/Instrumentation/groundradar.hxx @@ -0,0 +1,48 @@ +// groundradar.hxx - Background layer for the ATC radar. +// +// Written by Csaba Halasz. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + +#ifndef __INST_GROUNDRADAR_HXX +#define __INST_GROUNDRADAR_HXX + +#include +#include +#include +#include "od_gauge.hxx" + +//////////////////////////////////////////////////////////////////////// +// Built-in layer for the atc radar. +//////////////////////////////////////////////////////////////////////// + +class GroundRadar : public SGPropertyChangeListener, public FGODGauge +{ +public: + GroundRadar(SGPropertyNode* node); + virtual ~GroundRadar(); + void updateTexture(); + virtual void valueChanged(SGPropertyNode*); + +protected: + osg::ref_ptr _geom; + SGPropertyNode_ptr _airport_node; + SGPropertyNode_ptr _radar_node; + + void createTexture(); +}; + +#endif // __INST_GROUNDRADAR_HXX diff --git a/src/Instrumentation/instrument_mgr.cxx b/src/Instrumentation/instrument_mgr.cxx index e8b5eb64c..638bd47a1 100644 --- a/src/Instrumentation/instrument_mgr.cxx +++ b/src/Instrumentation/instrument_mgr.cxx @@ -47,7 +47,7 @@ #include "tacan.hxx" #include "mk_viii.hxx" #include "mrg.hxx" - +#include "groundradar.hxx" FGInstrumentMgr::FGInstrumentMgr () { @@ -178,6 +178,9 @@ bool FGInstrumentMgr::build () } else if ( name == "master-reference-gyro" ) { set_subsystem( "instrument" + temp.str(), new MasterReferenceGyro( node ) ); + } else if ( name == "groundradar" ) { + set_subsystem( "instrument" + temp.str(), + new GroundRadar( node ), 1 ); } else { SG_LOG( SG_ALL, SG_ALERT, "Unknown top level section: " diff --git a/src/Instrumentation/wxradar.cxx b/src/Instrumentation/wxradar.cxx index 2727dd2ef..e4a96b3bf 100644 --- a/src/Instrumentation/wxradar.cxx +++ b/src/Instrumentation/wxradar.cxx @@ -32,6 +32,8 @@ #include #include #include +#include +#include #include #include @@ -42,6 +44,15 @@ #include #include +#include +#include +SG_USING_STD(stringstream); +SG_USING_STD(endl); +SG_USING_STD(setprecision); +SG_USING_STD(fixed); +SG_USING_STD(setw); +SG_USING_STD(setfill); + #include
#include
#include @@ -61,7 +72,7 @@ typedef radar_list_type::const_iterator radar_list_const_iterator; 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")), @@ -82,6 +93,9 @@ wxRadarBg::wxRadarBg ( SGPropertyNode *node) : wxRadarBg::~wxRadarBg () { + if (_radar_font_node != 0) { + _radar_font_node->removeChangeListener(this); + } } @@ -107,6 +121,7 @@ wxRadarBg::init () // no mipmap or else alpha will mix with pixels on the border of shapes, ruining the effect _wxEcho = SGLoadTexture2D(tpath.c_str(), false, false); + _Instrument->setFloatValue("trk", 0.0); _Instrument->setFloatValue("tilt", 0.0); _Instrument->setStringValue("status",""); @@ -123,7 +138,7 @@ wxRadarBg::init () FGInstrumentMgr *imgr = (FGInstrumentMgr *) globals->get_subsystem("instrumentation"); _odg = (FGODGauge *) imgr->get_subsystem("od_gauge"); - _odg->setSize(256); + _odg->setSize(512); _ai = (FGAIManager*)globals->get_subsystem("ai_model"); _ai_enabled_node = fgGetNode("/sim/ai/enabled", true); @@ -150,7 +165,13 @@ wxRadarBg::init () _radar_weather_node = n->getNode("WX", true); _radar_position_node = n->getNode("pos", true); _radar_data_node = n->getNode("data", true); + _radar_symbol_node = n->getNode("symbol", true); _radar_centre_node = n->getNode("centre", true); + _radar_rotate_node = n->getNode("rotate", true); + _radar_font_node = _Instrument->getNode("font", true); + _radar_font_node->addChangeListener(this); + + updateFont(); _radar_centre_node->setBoolValue(false); if (_radar_coverage_node->getType() == SGPropertyNode::NONE) @@ -203,8 +224,11 @@ wxRadarBg::init () // Texture in the 2D panel system FGTextureManager::addTexture(_texture_path.c_str(), _odg->getTexture()); + _textGeode = new osg::Geode; + osg::Camera* camera = _odg->getCamera(); camera->addChild(_radarGeode.get()); + camera->addChild(_textGeode.get()); } @@ -330,13 +354,16 @@ wxRadarBg::update (double delta_time_sec) _centerTrans.makeTranslate(_x_offset, _y_offset, 0.0f); } else if ( _display_mode == PLAN ) { - // no sense I presume + if (_radar_rotate_node->getBoolValue()) { + _angle_offset = -_view_heading; + } } else { // rose } _vertices->clear(); _texCoords->clear(); + _textGeode->removeDrawables(0, _textGeode->getNumDrawables()); update_weather(); @@ -520,6 +547,34 @@ wxRadarBg::update_weather() } } +void +wxRadarBg::update_data(FGAIBase* ac, double radius, double bearing, bool selected) +{ + osgText::Text* callsign = new osgText::Text; + callsign->setFont(_font.get()); + callsign->setFontResolution(12, 12); + callsign->setCharacterSize(12); + callsign->setColor(selected ? osg::Vec4(1, 1, 1, 1) : osg::Vec4(0, 1, 0, 1)); + 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 + callsign->setPosition(osg::Vec3((int)pos.x(), (int)pos.y(), 0)); + callsign->setAlignment(osgText::Text::LEFT_BOTTOM_BASE_LINE); + callsign->setLineSpacing(0.25); + + stringstream text; + text << ac->_getCallsign() << endl + << setprecision(0) << fixed + << setw(3) << setfill('0') << ac->_getHeading() << "\xB0 " + << setw(0) << ac->_getAltitude() << "ft" << endl + << ac->_getSpeed() << "kts"; + + callsign->setText(text.str()); + _textGeode->addDrawable(callsign); +} void wxRadarBg::update_aircraft() @@ -528,8 +583,9 @@ wxRadarBg::update_aircraft() return; bool draw_echoes = _radar_position_node->getBoolValue(); - bool draw_symbols = _radar_data_node->getBoolValue(); - if (!draw_echoes && !draw_symbols) + bool draw_symbols = _radar_symbol_node->getBoolValue(); + bool draw_data = _radar_data_node->getBoolValue(); + if (!draw_echoes && !draw_symbols && !draw_data) return; radar_list_type radar_list = _ai->get_ai_list(); @@ -544,8 +600,19 @@ wxRadarBg::update_aircraft() double user_lon = _user_lon_node->getDoubleValue(); double user_alt = _user_alt_node->getDoubleValue(); + float limit = _radar_coverage_node->getFloatValue(); + if (limit > 180) + limit = 180; + else if (limit < 0) + limit = 0; + limit *= SG_DEGREES_TO_RADIANS; + radar_list_iterator it = radar_list.begin(); radar_list_iterator end = radar_list.end(); + FGAIBase *selected_ac = 0; + double selected_radius = 0; + double selected_bearing = 0; + int selected_id = fgGetInt("/instrumentation/radar/selected-id", -1); for (; it != end; ++it) { FGAIBase *ac = *it; @@ -581,12 +648,6 @@ wxRadarBg::update_aircraft() float radius = range * _scale; float angle = calcRelBearing(bearing, _view_heading); - float limit = _radar_coverage_node->getFloatValue(); - if (limit > 180) - limit = 180; - else if (limit < 0) - limit = 0; - limit *= SG_DEGREES_TO_RADIANS; if (angle > limit || angle < -limit) continue; @@ -625,6 +686,19 @@ wxRadarBg::update_aircraft() // << " bearing=" << angle * SG_RADIANS_TO_DEGREES // << " radius=" << radius); } + + if (draw_data) { + if (ac->getID() == selected_id) { + selected_ac = ac; + selected_radius = radius; + selected_bearing = bearing; + } else { + update_data(ac, radius, bearing, false); + } + } + } + if (selected_ac) { + update_data(selected_ac, selected_radius, selected_bearing, true); } } @@ -780,3 +854,38 @@ wxRadarBg::calcRelBearing(float bearing, float heading) return angle; } +void +wxRadarBg::updateFont() +{ + SGPath tpath; + string path = _radar_font_node->getStringValue(); + if (path.length() == 0) { + path = DEFAULT_FONT; + } + if (path[0] != '/') { + tpath = globals->get_fg_root(); + tpath.append("Fonts"); + tpath.append(path); + } else { + tpath = path; + } +#if OSG_VERSION_MAJOR > 2 || (OSG_VERSION_MAJOR == 2 && OSG_VERSION_MINOR > 0) + osg::ref_ptr fontOptions = new osgDB::ReaderWriter::Options("monochrome"); + osg::ref_ptr font = osgText::readFontFile(tpath.c_str(), fontOptions.get()); +#else + osg::ref_ptr font = osgText::readFontFile(tpath.c_str()); +#endif + if (font != 0) { + _font = font; + _font->setMinFilterHint(osg::Texture::NEAREST); + _font->setMagFilterHint(osg::Texture::NEAREST); + _font->setGlyphImageMargin(0); + _font->setGlyphImageMarginRatio(0); + } +} + +void +wxRadarBg::valueChanged(SGPropertyNode*) +{ + updateFont(); +} diff --git a/src/Instrumentation/wxradar.hxx b/src/Instrumentation/wxradar.hxx index ac22959a7..f42e8d21b 100644 --- a/src/Instrumentation/wxradar.hxx +++ b/src/Instrumentation/wxradar.hxx @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -44,7 +45,7 @@ class FGAIBase; class FGODGauge; -class wxRadarBg : public SGSubsystem { +class wxRadarBg : public SGSubsystem, public SGPropertyChangeListener { public: wxRadarBg ( SGPropertyNode *node ); @@ -53,6 +54,7 @@ public: virtual void init (); virtual void update (double dt); + virtual void valueChanged(SGPropertyNode*); private: string _name; @@ -96,21 +98,26 @@ private: SGPropertyNode_ptr _radar_weather_node; SGPropertyNode_ptr _radar_position_node; SGPropertyNode_ptr _radar_data_node; + SGPropertyNode_ptr _radar_symbol_node; SGPropertyNode_ptr _radar_mode_control_node; SGPropertyNode_ptr _radar_centre_node; SGPropertyNode_ptr _radar_coverage_node; SGPropertyNode_ptr _radar_ref_rng_node; SGPropertyNode_ptr _radar_hdg_marker_node; + SGPropertyNode_ptr _radar_rotate_node; + SGPropertyNode_ptr _radar_font_node; SGPropertyNode_ptr _ai_enabled_node; osg::ref_ptr _resultTexture; osg::ref_ptr _wxEcho; osg::ref_ptr _radarGeode; + osg::ref_ptr _textGeode; osg::Geometry *_geom; osg::Vec2Array *_vertices; osg::Vec2Array *_texCoords; osg::Matrixf _centerTrans; + osg::ref_ptr _font; list_of_SGWxRadarEcho _radarEchoBuffer; @@ -121,6 +128,7 @@ private: void update_aircraft(); void update_tacan(); void update_heading_marker(); + void update_data(FGAIBase* ac, double radius, double bearing, bool selected); void center_map(); void apply_map_offset(); @@ -129,6 +137,7 @@ private: float calcRelBearing(float bearing, float heading); void calcRangeBearing(double lat, double lon, double lat2, double lon2, double &range, double &bearing) const; + void updateFont(); }; #endif // _INST_WXRADAR_HXX -- 2.39.5