From 784cf29af5f5717683e2d149e3a9727169f23bdc Mon Sep 17 00:00:00 2001 From: Klaus Basan Date: Fri, 27 Apr 2018 02:27:03 +0200 Subject: [PATCH] Ref T261, aircraft changes class reflecting delta/average values * math utils for standard deviation etc. * CAircraftSituationChange value class --- .../aviation/aircraftsituationchange.cpp | 212 ++++++++++++++++++ .../aviation/aircraftsituationchange.h | 204 +++++++++++++++++ src/blackmisc/aviation/aviation.h | 1 + .../aviation/registermetadataaviation.cpp | 6 + src/blackmisc/math/mathutils.cpp | 58 +++++ src/blackmisc/math/mathutils.h | 27 ++- src/blackmisc/propertyindex.h | 13 +- 7 files changed, 514 insertions(+), 7 deletions(-) create mode 100644 src/blackmisc/aviation/aircraftsituationchange.cpp create mode 100644 src/blackmisc/aviation/aircraftsituationchange.h diff --git a/src/blackmisc/aviation/aircraftsituationchange.cpp b/src/blackmisc/aviation/aircraftsituationchange.cpp new file mode 100644 index 000000000..973ed6ff6 --- /dev/null +++ b/src/blackmisc/aviation/aircraftsituationchange.cpp @@ -0,0 +1,212 @@ +/* Copyright (C) 2018 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +#include "blackmisc/aviation/aircraftsituationchange.h" +#include "blackmisc/aviation/aircraftsituationlist.h" +#include "blackmisc/aviation/callsign.h" +#include "blackmisc/pq/length.h" +#include "blackmisc/pq/angle.h" +#include "blackmisc/pq/units.h" +#include "blackmisc/math/mathutils.h" +#include "blackmisc/propertyindex.h" +#include "blackmisc/variant.h" +#include "blackmisc/verify.h" +#include "blackmisc/stringutils.h" +#include "blackconfig/buildconfig.h" +#include "QStringBuilder" +#include +#include + +using namespace BlackMisc::PhysicalQuantities; +using namespace BlackMisc::Math; +using namespace BlackConfig; + +namespace BlackMisc +{ + namespace Aviation + { + CAircraftSituationChange::CAircraftSituationChange() {} + + CAircraftSituationChange::CAircraftSituationChange(const CAircraftSituation &s1, const CAircraftSituation &s2) : + CAircraftSituationChange::CAircraftSituationChange(CAircraftSituationList({s1, s2})) {} + + CAircraftSituationChange::CAircraftSituationChange(const CAircraftSituationList &situations, bool alreadySortedLatestFirst, bool calcStdDeviations) + { + if (situations.size() < 2) { return; } + const CAircraftSituationList sorted(alreadySortedLatestFirst ? situations : situations.getSortedAdjustedLatestFirst()); + + if (CBuildConfig::isLocalDeveloperDebugBuild()) + { + Q_ASSERT_X(sorted.isSortedAdjustedLatestFirstWithoutNullPositions(), Q_FUNC_INFO, "Wrong sort order or NULL position"); + } + + const CAircraftSituation latest(sorted.front()); + const CAircraftSituation oldest(sorted.back()); + const CCallsign cs(latest.getCallsign()); + + m_situationsCount = situations.size(); + m_correspondingCallsign = cs; + m_timestampMSecsSinceEpoch = latest.getMSecsSinceEpoch(); + m_timeOffsetMs = latest.getTimeOffsetMs(); + m_oldestTimestampMSecsSinceEpoch = oldest.getMSecsSinceEpoch(); + m_latestAdjustedTimestampMSecsSinceEpoch = latest.getAdjustedMSecsSinceEpoch(); + m_oldestAdjustedTimestampMSecsSinceEpoch = oldest.getAdjustedMSecsSinceEpoch(); + m_constAscending = sorted.isConstAscending(true); + m_constDescending = sorted.isConstDescending(true); + m_constOnGround = sorted.isConstOnGround(); + m_constNotOnGround = sorted.isConstNotOnGround(); + m_justTakeoff = sorted.isJustTakingOff(true); + m_justTouchdown = sorted.isJustTouchingDown(true); + m_constAccelerating = sorted.isConstAccelerating(true); + m_constDecelerating = sorted.isConstDecelarating(true); + m_containsPushBack = sorted.containsPushBack(); + + if (sorted.size() >= 3) + { + const CAircraftSituationList sortedWithout = sorted.withoutFrontSituation(); + m_wasNotOnGround = sortedWithout.isConstNotOnGround(); + m_wasOnGround = sortedWithout.isConstOnGround(); + } + + if (calcStdDeviations) + { + this->calculateStdDeviations(situations); + m_rotateUp = sorted.front().getPitch() > (m_pitchMean + m_pitchStdDev); + } + else + { + m_rotateUp = sorted.isRotatingUp(true); + } + } + + QString CAircraftSituationChange::convertToQString(bool i18n) const + { + Q_UNUSED(i18n); + static const QString null("null"); + if (this->isNull()) { return null; } + return QStringLiteral("CS: '") % this->getCallsign().asString() % + QStringLiteral(" ' ts: ") % this->getTimestampAndOffset(true) % + QStringLiteral(" | situations:") % QString::number(m_situationsCount) % + QStringLiteral(" | ts adj.: ") % QString::number(m_oldestAdjustedTimestampMSecsSinceEpoch) % QStringLiteral("-") % QString::number(m_latestAdjustedTimestampMSecsSinceEpoch) % + QStringLiteral(" | just takeoff: ") % boolToYesNo(this->isJustTakingOff()) % QStringLiteral(" just touchdown: ") % boolToYesNo(this->isJustTouchingDown()) % + QStringLiteral(" | all gnd: ") % boolToYesNo(this->isConstOnGround()) % QStringLiteral("/ ") % boolToYesNo(this->wasConstOnGround()) % + QStringLiteral(" | all not gnd: ") % boolToYesNo(this->isConstNotOnGround()) % QStringLiteral("/ ") % boolToYesNo(this->wasConstNotOnGround()) % + QStringLiteral(" | ascending: ") % boolToYesNo(this->isConstAscending()) % QStringLiteral(" descending: ") % boolToYesNo(this->isConstDescending()) % + QStringLiteral(" | accelerating.: ") % boolToYesNo(this->isConstAccelerating()) % QStringLiteral(" decelarating: ") % boolToYesNo(this->isConstDecelarating()) % + QStringLiteral(" | rotate up: ") % boolToYesNo(this->isRotatingUp()) % + QStringLiteral(" | push back: ") % boolToYesNo(this->containsPushBack()) % + QStringLiteral(" | alt.delta: ") % m_altAglMean.valueRoundedWithUnit(1) % QStringLiteral("/") % m_altAglStdDev.valueRoundedWithUnit(1) % + QStringLiteral(" | std.dev: pitch ") % m_pitchMean.valueRoundedWithUnit(1) % QStringLiteral("/") % m_pitchStdDev.valueRoundedWithUnit(1) % + QStringLiteral(" gs ") % m_gsMean.valueRoundedWithUnit(1) % QStringLiteral("/") % m_gsStdDev.valueRoundedWithUnit(1) % + QStringLiteral(" alt. ") % m_altMean.valueRoundedWithUnit(1) % QStringLiteral("/") % m_altStdDev.valueRoundedWithUnit(1) % + QStringLiteral(" elv. ") % m_elvMean.valueRoundedWithUnit(1) % QStringLiteral("/") % m_elvStdDev.valueRoundedWithUnit(1); + } + + CVariant CAircraftSituationChange::propertyByIndex(const CPropertyIndex &index) const + { + if (index.isMyself()) { return CVariant::from(*this); } + if (ITimestampWithOffsetBased::canHandleIndex(index)) { return ITimestampWithOffsetBased::propertyByIndex(index); } + + const ColumnIndex i = index.frontCasted(); + switch (i) + { + case IndexCallsign: return m_correspondingCallsign.propertyByIndex(index.copyFrontRemoved()); + case IndexConstAscending: return CVariant::from(m_constAscending); + case IndexConstDescending: return CVariant::from(m_constDescending); + case IndexConstNotOnGround: return CVariant::from(m_constNotOnGround); + case IndexConstOnGround: return CVariant::from(m_constOnGround); + case IndexIsNull: return CVariant::from(this->isNull()); + case IndexJustTakingOff: return CVariant::from(m_justTakeoff); + case IndexJustTouchingDown: return CVariant::from(m_justTouchdown); + case IndexRotatingUp: return CVariant::from(m_rotateUp); + case IndexContainsPushBack: return CVariant::from(m_containsPushBack); + default: return CValueObject::propertyByIndex(index); + } + } + + void CAircraftSituationChange::setPropertyByIndex(const CPropertyIndex &index, const CVariant &variant) + { + if (index.isMyself()) { (*this) = variant.to(); return; } + if (ITimestampWithOffsetBased::canHandleIndex(index)) { ITimestampWithOffsetBased::setPropertyByIndex(index, variant); return; } + + const ColumnIndex i = index.frontCasted(); + switch (i) + { + case IndexCallsign: m_correspondingCallsign.setPropertyByIndex(index.copyFrontRemoved(), variant); break; + case IndexConstAscending: m_constAscending = variant.toBool(); break; + case IndexConstDescending: m_constDescending = variant.toBool(); break; + case IndexConstNotOnGround: m_constNotOnGround = variant.toBool(); break; + case IndexConstOnGround: m_constOnGround = variant.toBool(); break; + case IndexJustTakingOff: m_justTakeoff = variant.toBool(); break; + case IndexJustTouchingDown: m_justTouchdown = variant.toBool(); break; + case IndexRotatingUp: m_rotateUp = variant.toBool(); break; + case IndexContainsPushBack: m_containsPushBack = variant.toBool(); break; + case IndexIsNull: break; + default: CValueObject::setPropertyByIndex(index, variant); break; + } + } + + bool CAircraftSituationChange::calculateStdDeviations(const CAircraftSituationList &situations) + { + if (situations.isEmpty()) { return false; } + + const QList altValues = situations.altitudeValues(CLengthUnit::ft()); + if (altValues.size() == situations.size()) + { + const QPair altFt = CMathUtils::standardDeviationAndMean(altValues); + m_altStdDev = CAltitude(altFt.first, CAltitude::MeanSeaLevel, CLengthUnit::ft()); + m_altMean = CAltitude(altFt.second, CAltitude::MeanSeaLevel, CLengthUnit::ft()); + } + + const QList elvValues = situations.elevationValues(CLengthUnit::ft()); + if (elvValues.size() == situations.size()) + { + const QPair elvFt = CMathUtils::standardDeviationAndMean(elvValues); + m_elvStdDev = CAltitude(elvFt.first, CAltitude::MeanSeaLevel, CLengthUnit::ft()); + m_elvMean = CAltitude(elvFt.second, CAltitude::MeanSeaLevel, CLengthUnit::ft()); + + if (altValues.size() == situations.size()) + { + QList altElvDeltas; + for (int i = 0; i < altValues.size(); i++) + { + const double delta = altValues[i] - elvValues[i]; + altElvDeltas.push_back(delta); + } + const QPair deltaFt = CMathUtils::standardDeviationAndMean(altElvDeltas); + m_altAglStdDev = CLength(deltaFt.first, CLengthUnit::ft()); + m_altAglMean = CLength(deltaFt.second, CLengthUnit::ft()); + } + } + + const QList gsValues = situations.groundSpeedValues(CSpeedUnit::kts()); + if (gsValues.size() == situations.size()) + { + const QPair gsKts = CMathUtils::standardDeviationAndMean(gsValues); + m_gsStdDev = CSpeed(gsKts.first, CSpeedUnit::kts()); + m_gsMean = CSpeed(gsKts.second, CSpeedUnit::kts()); + } + + const QList pitchValues = situations.pitchValues(CAngleUnit::deg()); + if (gsValues.size() == situations.size()) + { + const QPair pitchDeg = CMathUtils::standardDeviationAndMean(pitchValues); + m_pitchStdDev = CAngle(pitchDeg.first, CAngleUnit::deg()); + m_pitchMean = CAngle(pitchDeg.second, CAngleUnit::deg()); + } + return true; + } + + const CAircraftSituationChange &CAircraftSituationChange::null() + { + static const CAircraftSituationChange null; + return null; + } + } // namespace +} // namespace diff --git a/src/blackmisc/aviation/aircraftsituationchange.h b/src/blackmisc/aviation/aircraftsituationchange.h new file mode 100644 index 000000000..096d713fd --- /dev/null +++ b/src/blackmisc/aviation/aircraftsituationchange.h @@ -0,0 +1,204 @@ +/* Copyright (C) 2018 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +//! \file + +#ifndef BLACKMISC_AVIATION_AIRCRAFTSITUATIONCHANGE_H +#define BLACKMISC_AVIATION_AIRCRAFTSITUATIONCHANGE_H + +#include "callsign.h" +#include "altitude.h" +#include "blackmisc/pq/angle.h" +#include "blackmisc/pq/speed.h" +#include "blackmisc/metaclass.h" +#include "blackmisc/timestampbased.h" +#include "blackmisc/valueobject.h" +#include "blackmisc/variant.h" +#include "blackmisc/blackmiscexport.h" +#include "blackmisc/propertyindex.h" +#include + +namespace BlackMisc +{ + namespace Aviation + { + class CAircraftSituation; + class CAircraftSituationList; + + //! Value object about changes in situations + class BLACKMISC_EXPORT CAircraftSituationChange : + public CValueObject, + public ITimestampWithOffsetBased + { + public: + //! Properties by index + enum ColumnIndex + { + IndexCallsign = CPropertyIndex::GlobalIndexCAircraftSituationChange, + IndexIsNull, + IndexSituationsCount, + IndexConstAscending, + IndexConstDescending, + IndexConstOnGround, + IndexConstNotOnGround, + IndexJustTakingOff, + IndexJustTouchingDown, + IndexRotatingUp, + IndexContainsPushBack + }; + + //! Default constructor. + CAircraftSituationChange(); + + //! Ctor with 2 situations + CAircraftSituationChange(const CAircraftSituation &s1, const CAircraftSituation &s2); + + //! Ctor with n situations + CAircraftSituationChange(const CAircraftSituationList &situations, bool alreadySortedLatestFirst = false, bool calcStdDeviations = false); + + //! Get callsign + const CCallsign &getCallsign() const { return m_correspondingCallsign; } + + //! Null? + bool isNull() const { return m_situationsCount < 2; } // we need at least 2 situations + + //! Basend on n situations + int getSituationsCount() const { return m_situationsCount; } + + //! \copydoc BlackMisc::Aviation::CAircraftSituationList::isConstAscending + bool isConstAscending() const { return m_constAscending; } + + //! \copydoc BlackMisc::Aviation::CAircraftSituationList::isConstDescending + bool isConstDescending() const { return m_constDescending; } + + //! \copydoc BlackMisc::Aviation::CAircraftSituationList::isConstOnGround + bool isConstOnGround() const { return m_constOnGround; } + + //! Was on ground (without latest situation)? + bool wasConstOnGround() const { return m_wasOnGround; } + + //! \copydoc BlackMisc::Aviation::CAircraftSituationList::isConstNotOnGround + bool isConstNotOnGround() const { return m_constNotOnGround; } + + //! Was not on ground (without latest situation)? + bool wasConstNotOnGround() const { return m_wasNotOnGround; } + + //! \copydoc BlackMisc::Aviation::CAircraftSituationList::isConstAccelerating + bool isConstAccelerating() const { return m_constAccelerating; } + + //! \copydoc BlackMisc::Aviation::CAircraftSituationList::isConstDecelarating + bool isConstDecelarating() const { return m_constAccelerating; } + + //! \copydoc BlackMisc::Aviation::CAircraftSituationList::isJustTakingOff + bool isJustTakingOff() const { return m_justTakeoff; } + + //! \copydoc BlackMisc::Aviation::CAircraftSituationList::isJustTouchingDown + bool isJustTouchingDown() const { return m_justTouchdown; } + + //! \copydoc BlackMisc::Aviation::CAircraftSituationList::isRotatingUp + bool isRotatingUp() const { return m_rotateUp; } + + //! \copydoc BlackMisc::Aviation::CAircraftSituationList::containsPushBack + bool containsPushBack() const { return m_containsPushBack; } + + //! AGL if it can be calculated, otherwise NULL + //! \note distance is without CG, so on ground it can also be used to calculate + QPair getAltAglStdDevAndMean() const { return QPair(m_altAglStdDev, m_altAglMean); } + + //! Altitude values + QPair getAltitudeStdDevAndMean() const { return QPair(m_altStdDev, m_altMean); } + + //! Elevation values + QPair getElevationStdDevAndMean() const { return QPair(m_elvStdDev, m_elvMean); } + + //! Ground speed values + QPair getGroundSpeedStdDevAndMean() const { return QPair(m_gsStdDev, m_gsMean); } + + //! Pitch values + QPair getPitchStdDevAndMean() const { return QPair(m_pitchStdDev, m_pitchMean); } + + //! \copydoc Mixin::String::toQString + QString convertToQString(bool i18n = false) const; + + //! \copydoc Mixin::Index::propertyByIndex + CVariant propertyByIndex(const CPropertyIndex &index) const; + + //! \copydoc Mixin::Index::setPropertyByIndex + void setPropertyByIndex(const CPropertyIndex &index, const CVariant &variant); + + //! Calculate the standard deviiations + bool calculateStdDeviations(const CAircraftSituationList &situations); + + //! NULL object + static const CAircraftSituationChange &null(); + + private: + int m_situationsCount = -1; + CCallsign m_correspondingCallsign; + // latest -> m_timestampMSecsSinceEpoch + qint64 m_oldestTimestampMSecsSinceEpoch = -1; + qint64 m_oldestAdjustedTimestampMSecsSinceEpoch = -1; + qint64 m_latestAdjustedTimestampMSecsSinceEpoch = -1; + bool m_constAscending = false; + bool m_constDescending = false; + bool m_constOnGround = false; + bool m_wasOnGround = false; + bool m_constNotOnGround = false; + bool m_wasNotOnGround = false; + bool m_justTakeoff = false; + bool m_justTouchdown = false; + bool m_rotateUp = false; + bool m_constAccelerating = false; + bool m_constDecelerating = false; + bool m_containsPushBack = false; + CAltitude m_altStdDev = CAltitude::null(); + CAltitude m_altMean = CAltitude::null(); + CAltitude m_elvStdDev = CAltitude::null(); + CAltitude m_elvMean = CAltitude::null(); + PhysicalQuantities::CSpeed m_gsStdDev = PhysicalQuantities::CSpeed::null(); + PhysicalQuantities::CSpeed m_gsMean = PhysicalQuantities::CSpeed::null(); + PhysicalQuantities::CAngle m_pitchStdDev = PhysicalQuantities::CAngle::null(); + PhysicalQuantities::CAngle m_pitchMean = PhysicalQuantities::CAngle::null(); + PhysicalQuantities::CLength m_altAglStdDev = PhysicalQuantities::CLength::null(); + PhysicalQuantities::CLength m_altAglMean = PhysicalQuantities::CLength::null(); + + BLACK_METACLASS( + CAircraftSituationChange, + BLACK_METAMEMBER(situationsCount), + BLACK_METAMEMBER(correspondingCallsign), + BLACK_METAMEMBER(constAscending), + BLACK_METAMEMBER(constDescending), + BLACK_METAMEMBER(constOnGround), + BLACK_METAMEMBER(constNotOnGround), + BLACK_METAMEMBER(justTakeoff), + BLACK_METAMEMBER(justTouchdown), + BLACK_METAMEMBER(containsPushBack), + BLACK_METAMEMBER(rotateUp), + BLACK_METAMEMBER(altStdDev), + BLACK_METAMEMBER(altMean), + BLACK_METAMEMBER(elvStdDev), + BLACK_METAMEMBER(elvMean), + BLACK_METAMEMBER(gsStdDev), + BLACK_METAMEMBER(gsMean), + BLACK_METAMEMBER(altAglStdDev), + BLACK_METAMEMBER(altAglMean), + BLACK_METAMEMBER(pitchStdDev), + BLACK_METAMEMBER(pitchMean), + BLACK_METAMEMBER(timestampMSecsSinceEpoch), + BLACK_METAMEMBER(oldestTimestampMSecsSinceEpoch), + BLACK_METAMEMBER(oldestAdjustedTimestampMSecsSinceEpoch), + BLACK_METAMEMBER(latestAdjustedTimestampMSecsSinceEpoch) + ); + }; + } // namespace +} // namespace + +Q_DECLARE_METATYPE(BlackMisc::Aviation::CAircraftSituationChange) + +#endif // guard diff --git a/src/blackmisc/aviation/aviation.h b/src/blackmisc/aviation/aviation.h index 2acbfb57f..6e971ce1b 100644 --- a/src/blackmisc/aviation/aviation.h +++ b/src/blackmisc/aviation/aviation.h @@ -29,6 +29,7 @@ #include "blackmisc/aviation/aircrafticaocode.h" #include "blackmisc/aviation/aircrafticaocodelist.h" #include "blackmisc/aviation/aircraftsituation.h" +#include "blackmisc/aviation/aircraftsituationchange.h" #include "blackmisc/aviation/aircraftsituationlist.h" #include "blackmisc/aviation/airlineicaocode.h" #include "blackmisc/aviation/airlineicaocodelist.h" diff --git a/src/blackmisc/aviation/registermetadataaviation.cpp b/src/blackmisc/aviation/registermetadataaviation.cpp index 9db3bc14e..2df662bcb 100644 --- a/src/blackmisc/aviation/registermetadataaviation.cpp +++ b/src/blackmisc/aviation/registermetadataaviation.cpp @@ -27,6 +27,8 @@ namespace BlackMisc CAircraftSituation::registerMetadata(); qRegisterMetaType(); qRegisterMetaType(); + qRegisterMetaType(); + CAircraftSituationChange::registerMetadata(); CAircraftSituationList::registerMetadata(); CAirlineIcaoCode::registerMetadata(); CAirlineIcaoCodeList::registerMetadata(); @@ -34,15 +36,19 @@ namespace BlackMisc CAirport::registerMetadata(); CAirportList::registerMetadata(); CAltitude::registerMetadata(); + qRegisterMetaType(); + qRegisterMetaType(); CAtcStation::registerMetadata(); CAtcStationList::registerMetadata(); CCallsign::registerMetadata(); + qRegisterMetaType(); CCallsignSet::registerMetadata(); CComSystem::registerMetadata(); CFlightPlan::registerMetadata(); CFlightPlanList::registerMetadata(); CFlightPlanRemarks::registerMetadata(); CHeading::registerMetadata(); + qRegisterMetaType(); CInformationMessage::registerMetadata(); CLivery::registerMetadata(); CLiveryList::registerMetadata(); diff --git a/src/blackmisc/math/mathutils.cpp b/src/blackmisc/math/mathutils.cpp index 5b4a6d856..efe0e6070 100644 --- a/src/blackmisc/math/mathutils.cpp +++ b/src/blackmisc/math/mathutils.cpp @@ -8,6 +8,7 @@ */ #include "blackmisc/math/mathutils.h" +#include "blackmisc/verify.h" #include #include @@ -110,6 +111,13 @@ namespace BlackMisc return r % ((high + 1) - low) + low; } + double CMathUtils::randomDouble(double max) + { + static const int MAX(INT_MAX); + const double r = randomInteger(0, MAX); + return (r / MAX) * max; + } + int CMathUtils::roundToMultipleOf(int value, int divisor) { Q_ASSERT(divisor != 0); @@ -136,5 +144,55 @@ namespace BlackMisc if (fInt.length() >= width) { return fInt.left(width); } return fInt.leftJustified(width, '0'); } + + double CMathUtils::sum(const QList &values) + { + double sum = 0; + for (double v : values) { sum += v; } + return sum; + } + + QList CMathUtils::squaredDifferences(const QList &values) + { + const double meanValue = mean(values); + return squaredDifferences(values, meanValue); + } + + QList CMathUtils::squaredDifferences(const QList &values, double meanValue) + { + QList squaresDifferences; + for (double v : values) + { + const double vd = v - meanValue; + squaresDifferences.push_back(vd * vd); + } + return squaresDifferences; + } + + double CMathUtils::mean(const QList &values) + { + BLACK_VERIFY_X(!values.isEmpty(), Q_FUNC_INFO, "Need values"); + return sum(values) / values.size(); + } + + double CMathUtils::variance(const QList &values) + { + const double variance = mean(squaredDifferences(values)); + return variance; + } + + double CMathUtils::standardDeviation(const QList &values) + { + const double sd = sqrt(variance(values)); + return sd; + } + + QPair CMathUtils::standardDeviationAndMean(const QList &values) + { + const double meanValue = mean(values); + const double varianceValue = mean(squaredDifferences(values, meanValue)); + const double sd = sqrt(varianceValue); + return QPair(sd, meanValue); + } } // namespace } // namespace diff --git a/src/blackmisc/math/mathutils.h b/src/blackmisc/math/mathutils.h index 666669a46..2b136e1c2 100644 --- a/src/blackmisc/math/mathutils.h +++ b/src/blackmisc/math/mathutils.h @@ -15,6 +15,7 @@ #include "blackmisc/blackmiscexport.h" #include +#include #include namespace BlackMisc @@ -25,7 +26,6 @@ namespace BlackMisc class BLACKMISC_EXPORT CMathUtils { public: - //! No objects, just static CMathUtils() = delete; @@ -108,12 +108,37 @@ namespace BlackMisc //! Random number between low and high static int randomInteger(int low, int high); + //! Random double 0-max + static double randomDouble(double max = 1); + //! Round numToRound to the nearest multiple of divisor static int roundToMultipleOf(int value, int divisor); //! Fractional part as integer string, e.g. 3.12 -> 12 / 3.012 -> 012 //! \remark because of leading 0 returned as string static QString fractionalPartAsString(double value, int width = -1); + + //! Calculate the sum + static double sum(const QList &values); + + //! Calculate the mean + static double mean(const QList &values); + + //! Calculate the standard deviation + static double standardDeviation(const QList &values); + + //! Standard deviation (first) and mean (second) + static QPair standardDeviationAndMean(const QList &values); + + private: + //! Calculate the variance + static double variance(const QList &values); + + //! The squared differences to mean + static QList squaredDifferences(const QList &values); + + //! The squared differences to mean + static QList squaredDifferences(const QList &values, double meanValue); }; } // namespace } // namespace diff --git a/src/blackmisc/propertyindex.h b/src/blackmisc/propertyindex.h index 7aaac04f6..ce019c8ba 100644 --- a/src/blackmisc/propertyindex.h +++ b/src/blackmisc/propertyindex.h @@ -89,12 +89,13 @@ namespace BlackMisc GlobalIndexCPlatform = 1000, GlobalIndexCCallsign = 2000, GlobalIndexCAircraftSituation = 2100, - GlobalIndexCAtcStation = 2200, - GlobalIndexCAirport = 2300, - GlobalIndexCAircraftParts = 2400, - GlobalIndexCAircraftLights = 2500, - GlobalIndexCLivery = 2600, - GlobalIndexCFlightPlan = 2700, + GlobalIndexCAircraftSituationChange = 2200, + GlobalIndexCAtcStation = 2300, + GlobalIndexCAirport = 2400, + GlobalIndexCAircraftParts = 2500, + GlobalIndexCAircraftLights = 2600, + GlobalIndexCLivery = 2700, + GlobalIndexCFlightPlan = 2800, GlobalIndexCComSystem = 3000, GlobalIndexCModulator = 3100, GlobalIndexCTransponder = 3200,