From dbe2734e2c770a554b56c27b0c6f70cc02353e81 Mon Sep 17 00:00:00 2001 From: James Turner Date: Mon, 30 May 2022 15:52:59 +0100 Subject: [PATCH] Launcher and startup support for TACANs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The launcher diagram and navaid-search now include TACANs, using the current NavCache encoding (DME whose name ends in TACAN). A new ‘—tacan’ option works similar to —vor etc to set position (with optional offset). Tuning the Tacan instrument from the command line is not yet supported, but could be easily added. --- src/GUI/BaseDiagram.cxx | 22 +++++++++++-- src/GUI/LocationController.cxx | 56 ++++++++++++++++++++++++++++----- src/GUI/NavaidSearchModel.cxx | 16 ++++++++++ src/GUI/assets/tacan-icon.png | Bin 0 -> 1530 bytes src/GUI/resources.qrc | 1 + src/Main/options.cxx | 10 ++++++ src/Main/positioninit.cxx | 9 ++++++ src/Navaids/NavDataCache.cxx | 5 ++- 8 files changed, 108 insertions(+), 11 deletions(-) create mode 100644 src/GUI/assets/tacan-icon.png diff --git a/src/GUI/BaseDiagram.cxx b/src/GUI/BaseDiagram.cxx index a18f109dc..b4a832e37 100644 --- a/src/GUI/BaseDiagram.cxx +++ b/src/GUI/BaseDiagram.cxx @@ -260,6 +260,11 @@ public: addType(FGPositioned::NDB); addType(FGPositioned::VOR); + // this doesn't work, since the cache does not actually contain TACAN entries + // addType(FGPositioned::TACAN); + // instead we look for DMEs and filter on the name + addType(FGPositioned::DME); + if (aircraft == LauncherController::Helicopter) { addType(FGPositioned::HELIPAD); } @@ -273,16 +278,24 @@ public: addType(FGPositioned::AIRPORT); } - virtual bool pass(FGPositioned* aPos) const + bool pass(FGPositioned* aPos) const override { bool ok = TypeFilter::pass(aPos); - if (ok && (aPos->type() == FGPositioned::FIX)) { + const auto ty = aPos->type(); + if (ok && (ty == FGPositioned::FIX)) { // ignore fixes which end in digits if (aPos->ident().length() > 4 && isdigit(aPos->ident()[3]) && isdigit(aPos->ident()[4])) { return false; } } + + if (ok && (ty == FGPositioned::DME)) { + if (!simgear::strutils::ends_with(aPos->name(), "TACAN")) { + return false; + } + } + return ok; } }; @@ -815,6 +828,11 @@ QPixmap BaseDiagram::iconForPositioned(const FGPositionedRef& pos, return QPixmap(":/vor-icon"); + // our filter only passes DMEs which are TACANs, so this is correct, + // until we actually record TACANs in the NavCache + case FGPositioned::DME: + return QPixmap(":/tacan-icon"); + case FGPositioned::AIRPORT: return iconForAirport(static_cast(pos.ptr()), options); diff --git a/src/GUI/LocationController.cxx b/src/GUI/LocationController.cxx index 2fcb877f2..c29dc53a8 100644 --- a/src/GUI/LocationController.cxx +++ b/src/GUI/LocationController.cxx @@ -571,9 +571,18 @@ void LocationController::restoreLocation(QVariantMap l) const SGGeod vicinity = SGGeod::fromDeg(l.value("vicinity-lon").toDouble(), l.value("vicinity-lat").toDouble()); - FGPositioned::TypeFilter filter({FGPositioned::Type::NDB, FGPositioned::Type::VOR, - FGPositioned::Type::FIX, FGPositioned::Type::WAYPOINT}); - m_location = FGPositioned::findClosestWithIdent(ident, vicinity, &filter); + // special-case TACANs so we don't get an ambiguity on VORTACs, where + // there would be a double hit on any ident + const auto isTacan = l.value("location-is-tacan").toBool(); + if (isTacan) { + FGPositioned::TypeFilter filter(FGPositioned::Type::DME); + m_location = FGPositioned::findClosestWithIdent(ident, vicinity, &filter); + } else { + FGPositioned::TypeFilter filter({FGPositioned::Type::NDB, FGPositioned::Type::VOR, + FGPositioned::Type::FIX, FGPositioned::Type::WAYPOINT}); + m_location = FGPositioned::findClosestWithIdent(ident, vicinity, &filter); + } + m_baseQml->setInner(m_location); } else if (l.contains("location-lat")) { m_locationIsLatLon = true; @@ -680,6 +689,11 @@ QVariantMap LocationController::saveLocation() const } } else { // not an aiport, must be a navaid locationSet.insert("location-navaid", QString::fromStdString(m_location->ident())); + if (m_location->type() == FGPositioned::DME) { + // so we don't get ambiguous on VORTACs, explicity mark TACANs + // otherwise every VORTAC would be ambiguous + locationSet.insert("location-is-tacan", true); + } locationSet.insert("vicinity-lat", m_location->geod().getLatitudeDeg()); locationSet.insert("vicinity-lon", m_location->geod().getLongitudeDeg()); @@ -714,11 +728,23 @@ void LocationController::setLocationProperties() { SGPropertyNode_ptr presets = fgGetNode("/sim/presets", true); - QStringList props = QStringList() << "vor-id" << "fix" << "ndb-id" << - "runway-requested" << "navaid-id" << "offset-azimuth-deg" << - "offset-distance-nm" << "glideslope-deg" << - "speed-set" << "on-ground" << "airspeed-kt" << - "airport-id" << "runway" << "parkpos" << "carrier" << "carrier-position"; + QStringList props = QStringList() << "vor-id" + << "fix" + << "ndb-id" + << "tacan-id" + << "runway-requested" + << "navaid-id" + << "offset-azimuth-deg" + << "offset-distance-nm" + << "glideslope-deg" + << "speed-set" + << "on-ground" + << "airspeed-kt" + << "airport-id" + << "runway" + << "parkpos" + << "carrier" + << "carrier-position"; Q_FOREACH(QString s, props) { SGPropertyNode* c = presets->getChild(s.toStdString()); @@ -835,6 +861,12 @@ void LocationController::setLocationProperties() case FGPositioned::FIX: fgSetString("/sim/presets/fix", m_location->ident()); break; + + // assume if a DME was selected, it was actually a TACAN + case FGPositioned::DME: + fgSetString("/sim/presets/tacan-id", m_location->ident()); + break; + default: break; } @@ -1031,6 +1063,11 @@ void LocationController::onCollectConfig() case FGPositioned::FIX: m_config->setArg("fix", m_location->ident()); break; + + case FGPositioned::DME: + m_config->setArg("tacan", m_location->ident()); + break; + default: break; } @@ -1162,6 +1199,9 @@ QString LocationController::description() const navaidType = QString("VOR"); break; case FGPositioned::NDB: navaidType = QString("NDB"); break; + case FGPositioned::DME: + navaidType = QString("TACAN"); + break; case FGPositioned::FIX: return tr("%2 waypoint %1").arg(ident).arg(offsetDesc); default: diff --git a/src/GUI/NavaidSearchModel.cxx b/src/GUI/NavaidSearchModel.cxx index d67e0f38f..4d15c8d85 100644 --- a/src/GUI/NavaidSearchModel.cxx +++ b/src/GUI/NavaidSearchModel.cxx @@ -116,6 +116,7 @@ public: addType(FGPositioned::VOR); addType(FGPositioned::FIX); addType(FGPositioned::NDB); + addType(FGPositioned::DME); // see custom pass() impl below } addType(FGPositioned::AIRPORT); @@ -137,6 +138,21 @@ public: addType(FGPositioned::SEAPORT); } } + + bool pass(FGPositioned* aPos) const override + { + bool ok = TypeFilter::pass(aPos); + const auto ty = aPos->type(); + + // filter only DMEs which are TACANs, until we have real TACANs in the DB + if (ok && (ty == FGPositioned::DME)) { + if (!simgear::strutils::ends_with(aPos->name(), "TACAN")) { + return false; + } + } + + return ok; + } }; void NavaidSearchModel::clear() diff --git a/src/GUI/assets/tacan-icon.png b/src/GUI/assets/tacan-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..7526e954a63eaba89e559cadbb0269496808a7c1 GIT binary patch literal 1530 zcmeAS@N?(olHy`uVBq!ia0vp^Vn8g+!3HF!RQ~M%QjEnx?oJHr&dIz4aweokc&7RK zGH3zW91LuXYz)jGB@9eJLI}!c1hSW0$H3m6e5E?|PI7F)m!XA6V0<1^l#~=$>Fbx5m+O@q>*W`v z>l<2HTIw4Z=^Gj80#)c1SLT%@R_NvxD?a#8ycO zWDy)d+*y*DhOjBG80syc2lYWR`i6Q2`f#&>T5Uk0Rza!7`8lN^<3sZ@OYDqnz>3j@ z5w<~e`4?rT0^JMJW@iZ1f-Huv-Z?+FpeVJt7^L15svcPkU41}Cen~zstcx;}J@ZOZ zi-4M;w!s{TTLsiC8+}l4Aq5{KZos0z$g$(H(T7L09oH4ER~o=%@y64|F~mY}Ye>Au zGpdbLe`qs|7NNnFMYuT&K-)~UJp zOfAT9L4qxdt%Rs;OWICJwY@wiRZ4fgFM0Rn)~^tjh1~Mb?|uF~_r9(5^E-+zI@RAZ z_?eEbU=fd*cBNeK!m8-@YK@s+j z|Np?}`*fDh?A({i@mmf*UauPaML_5s>j9S9o{y`Zd~{`Sf6ps@_GFts%ZFQ;hSM!< zlwBLVlogIJ9V}kflG3oHWyAdMg*sKn0<*IELVZ#@Zuk@z-&_|g8Gl$Z-}ln|^dKh2 zs~rczvUgbvNS#+&#J2Cuk^63M+qG=x&Rs6MR;)NVJkYPeNw$FTk$@CWc?;XEJ^yt7 zT(%cp@zu}!mhQut_epDOSU1|c6u3N`vYz?Emp3XVYr_MM6&{Wka-7e%ptJO)wYr@6 zUeA}hpMR8lPP#U&x;^-m_20;-{WkMoOZYru%y_;}=Gmmaw%O_|i$!uCx;_2qn!A7b zvg}*lo0yDFJd{0~Tx4&4{D5R=QS*E6GtX+ISwDW9F7Sb?`=3JS-WQveeQlZj{lctY z`?g-FX?R^Ti-Frm+Gnm&K!n&?@kQKrS{+9civO4%%ZmJRZ2QK9J^|5+hzOybmHr(e z+WnUlXCE(*`Bm9s6;a!={E51PZtCk7AC@XPn);tfO<(x)TlcA+NJ(M6RvQoPEZ2_T zE^HH1j^>&x>@ty0w&WXugd3*W_LG~4^gGFSw6)4Z&>9_2=-r;azu_F&u z?xqSGyqfwUTf}asR*`pC#e80qc-`8l-ATuqzWm6!#WUO7VagibNB`L$N*70M=g&5* zj0=qrx|aQa_J+E(JwIHJ-q`J4mQ~?d-|p9}BX?cEtxh1IXTk3Uyy{C*_j#O?Z!{KJ UadVr838>KXboFyt=akR{04&rkcK`qY literal 0 HcmV?d00001 diff --git a/src/GUI/resources.qrc b/src/GUI/resources.qrc index ea346e4b9..7f2955057 100644 --- a/src/GUI/resources.qrc +++ b/src/GUI/resources.qrc @@ -13,6 +13,7 @@ assets/airport-icon.png assets/airport-tower-icon.png assets/vor-dme-icon.png + assets/tacan-icon.png assets/seaport-tower-icon.png assets/ndb-small-icon.png assets/ndb-large-icon.png diff --git a/src/Main/options.cxx b/src/Main/options.cxx index 038109d90..8e5de40cb 100644 --- a/src/Main/options.cxx +++ b/src/Main/options.cxx @@ -692,6 +692,7 @@ clearLocation () fgSetString("/sim/presets/parkpos", ""); fgSetString("/sim/presets/carrier-position", ""); fgSetString("/sim/presets/fix", ""); + fgSetString("/sim/presets/tacan-id", ""); } /* @@ -805,6 +806,14 @@ fgOptLat( const char *arg ) return FG_OPTIONS_OK; } +static int +fgOptTACAN(const char* arg) +{ + clearLocation(); + fgSetString("/sim/presets/tacan-id", arg); + return FG_OPTIONS_OK; +} + static int fgOptAltitude( const char *arg ) { @@ -1842,6 +1851,7 @@ struct OptionDesc { {"carrier", true, OPTION_FUNC, "", false, "", fgOptCarrier }, {"carrier-position", true, OPTION_FUNC, "", false, "", fgOptCarrierPos }, {"fix", true, OPTION_FUNC, "", false, "", fgOptFIX }, + {"tacan", true, OPTION_FUNC, "", false, "", fgOptTACAN }, {"offset-distance", true, OPTION_DOUBLE, "/sim/presets/offset-distance-nm", false, "", 0 }, {"offset-azimuth", true, OPTION_DOUBLE, "/sim/presets/offset-azimuth-deg", false, "", 0 }, {"lon", true, OPTION_FUNC, "", false, "", fgOptLon }, diff --git a/src/Main/positioninit.cxx b/src/Main/positioninit.cxx index a3090cba5..f4b514ea3 100644 --- a/src/Main/positioninit.cxx +++ b/src/Main/positioninit.cxx @@ -596,6 +596,7 @@ bool initPosition() string carrier = fgGetString("/sim/presets/carrier"); string parkpos = fgGetString("/sim/presets/parkpos"); string fix = fgGetString("/sim/presets/fix"); + const auto tacan = fgGetString("/sim/presets/tacan-id"); // the launcher sets this to precisely identify a navaid PositionedID navaidId = fgGetInt("/sim/presets/navaid-id"); @@ -698,6 +699,14 @@ bool initPosition() } } + if (!set_pos & !tacan.empty()) { + // we don't record TACANs in the NavCache right now, instead we + // record DMEs, some of which have TACAN in the name. + if (fgSetPosFromNAV(tacan, 0.0, FGPositioned::DME, navaidId)) { + set_pos = true; + } + } + if ( !set_pos ) { const std::string defaultAirportId = fgGetString("/sim/presets/airport-id"); const FGAirport* airport = fgFindAirportID(defaultAirportId); diff --git a/src/Navaids/NavDataCache.cxx b/src/Navaids/NavDataCache.cxx index 30eced268..32630ec8e 100755 --- a/src/Navaids/NavDataCache.cxx +++ b/src/Navaids/NavDataCache.cxx @@ -2873,8 +2873,11 @@ NavDataCache::ThreadedGUISearch::ThreadedGUISearch(const std::string& term, bool sql = "SELECT rowid FROM positioned WHERE name LIKE '%" + term + "%' AND (type >= 1 AND type <= 3)"; } else { + // types are hard-coded here becuase this is only used by NavaidSearchModel + // in ther launcher. We would ideally use a TypeFilter but that would + // mean loading each positioned to filter them, which is inefficient. sql = "SELECT rowid FROM positioned WHERE name LIKE '%" + term - + "%' AND ((type >= 1 AND type <= 3) OR ((type >= 9 AND type <= 11))) "; + + "%' AND ((type >= 1 AND type <= 3) OR ((type >= 9 AND type <= 11)) OR (type=18 AND name LIKE '% TACAN') ) "; } sqlite3_prepare_v2(d->db, sql.c_str(), sql.length(), &d->query, NULL);