1
0
Fork 0

Launcher and startup support for TACANs

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.
This commit is contained in:
James Turner 2022-05-30 15:52:59 +01:00
parent 43e0b3be48
commit dbe2734e2c
8 changed files with 108 additions and 11 deletions

View file

@ -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<FGAirport*>(pos.ptr()), options);

View file

@ -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:

View file

@ -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()

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -13,6 +13,7 @@
<file alias="airport-icon">assets/airport-icon.png</file>
<file alias="airport-tower-icon">assets/airport-tower-icon.png</file>
<file alias="vor-dme-icon">assets/vor-dme-icon.png</file>
<file alias="tacan-icon">assets/tacan-icon.png</file>
<file alias="seaport-tower-icon">assets/seaport-tower-icon.png</file>
<file alias="ndb-small-icon">assets/ndb-small-icon.png</file>
<file alias="ndb-large-icon">assets/ndb-large-icon.png</file>

View file

@ -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 },

View file

@ -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);

View file

@ -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);