Ref T397, checks on vector to find root cause NaN/inf issue

* sometimes very large numbers (xxE38) in vector
* sometimes vectors which look correct, but exceed [-1,1]
This commit is contained in:
Klaus Basan
2018-10-10 15:03:31 +02:00
parent 4038729e82
commit dec0b12ea2
10 changed files with 98 additions and 9 deletions

View File

@@ -38,6 +38,7 @@
#include "blackmisc/variant.h"
#include "blackmisc/verify.h"
#include "blackmisc/worker.h"
#include "blackconfig/buildconfig.h"
#include <QCoreApplication>
#include <QDateTime>
@@ -49,6 +50,7 @@
#include <QWriteLocker>
#include <Qt>
using namespace BlackConfig;
using namespace BlackMisc;
using namespace BlackMisc::Audio;
using namespace BlackMisc::Aviation;
@@ -930,15 +932,23 @@ namespace BlackCore
if (!this->isConnectedAndNotShuttingDown()) { return; }
const CCallsign callsign(situation.getCallsign());
// checks
Q_ASSERT_X(!callsign.isEmpty(), Q_FUNC_INFO, "Empty callsign");
if (CBuildConfig::isLocalDeveloperDebugBuild())
{
Q_ASSERT_X(!situation.isNaNVectorDouble(), Q_FUNC_INFO, "Detected NaN");
Q_ASSERT_X(!situation.isInfVectorDouble(), Q_FUNC_INFO, "Detected inf");
Q_ASSERT_X(situation.isValidVectorRange(), Q_FUNC_INFO, "out of range [-1,1]");
}
// Interim packets do not have groundspeed, hence set the last known value.
// If there is no full position available yet, throw this interim position away.
CAircraftSituation interimSituation(situation);
CAircraftSituationList history = this->remoteAircraftSituations(callsign);
if (history.empty()) { return; } // we need one full situation at least
const CAircraftSituation lastSituation = history.latestObject();
const bool samePosition = (lastSituation.getPosition() == interimSituation.getPosition());
// changed position, continue and copy values
interimSituation.setCurrentUtcTime();
@@ -949,6 +959,8 @@ namespace BlackCore
// if we have no aircraft in range yet, we stop here
if (!this->isAircraftInRange(callsign)) { return; }
const bool samePosition = lastSituation.equalNormalVectorDouble(interimSituation);
if (samePosition) { return; } // nothing to update
// update aircraft

View File

@@ -90,7 +90,7 @@ namespace BlackGui
}
if (m_withMenuFastPosition)
{
menuActions.addAction(CIcons::globe16(), aircraft.fastPositionUpdates() ? "Normal updates" : "Fast position updates", CMenuAction::pathClientSimulationTransfer(), { this, &CSimulatedAircraftView::toggleFastPositionUpdates });
menuActions.addAction(CIcons::globe16(), aircraft.fastPositionUpdates() ? "Normal updates" : "Fast position updates (send)", CMenuAction::pathClientSimulationTransfer(), { this, &CSimulatedAircraftView::toggleFastPositionUpdates });
}
const bool any = m_withMenuEnableAircraft || m_withMenuFastPosition || m_withMenuHighlightAndFollow || m_withMenuEnableGndFlag;
if (any && (sApp && sApp->isDeveloperFlagSet()))

View File

