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:
parent
43e0b3be48
commit
dbe2734e2c
8 changed files with 108 additions and 11 deletions
|
@ -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);
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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()
|
||||
|
|
BIN
src/GUI/assets/tacan-icon.png
Normal file
BIN
src/GUI/assets/tacan-icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
|
@ -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>
|
||||
|
|
|
@ -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 },
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in a new issue