From 3f7b229e669071c4ff4acced13ffd6c80fa4a6bd Mon Sep 17 00:00:00 2001 From: Klaus Basan Date: Fri, 27 Apr 2018 03:04:46 +0200 Subject: [PATCH] Ref T261, aircraft ICAO/parts guessing --- src/blackmisc/aviation/aircrafticaocode.cpp | 41 ++++++++++ src/blackmisc/aviation/aircrafticaocode.h | 4 + src/blackmisc/aviation/aircraftparts.cpp | 85 +++++++++++++++++---- src/blackmisc/aviation/aircraftparts.h | 8 +- 4 files changed, 119 insertions(+), 19 deletions(-) diff --git a/src/blackmisc/aviation/aircrafticaocode.cpp b/src/blackmisc/aviation/aircrafticaocode.cpp index d46fea161..f9e986a02 100644 --- a/src/blackmisc/aviation/aircrafticaocode.cpp +++ b/src/blackmisc/aviation/aircrafticaocode.cpp @@ -8,6 +8,7 @@ */ #include "blackmisc/simulation/matchingutils.h" +#include "blackmisc/aviation/aircraftsituationchange.h" #include "blackmisc/aviation/aircrafticaocode.h" #include "blackmisc/db/datastoreutility.h" #include "blackmisc/comparefunctions.h" @@ -28,6 +29,7 @@ using namespace BlackMisc; using namespace BlackMisc::Db; +using namespace BlackMisc::PhysicalQuantities; using namespace BlackMisc::Simulation; namespace BlackMisc @@ -196,6 +198,45 @@ namespace BlackMisc return score; } + void CAircraftIcaoCode::guessModelParameters(CLength &guessedCG, CSpeed &guessedLiftOffGs) const + { + // init to defaults + CLength guessedCG_ = CLength(1.5, CLengthUnit::m()); + CSpeed guessedLiftOffGs_ = this->isVtol() ? CSpeed::null() : CSpeed(70, CSpeedUnit::km_h()); + + const int engines = this->getEnginesCount(); + const QChar engineType = this->getEngineType()[0].toUpper(); + do + { + if (engines == 1) + { + if (engineType == 'T') { guessedCG_ = CLength(2.0, CLengthUnit::m()); break; } + } + else if (engines == 2) + { + guessedCG_ = CLength(2.0, CLengthUnit::m()); + guessedLiftOffGs_ = CSpeed(80, CSpeedUnit::kts()); + if (engineType == 'T') { guessedCG_ = CLength(2.0, CLengthUnit::m()); break; } + if (engineType == 'J') { guessedCG_ = CLength(2.5, CLengthUnit::m()); break; } + } + else if (engines > 2) + { + guessedCG_ = CLength(4.0, CLengthUnit::m()); + guessedLiftOffGs_ = CSpeed(70, CSpeedUnit::kts()); + if (engineType == 'J') + { + guessedCG_ = CLength(6.0, CLengthUnit::m()); + guessedLiftOffGs_ = CSpeed(100, CSpeedUnit::kts()); + break; + } + } + } + while (false); + + if (!guessedCG_.isNull()) { guessedCG = guessedCG_; } + if (!guessedLiftOffGs_.isNull()) { guessedLiftOffGs = guessedLiftOffGs_; } + } + bool CAircraftIcaoCode::isNull() const { return m_designator.isEmpty() && m_manufacturer.isEmpty() && m_modelDescription.isEmpty(); diff --git a/src/blackmisc/aviation/aircrafticaocode.h b/src/blackmisc/aviation/aircrafticaocode.h index bcd9c514b..3fd055d91 100644 --- a/src/blackmisc/aviation/aircrafticaocode.h +++ b/src/blackmisc/aviation/aircrafticaocode.h @@ -13,6 +13,7 @@ #define BLACKMISC_AVIATION_AIRCRAFTICAOCODE_H #include "blackmisc/db/datastore.h" +#include "blackmisc/pq/length.h" #include "blackmisc/blackmiscexport.h" #include "blackmisc/metaclass.h" #include "blackmisc/propertyindex.h" @@ -291,6 +292,9 @@ namespace BlackMisc //! \remark normally used with a selected set of ICAO codes or combined types int calculateScore(const CAircraftIcaoCode &otherCode, CStatusMessageList *log = nullptr) const; + //! Guess aircraft model parameters + void guessModelParameters(PhysicalQuantities::CLength &guessedCG, PhysicalQuantities::CSpeed &guessedLiftOffGs) const; + //! Null ICAO? bool isNull() const; diff --git a/src/blackmisc/aviation/aircraftparts.cpp b/src/blackmisc/aviation/aircraftparts.cpp index 11ceb8b0e..aee1568d5 100644 --- a/src/blackmisc/aviation/aircraftparts.cpp +++ b/src/blackmisc/aviation/aircraftparts.cpp @@ -7,16 +7,21 @@ * contained in the LICENSE file. */ +#include "blackmisc/simulation/aircraftmodel.h" #include "aircraftparts.h" #include "aircraftlights.h" #include "aircraftsituation.h" +#include "aircraftsituationchange.h" #include "blackmisc/comparefunctions.h" #include "blackmisc/stringutils.h" +#include "blackconfig/buildconfig.h" #include "QStringBuilder" #include using namespace BlackMisc::PhysicalQuantities; +using namespace BlackMisc::Simulation; +using namespace BlackConfig; namespace BlackMisc { @@ -33,6 +38,7 @@ namespace BlackMisc { return QStringLiteral("ts: ") % this->getFormattedTimestampAndOffset(true) % QStringLiteral(" details: ") % this->getPartsDetailsAsString() % + (m_guessingDetails.isEmpty() ? QStringLiteral("") : QStringLiteral(" - ") % m_guessingDetails) % QStringLiteral(" | on ground: ") % BlackMisc::boolToYesNo(m_isOnGround) % QStringLiteral(" | lights: ") % m_lights.toQString(i18n) % QStringLiteral(" | gear down: ") % BlackMisc::boolToYesNo(m_gearDown) % @@ -60,68 +66,115 @@ namespace BlackMisc return null; } - CAircraftParts CAircraftParts::guessedParts(const CAircraftSituation &situation, bool vtol, int engineNumber) + CAircraftParts CAircraftParts::guessedParts(const CAircraftSituation &situation, const CAircraftSituationChange &change, const CAircraftModel &model) { CAircraftParts parts; CAircraftEngineList engines; parts.setLights(CAircraftLights::guessedLights(situation)); - // set some reasonable defaults - const bool onGround = situation.isOnGround(); - parts.setGearDown(onGround); - engines.initEngines(engineNumber, !onGround || situation.isMoving()); + QString *details = CBuildConfig::isLocalDeveloperDebugBuild() ? &parts.m_guessingDetails : nullptr; + + const bool vtol = model.isVtol(); + CSpeed guessedLiftOffGs = CSpeed::null(); + CLength guessedCG = model.getCG(); + model.getAircraftIcaoCode().guessModelParameters(guessedCG, guessedLiftOffGs); + // const QChar engineType = model.getAircraftIcaoCode().getEngineTypeChar(); + + // set some reasonable values + const bool isOnGround = situation.isOnGround(); + const int engineCount = model.getEngineCount(); + engines.initEngines(engineCount, !isOnGround || situation.isMoving()); + parts.setGearDown(isOnGround); + parts.setSpoilersOut(false); + parts.setEngines(engines); + parts.setPartsDetails(GuessedParts); + + if (isOnGround) + { + if (!change.isNull()) + { + if (change.isConstDecelarating()) + { + parts.setSpoilersOut(true); + parts.setFlapsPercent(10); + return parts; + } + } + + const CSpeed slowSpeed = guessedLiftOffGs * 0.30; + if (situation.getGroundSpeed() < slowSpeed) + { + if (details) { *details += QStringLiteral("slow speed <") % slowSpeed.valueRoundedWithUnit(1) % QStringLiteral(" on ground"); } + parts.setFlapsPercent(0); + return parts; + } + else + { + if (details) { *details += QStringLiteral("faster speed >") % slowSpeed.valueRoundedWithUnit(1) % QStringLiteral(" on ground"); } + parts.setFlapsPercent(0); + return parts; + } + } const double pitchDeg = situation.getPitch().value(CAngleUnit::deg()); - double nearGround1Ft = 750; - double nearGround2Ft = 1500; - if (pitchDeg > 10) + const bool isLikelyTakeOffOrClimbing = change.isNull() ? pitchDeg > 20 : (change.isRotatingUp() || change.isConstAscending()); + const bool isLikelyLanding = change.isNull() ? false : change.isConstDescending(); + + double nearGround1Ft = 300; + double nearGround2Ft = 1000; + if (isLikelyTakeOffOrClimbing) { // likely starting - nearGround1Ft = 250; nearGround2Ft = 500; } if (situation.hasGroundElevation()) { const double aGroundFt = situation.getHeightAboveGround().value(CLengthUnit::ft()); + static const QString detailsInfo("above ground: %1ft near grounds: %2ft %3ft likely takeoff: %4 likely landing: %5"); + if (details) { *details = detailsInfo.arg(aGroundFt).arg(nearGround1Ft).arg(nearGround2Ft).arg(boolToYesNo(isLikelyTakeOffOrClimbing), boolToYesNo(isLikelyLanding)); } if (aGroundFt < nearGround1Ft) { + if (details) { details->prepend(QStringLiteral("near ground: ")); } parts.setGearDown(true); parts.setFlapsPercent(25); } else if (aGroundFt < nearGround2Ft) { - parts.setGearDown(true); + if (details) { details->prepend(QStringLiteral("2nd layer: ")); } + const bool gearDown = !isLikelyTakeOffOrClimbing && (situation.getGroundSpeed() < guessedLiftOffGs || isLikelyLanding); + parts.setGearDown(gearDown); parts.setFlapsPercent(10); } else { + if (details) { details->prepend(QStringLiteral("airborne: ")); } parts.setGearDown(false); parts.setFlapsPercent(0); } } else { - if (!situation.hasInboundGroundDetails()) + if (situation.getOnGroundDetails() != CAircraftSituation::NotSetGroundDetails) { // the ground flag is not reliable and we have no ground elevation if (situation.getOnGroundDetails() == CAircraftSituation::OnGroundByGuessing) { // should be OK + if (details) { *details = QStringLiteral("on ground, no elv."); } } else { if (!vtol) { - const bool gearDown = situation.getGroundSpeed().value(CSpeedUnit::kts()) < 60; + const bool gearDown = situation.getGroundSpeed() < guessedLiftOffGs; parts.setGearDown(gearDown); + if (details) { *details = QStringLiteral("not on ground elv., gs < ") + guessedLiftOffGs.valueRoundedWithUnit(1); } } } } } - parts.setEngines(engines); - parts.setPartsDetails(GuessedParts); return parts; } @@ -235,9 +288,9 @@ namespace BlackMisc m_engines = engines; } - void CAircraftParts::guessParts(const CAircraftSituation &situation, bool vtol, int engineNumber) + void CAircraftParts::guessParts(const CAircraftSituation &situation, const CAircraftSituationChange &change, const CAircraftModel &model) { - *this = guessedParts(situation, vtol, engineNumber); + *this = guessedParts(situation, change, model); } } // namespace } // namespace diff --git a/src/blackmisc/aviation/aircraftparts.h b/src/blackmisc/aviation/aircraftparts.h index 4486d72a7..73232f1bb 100644 --- a/src/blackmisc/aviation/aircraftparts.h +++ b/src/blackmisc/aviation/aircraftparts.h @@ -12,7 +12,6 @@ #ifndef BLACKMISC_AVIATION_AIRCRAFTPARTS_H #define BLACKMISC_AVIATION_AIRCRAFTPARTS_H -#include "blackmisc/aviation/aircraftengine.h" #include "blackmisc/aviation/aircraftenginelist.h" #include "blackmisc/aviation/aircraftlights.h" #include "blackmisc/blackmiscexport.h" @@ -27,9 +26,11 @@ namespace BlackMisc { + namespace Simulation { class CAircraftModel; } namespace Aviation { class CAircraftSituation; + class CAircraftSituationChange; //! Value object encapsulating information of aircraft's parts class BLACKMISC_EXPORT CAircraftParts : @@ -151,7 +152,7 @@ namespace BlackMisc void setPartsDetails(PartsDetails details) { m_partsDetails = static_cast(details); } //! Guess the parts - void guessParts(const CAircraftSituation &situation, bool vtol = false, int engineNumber = 2); + void guessParts(const CAircraftSituation &situation, const CAircraftSituationChange &change, const Simulation::CAircraftModel &model); //! \copydoc BlackMisc::Mixin::String::toQString QString convertToQString(bool i18n = false) const; @@ -166,7 +167,7 @@ namespace BlackMisc static const CAircraftParts &null(); //! Guessed parts - static CAircraftParts guessedParts(const CAircraftSituation &situation, bool vtol = false, int engineNumber = 2); + static CAircraftParts guessedParts(const CAircraftSituation &situation, const CAircraftSituationChange &change, const Simulation::CAircraftModel &model); //! Convert to QString static const QString &partsDetailsToString(PartsDetails details); @@ -179,6 +180,7 @@ namespace BlackMisc bool m_gearDown = false; bool m_spoilersOut = false; bool m_isOnGround = false; + QString m_guessingDetails; //!< just for debugging, not via DBus ... BLACK_METACLASS( CAircraftParts,