@@ -243,16 +243,24 @@ namespace BlackMisc
CElevationPlane CAircraftSituation::interpolatedElevation(const CAircraftSituation &situation, const CAircraftSituation &oldSituation, const CAircraftSituation &newSituation, const CLength &distance)
{
if (oldSituation.isNull() || newSituation.isNull()) { return CAircraftSituation::null(); }
if (oldSituation.isNull() || newSituation.isNull()) { return CElevationPlane::null(); }
if (!oldSituation.hasGroundElevation() || !newSituation.hasGroundElevation()) { return CElevationPlane::null(); }
if (oldSituation.equalNormalVectorDouble(newSituation)) { return newSituation.getGroundElevationPlane(); }
const double newElvFt = newSituation.getGroundElevation().value(CLengthUnit::ft());
const double oldElvFt = oldSituation.getGroundElevation().value(CLengthUnit::ft());
const double deltaElvFt = newElvFt - oldElvFt;
if (deltaElvFt > 25) { return CElevationPlane::null(); }
if (deltaElvFt > MaxDeltaElevationFt) { return CElevationPlane::null(); } // threshold, interpolation not possible
if (!situation.isNull())
{
if (CBuildConfig::isLocalDeveloperDebugBuild())
{
Q_ASSERT_X(situation.isValidVectorRange(), Q_FUNC_INFO, "Invalid range");
Q_ASSERT_X(oldSituation.isValidVectorRange(), Q_FUNC_INFO, "Invalid range");
Q_ASSERT_X(newSituation.isValidVectorRange(), Q_FUNC_INFO, "Invalid range");
}
const double distanceSituationNewM = situation.calculateGreatCircleDistance(newSituation).value(CLengthUnit::m());
if (distanceSituationNewM < 5.0) { return newSituation.getGroundElevationPlane(); }
@@ -261,7 +269,7 @@ namespace BlackMisc
const double distRatio = distanceSituationNewM / distanceOldNewM;
// very close to the situations we return tehir elevation
// very close to the situations we return their elevation
if (distRatio < 0.05) { return newSituation.getGroundElevationPlane(); }
if (distRatio > 0.95) { return oldSituation.getGroundElevationPlane(); }

View File

@@ -550,9 +550,12 @@ namespace BlackMisc
static bool extrapolateElevation(CAircraftSituation &newSituation, const CAircraftSituation &oldSituation, const CAircraftSituation &olderSituation, const CAircraftSituationChange &oldChange);
//! Interpolate between the 2 situations for situation
//! \remark NULL if there are no two elevations
//! \remark NULL if there are no two elevations or threshold MaxDeltaElevationFt is exceeded
static Geo::CElevationPlane interpolatedElevation(const CAircraftSituation &situation, const CAircraftSituation &oldSituation, const CAircraftSituation &newSituation, const PhysicalQuantities::CLength &distance = PhysicalQuantities::CLength::null());
//! Threshold until we interpolate elevations
static constexpr double MaxDeltaElevationFt = 25.0;
//! Register metadata
static void registerMetadata();

View File

@@ -46,10 +46,12 @@ namespace BlackMisc
if (coordinate1.isNull() || coordinate2.isNull()) { return CLength::null(); }
// if (coordinate1.equalNormalVectorDouble(coordinate2)) { return CLength(0, CLengthUnit::defaultUnit()); }
static const float earthRadiusMeters = 6371000.8f;
const QVector3D v1 = coordinate1.normalVector();
const QVector3D v2 = coordinate2.normalVector();
Q_ASSERT_X(std::isfinite(v1.x()) && std::isfinite(v1.y()) && std::isfinite(v1.z()), Q_FUNC_INFO, "Distance calculation: v1 non-finite argument");
Q_ASSERT_X(std::isfinite(v2.x()) && std::isfinite(v2.y()) && std::isfinite(v2.z()), Q_FUNC_INFO, "Distance calculation: v2 non-finite argument");
Q_ASSERT_X(!std::isnan(v1.x()) && !std::isnan(v1.y()) && !std::isnan(v1.z()) && !std::isnan(v2.x()) && !std::isnan(v2.y()) && !std::isnan(v2.z()), Q_FUNC_INFO, "Distance calculation: NaN in argument");
const float d = earthRadiusMeters * std::atan2(QVector3D::crossProduct(v1, v2).length(), QVector3D::dotProduct(v1, v2));
BLACK_VERIFY_X(!std::isnan(d), Q_FUNC_INFO, "Distance calculation: NaN in result");
@@ -183,6 +185,44 @@ namespace BlackMisc
this->geodeticHeight().valueRoundedWithUnit(CLengthUnit::ft(), 2, i18n));
}
bool ICoordinateGeodetic::isNaNVector() const
{
const QVector3D v = this->normalVector();
return std::isnan(v.x()) || std::isnan(v.y()) || std::isnan(v.z());
}
bool ICoordinateGeodetic::isNaNVectorDouble() const
{
const std::array<double, 3> v = this->normalVectorDouble();
return std::isnan(v[0]) || std::isnan(v[1]) || std::isnan(v[2]);
}
bool ICoordinateGeodetic::isInfVector() const
{
const QVector3D v = this->normalVector();
return std::isinf(v.x()) || std::isinf(v.y()) || std::isinf(v.z());
}
bool ICoordinateGeodetic::isInfVectorDouble() const
{
const std::array<double, 3> v = this->normalVectorDouble();
return std::isinf(v[0]) || std::isinf(v[1]) || std::isinf(v[2]);
}
bool ICoordinateGeodetic::isValidVectorRange() const
{
// inf is out of range, comparing nans is always false
const std::array<double, 3> v = this->normalVectorDouble();
return isValidVector(v);
}
bool ICoordinateGeodetic::isValidVector(const std::array<double, 3> &v)
{
constexpr double l = 1.00001; // because of interpolation
return v[0] <= l && v[1] <= l && v[2] <= l &&
v[0] >= -l && v[1] >= -l && v[2] >= -l;
}
CVariant CCoordinateGeodetic::propertyByIndex(const BlackMisc::CPropertyIndex &index) const
{
if (index.isMyself()) { return CVariant::from(*this); }

View File

@@ -127,6 +127,15 @@ namespace BlackMisc
//! \copydoc Mixin::String::toQString
QString convertToQString(bool i18n = false) const;
//! Check values @{
bool isNaNVector() const;
bool isNaNVectorDouble() const;
bool isInfVector() const;
bool isInfVectorDouble() const;
bool isValidVectorRange() const;
static bool isValidVector(const std::array<double, 3> &v);
//! @}
protected:
//! Can given index be handled?
static bool canHandleIndex(const CPropertyIndex &index);

View File

@@ -196,6 +196,10 @@ namespace BlackMisc
// use derived interpolant function
const bool interpolateGndFlag = pbh.getNewSituation().hasGroundDetailsForGndInterpolation() && pbh.getOldSituation().hasGroundDetailsForGndInterpolation();
currentSituation = interpolant.interpolatePositionAndAltitude(currentSituation, interpolateGndFlag);
if (CBuildConfig::isLocalDeveloperDebugBuild())
{
Q_ASSERT_X(currentSituation.isValidVectorRange(), Q_FUNC_INFO, "Invalid interpolation situation");
}
if (!interpolateGndFlag) { currentSituation.guessOnGround(CAircraftSituationChange::null(), m_model); }
// as we now have the position and can interpolate elevation
@@ -446,6 +450,7 @@ namespace BlackMisc
CAircraftSituation CInterpolator<Derived>::initInterpolatedSituation(const CAircraftSituation &oldSituation, const CAircraftSituation &newSituation) const
{
if (m_currentSituations.isEmpty()) { return CAircraftSituation::null(); }
CAircraftSituation currentSituation = m_lastSituation.isNull() ? m_currentSituations.front() : m_lastSituation;
if (currentSituation.getCallsign() != m_callsign)
{
@@ -453,6 +458,11 @@ namespace BlackMisc
currentSituation.setCallsign(m_callsign);
}
if (CBuildConfig::isLocalDeveloperDebugBuild())
{
Q_ASSERT_X(currentSituation.isValidVectorRange(), Q_FUNC_INFO, "Invalid range");
}
// preset elevation here, as we do not know where the situation will be after the interpolation step!
const bool preset = currentSituation.presetGroundElevation(oldSituation, newSituation, m_pastSituationsChange);
Q_UNUSED(preset);

View File

@@ -281,6 +281,13 @@ namespace BlackMisc
const double newY = evalSplineInterval(m_currentTimeMsSinceEpoc, t1, t2, m_pa.y[1], m_pa.y[2], m_pa.dy[1], m_pa.dy[2]);
const double newZ = evalSplineInterval(m_currentTimeMsSinceEpoc, t1, t2, m_pa.z[1], m_pa.z[2], m_pa.dz[1], m_pa.dz[2]);
if (CBuildConfig::isLocalDeveloperDebugBuild())
{
Q_ASSERT_X(CAircraftSituation::isValidVector(m_pa.x), Q_FUNC_INFO, "invalid X"); // all x values
Q_ASSERT_X(CAircraftSituation::isValidVector(m_pa.y), Q_FUNC_INFO, "invalid Y"); // all y values
Q_ASSERT_X(CAircraftSituation::isValidVector(m_pa.z), Q_FUNC_INFO, "invalid Z"); // all z values
}
CAircraftSituation newSituation(currentSituation);
const std::array<double, 3> normalVector = {{ newX, newY, newZ }};
const CCoordinateGeodetic currentPosition(normalVector);

View File

@@ -76,7 +76,7 @@ namespace BlackMisc
void setTimes(qint64 currentTimeMs, double timeFraction, qint64 interpolatedTimeMs);
private:
PosArray m_pa; //! current positions array, latest values last
PosArray m_pa; //!< current positions array, latest values last
PhysicalQuantities::CLengthUnit m_altitudeUnit;
qint64 m_currentTimeMsSinceEpoc { -1 };
};

View File

@@ -262,7 +262,7 @@ namespace BlackMisc
BLACK_VERIFY_X(situation.getTimeOffsetMs() > 0, Q_FUNC_INFO, "Missing offset");
}
// add offset (for testing only)
// add altitude offset (for testing only)
CAircraftSituation situationCorrected(allowTestOffset ? this->addTestAltitudeOffsetToSituation(situation) : situation);
// CG, model