From 228744539cd28aa157eccbb86c14ed7cc629f275 Mon Sep 17 00:00:00 2001 From: Oliver Knoll Date: Sat, 11 May 2024 12:40:29 +0200 Subject: [PATCH 1/9] Separate aircraft attitude from position data (WIP) - MSFSSimConnectPlugin does not compile yet - Data needs to be migrated - Table schemas need to be adjusted (-> table attitude) --- src/Flight/src/FlightAugmentation.cpp | 126 +++++----- src/Model/CMakeLists.txt | 3 +- src/Model/include/Model/AbstractComponent.h | 5 + src/Model/include/Model/Aircraft.h | 2 + src/Model/include/Model/Attitude.h | 45 ++++ src/Model/include/Model/AttitudeData.h | 48 ++++ src/Model/include/Model/InitialPosition.h | 17 +- src/Model/include/Model/PositionData.h | 8 - src/Model/include/Model/TimeVariableData.h | 4 +- src/Model/src/Aircraft.cpp | 27 ++- src/Model/src/AircraftHandle.cpp | 6 +- src/Model/src/Attitude.cpp | 92 ++++++++ src/Model/src/AttitudeData.cpp | 34 +++ src/Model/src/Engine.cpp | 6 +- src/Model/src/EngineData.cpp | 10 +- src/Model/src/Flight.cpp | 4 +- src/Model/src/InitialPosition.cpp | 5 +- src/Model/src/Light.cpp | 6 +- src/Model/src/Location.cpp | 8 +- src/Model/src/Position.cpp | 17 +- src/Model/src/PositionData.cpp | 11 +- src/Model/src/PrimaryFlightControl.cpp | 6 +- src/Model/src/SecondaryFlightControl.cpp | 6 +- src/Model/src/Waypoint.cpp | 8 +- src/Persistence/CMakeLists.txt | 2 + src/Persistence/src/Dao/AttitudeDaoIntf.h | 58 +++++ .../src/Dao/SQLite/SQLiteAttitudeDao.cpp | 222 ++++++++++++++++++ .../src/Dao/SQLite/SQLiteAttitudeDao.h | 58 +++++ .../src/Dao/SQLite/SQLitePositionDao.cpp | 78 ++---- .../PluginManager/Connect/SkyConnectIntf.h | 3 +- .../include/PluginManager/SkyConnectManager.h | 4 +- .../src/Connect/AbstractSkyConnect.cpp | 19 +- src/PluginManager/src/Export.cpp | 24 +- .../src/Flight/FlightImportPluginBase.cpp | 20 +- src/PluginManager/src/SkyConnectManager.cpp | 6 +- .../src/MSFSSimConnectPlugin.cpp | 6 +- .../src/MSFSSimConnectPlugin.h | 2 +- .../MSFSSimConnectPlugin/src/SimConnectAi.cpp | 2 +- .../PathCreator/src/PathCreatorPlugin.cpp | 61 +++-- .../PathCreator/src/PathCreatorPlugin.h | 3 +- .../CsvExport/src/FlightRadar24CsvWriter.cpp | 17 +- .../src/PositionAndAttitudeCsvWriter.cpp | 17 +- .../Export/IgcExport/src/IgcExportPlugin.cpp | 18 +- .../Export/KmlExport/src/KmlExportPlugin.cpp | 16 +- .../Export/KmlExport/src/KmlExportPlugin.h | 3 +- .../CsvImport/src/FlightRadar24CsvParser.cpp | 35 +-- .../CsvImport/src/FlightRadar24CsvParser.h | 4 +- .../CsvImport/src/FlightRecorderCsvParser.cpp | 122 +++++----- .../Flight/Import/GpxImport/src/GpxParser.cpp | 6 +- .../Import/IgcImport/src/IgcImportPlugin.cpp | 4 +- .../KmlImport/src/FlightAwareKmlParser.cpp | 2 +- .../KmlImport/src/FlightRadar24KmlParser.cpp | 18 +- .../Module/Formation/src/Formation.cpp | 80 +++---- src/Plugins/Module/Formation/src/Formation.h | 6 +- .../Module/Formation/src/FormationWidget.cpp | 62 +++-- src/UserInterface/src/MainWindow.cpp | 4 +- .../AbstractSimulationVariableWidget.cpp | 20 +- .../src/Widget/AircraftHandleWidget.cpp | 32 ++- .../src/Widget/AircraftInfoWidget.cpp | 2 +- .../src/Widget/AircraftWidget.cpp | 114 +++++---- src/UserInterface/src/Widget/AircraftWidget.h | 4 +- src/UserInterface/src/Widget/EngineWidget.cpp | 32 ++- src/UserInterface/src/Widget/LightWidget.cpp | 32 ++- .../src/Widget/PrimaryFlightControlWidget.cpp | 32 ++- .../Widget/SecondaryFlightControlWidget.cpp | 32 ++- test/KernelTest/src/SkyMathTest.cpp | 2 +- .../src/AbstractFlightImportTest.cpp | 2 +- 67 files changed, 1209 insertions(+), 581 deletions(-) create mode 100644 src/Model/include/Model/Attitude.h create mode 100644 src/Model/include/Model/AttitudeData.h create mode 100644 src/Model/src/Attitude.cpp create mode 100644 src/Model/src/AttitudeData.cpp create mode 100644 src/Persistence/src/Dao/AttitudeDaoIntf.h create mode 100644 src/Persistence/src/Dao/SQLite/SQLiteAttitudeDao.cpp create mode 100644 src/Persistence/src/Dao/SQLite/SQLiteAttitudeDao.h diff --git a/src/Flight/src/FlightAugmentation.cpp b/src/Flight/src/FlightAugmentation.cpp index 8fea2d9f8..eacf82cf2 100644 --- a/src/Flight/src/FlightAugmentation.cpp +++ b/src/Flight/src/FlightAugmentation.cpp @@ -31,6 +31,8 @@ #include #include #include +#include +#include #include #include #include @@ -39,6 +41,7 @@ #include #include #include +#include #include "Analytics.h" #include "FlightAugmentation.h" @@ -111,117 +114,133 @@ void FlightAugmentation::augmentAircraftData(Aircraft &aircraft) noexcept void FlightAugmentation::augmentAttitudeAndVelocity(Aircraft &aircraft) noexcept { + using enum TimeVariableData::Access; + Position &position = aircraft.getPosition(); + Attitude &attitude = aircraft.getAttitude(); const auto positionCount = position.count(); + const auto attitudeCount = attitude.count(); Analytics analytics(aircraft); const auto [firstMovementTimestamp, firstMovementHeading] = analytics.firstMovementHeading(); - for (int i = 0; i < positionCount; ++i) { - if (i < positionCount - 1) { + // Ensure that attitude data exists if any attitude or velocity aspect has to be augmented + if (attitudeCount == 0 && (d->aspects & Aspect::AttitudeAndVelocity)) { + AttitudeData item; + attitude.insert(positionCount, item); + for (int i = 0; i < positionCount; ++i) { + attitude[i].timestamp = position[i].timestamp; + } + } + + for (int i = 0; i < attitudeCount; ++i) { - PositionData &startPositionData = position[i]; - const PositionData &endPositionData = position[i + 1]; - const SkyMath::Coordinate startPosition(startPositionData.latitude, startPositionData.longitude); - const auto startTimestamp = startPositionData.timestamp; - const SkyMath::Coordinate endPosition {endPositionData.latitude, endPositionData.longitude}; - const auto endTimestamp = endPositionData.timestamp; + if (i < attitudeCount - 1) { + AttitudeData currentAttitudeData = attitude[i]; + const auto currentTimestamp = currentAttitudeData.timestamp; + const auto nextTimeStamp = attitude[i + 1].timestamp; - const auto [distance, speed] = SkyMath::distanceAndSpeed(startPosition, startTimestamp, endPosition, endTimestamp); + const PositionData ¤tPositionData = position.interpolate(currentTimestamp, NoTimeOffset); + const PositionData &nextPositionData = position.interpolate(nextTimeStamp, NoTimeOffset); + const SkyMath::Coordinate currentPosition {currentPositionData.latitude, currentPositionData.longitude}; + const SkyMath::Coordinate nextPosition {nextPositionData.latitude, nextPositionData.longitude}; + + + const auto [distance, speed] = SkyMath::distanceAndSpeed(currentPosition, currentTimestamp, nextPosition, nextTimeStamp); // Velocity if (d->aspects.testFlag(Aspect::Velocity)) { - startPositionData.velocityBodyX = 0.0; - startPositionData.velocityBodyY = 0.0; - startPositionData.velocityBodyZ = Convert::metersPerSecondToFeetPerSecond(speed); + currentAttitudeData.velocityBodyX = 0.0; + currentAttitudeData.velocityBodyY = 0.0; + currentAttitudeData.velocityBodyZ = Convert::metersPerSecondToFeetPerSecond(speed); } // Attitude if ((d->aspects & Aspect::Attitude)) { - if (startPositionData.timestamp > firstMovementTimestamp) { - const double deltaAltitude = Convert::feetToMeters(endPositionData.altitude - startPositionData.altitude); + if (currentPositionData.timestamp > firstMovementTimestamp) { + const auto deltaAltitude = Convert::feetToMeters(nextPositionData.altitude - currentPositionData.altitude); // SimConnect: positive pitch values "point downwards", negative pitch values "upwards" // -> switch the sign if (d->aspects.testFlag(Aspect::Pitch)) { - startPositionData.pitch = -SkyMath::approximatePitch(distance, deltaAltitude); + currentAttitudeData.pitch = -SkyMath::approximatePitch(distance, deltaAltitude); } - const double initialBearing = SkyMath::initialBearing(startPosition, endPosition); + const auto initialBearing = SkyMath::initialBearing(currentPosition, nextPosition); if (d->aspects.testFlag(Aspect::Heading)) { - startPositionData.trueHeading = initialBearing; + currentAttitudeData.trueHeading = initialBearing; } if (d->aspects.testFlag(Aspect::Bank)) { if (i > 0) { // [-180, 180] - const double headingChange = SkyMath::headingChange(position[i - 1].trueHeading, startPositionData.trueHeading); + const auto headingChange = SkyMath::headingChange(attitude[i - 1].trueHeading, currentAttitudeData.trueHeading); // We go into maximum bank angle of 30 degrees with a heading change of 45 degrees // SimConnect: negative values are a "right" turn, positive values a left turn - startPositionData.bank = SkyMath::bankAngle(headingChange, 45.0, ::MaxBankAngle); + currentAttitudeData.bank = SkyMath::bankAngle(headingChange, 45.0, ::MaxBankAngle); } else { // First point, zero bank angle - startPositionData.bank = 0.0; + currentAttitudeData.bank = 0.0; } } } else { if (d->aspects.testFlag(Aspect::Pitch)) { - startPositionData.pitch = 0.0; + currentAttitudeData.pitch = 0.0; } if (d->aspects.testFlag(Aspect::Heading)) { - startPositionData.trueHeading = firstMovementHeading; + currentAttitudeData.trueHeading = firstMovementHeading; } if (d->aspects.testFlag(Aspect::Bank)) { - startPositionData.bank = 0.0; + currentAttitudeData.bank = 0.0; } } } - } else if (positionCount > 1) { + } else if (attitudeCount > 1) { // Last point - PositionData &lastPositionData = position[i]; + AttitudeData &lastAttitudeData = attitude[i]; // Velocity - PositionData &previousPositionData = position[i -1]; + AttitudeData &previousAttitudeData = attitude[i -1]; if (d->aspects.testFlag(Aspect::Velocity)) { - lastPositionData.velocityBodyX = previousPositionData.velocityBodyX; - lastPositionData.velocityBodyY = previousPositionData.velocityBodyY; - lastPositionData.velocityBodyZ = Convert::knotsToFeetPerSecond(::LandingVelocity); + lastAttitudeData.velocityBodyX = previousAttitudeData.velocityBodyX; + lastAttitudeData.velocityBodyY = previousAttitudeData.velocityBodyY; + lastAttitudeData.velocityBodyZ = Convert::knotsToFeetPerSecond(::LandingVelocity); } // Attitude if ((d->aspects & Aspect::Attitude)) { if (d->aspects.testFlag(Aspect::Pitch)) { - lastPositionData.pitch = ::LandingPitch; + lastAttitudeData.pitch = ::LandingPitch; } if (d->aspects.testFlag(Aspect::Bank)) { - lastPositionData.bank = 0.0; + lastAttitudeData.bank = 0.0; } if (d->aspects.testFlag(Aspect::Heading)) { - lastPositionData.trueHeading = previousPositionData.trueHeading; + lastAttitudeData.trueHeading = previousAttitudeData.trueHeading; } } } else { // Only one sampled data point ("academic case") - PositionData &lastPositionData = position[i]; + AttitudeData &lastAttitudeData = attitude[i]; // Velocity if (d->aspects.testFlag(Aspect::Velocity)) { - lastPositionData.velocityBodyX = 0.0; - lastPositionData.velocityBodyY = 0.0; - lastPositionData.velocityBodyZ = 0.0; + lastAttitudeData.velocityBodyX = 0.0; + lastAttitudeData.velocityBodyY = 0.0; + lastAttitudeData.velocityBodyZ = 0.0; } // Attitude if ((d->aspects & Aspect::Attitude)) { if (d->aspects.testFlag(Aspect::Pitch)) { - lastPositionData.pitch = 0.0; + lastAttitudeData.pitch = 0.0; } if (d->aspects.testFlag(Aspect::Bank)) { - lastPositionData.bank = 0.0; + lastAttitudeData.bank = 0.0; } if (d->aspects.testFlag(Aspect::Heading)) { - lastPositionData.trueHeading = 0.0; + lastAttitudeData.trueHeading = 0.0; } } } - } + } // For all positions } void FlightAugmentation::augmentProcedures(Aircraft &aircraft) noexcept @@ -249,7 +268,7 @@ void FlightAugmentation::augmentStartProcedure(Aircraft &aircraft) noexcept // Engine - Engine &engine = aircraft.getEngine(); + auto &engine = aircraft.getEngine(); EngineData engineData; // 0 seconds @@ -335,7 +354,7 @@ void FlightAugmentation::augmentStartProcedure(Aircraft &aircraft) noexcept // Secondary flight controls - SecondaryFlightControl &secondaryFlightControl = aircraft.getSecondaryFlightControl(); + auto &secondaryFlightControl = aircraft.getSecondaryFlightControl(); SecondaryFlightControlData secondaryFlightControlData; // 0 seconds @@ -366,7 +385,7 @@ void FlightAugmentation::augmentStartProcedure(Aircraft &aircraft) noexcept // Handles & gear - AircraftHandle &aircraftHandle = aircraft.getAircraftHandle(); + auto &aircraftHandle = aircraft.getAircraftHandle(); AircraftHandleData handleData; // 0 seconds @@ -383,7 +402,7 @@ void FlightAugmentation::augmentStartProcedure(Aircraft &aircraft) noexcept // Lights - Light &light = aircraft.getLight(); + auto &light = aircraft.getLight(); LightData lightData; // 0 seconds @@ -431,7 +450,7 @@ void FlightAugmentation::augmentLandingProcedure(Aircraft &aircraft) noexcept // Engine if (d->aspects.testFlag(Aspect::Engine)) { - Engine &engine = aircraft.getEngine(); + auto &engine = aircraft.getEngine(); EngineData engineData; // t minus 5 minutes @@ -514,7 +533,7 @@ void FlightAugmentation::augmentLandingProcedure(Aircraft &aircraft) noexcept // Secondary flight controls - SecondaryFlightControl &secondaryFlightControl = aircraft.getSecondaryFlightControl(); + auto &secondaryFlightControl = aircraft.getSecondaryFlightControl(); SecondaryFlightControlData secondaryFlightControlData; // t minus 10 minutes @@ -597,7 +616,7 @@ void FlightAugmentation::augmentLandingProcedure(Aircraft &aircraft) noexcept // Handles & gear - AircraftHandle &aircraftHandle = aircraft.getAircraftHandle(); + auto &aircraftHandle = aircraft.getAircraftHandle(); AircraftHandleData handleData; // t minus 3 minutes @@ -609,7 +628,7 @@ void FlightAugmentation::augmentLandingProcedure(Aircraft &aircraft) noexcept // Lights if (d->aspects.testFlag(Aspect::Light)) { - Light &light = aircraft.getLight(); + auto &light = aircraft.getLight(); LightData lightData; // t minus 8 minutes @@ -652,18 +671,19 @@ void FlightAugmentation::augmentLandingProcedure(Aircraft &aircraft) noexcept // Adjust approach pitch for the last 3 minutes // https://forum.aerosoft.com/index.php?/topic/123864-a320-pitch-angle-during-landing/ if (d->aspects.testFlag(Aspect::Pitch)) { - auto index = position.count() - 1; + Attitude &attitude = aircraft.getAttitude(); + auto index = attitude.count() - 1; if (index >= 0) { // Last sample: flare with nose up 6 degrees - PositionData &positionData = position[index]; - positionData.pitch = -6.0; + AttitudeData &attitudeData = attitude[index]; + attitudeData.pitch = -6.0; if (index > 0) { // Second to last sample -> adjust pitch to 3 degrees nose up --index; - while (index >= 0 && position[index].timestamp >= std::max(lastTimestamp - std::int64_t(3 * 60 * 1000), std::int64_t(0))) { + while (index >= 0 && attitude[index].timestamp >= std::max(lastTimestamp - std::int64_t(3 * 60 * 1000), std::int64_t(0))) { // Nose up 3 degrees - position[index].pitch = -3.0; + attitude[index].pitch = -3.0; --index; } } diff --git a/src/Model/CMakeLists.txt b/src/Model/CMakeLists.txt index c09536b19..ec2967701 100644 --- a/src/Model/CMakeLists.txt +++ b/src/Model/CMakeLists.txt @@ -22,6 +22,8 @@ target_sources(${LIBRARY_NAME} include/Model/TimeVariableData.h include/Model/Position.h src/Position.cpp include/Model/PositionData.h src/PositionData.cpp + include/Model/Attitude.h src/Attitude.cpp + include/Model/AttitudeData.h src/AttitudeData.cpp include/Model/InitialPosition.h src/InitialPosition.cpp include/Model/Engine.h src/Engine.cpp include/Model/EngineData.h src/EngineData.cpp @@ -52,7 +54,6 @@ target_include_directories(${LIBRARY_NAME} target_link_libraries(${LIBRARY_NAME} PUBLIC Qt6::Core - Qt6::Sql PRIVATE Sky::Kernel ) diff --git a/src/Model/include/Model/AbstractComponent.h b/src/Model/include/Model/AbstractComponent.h index 66750dc4f..9242f3f6e 100644 --- a/src/Model/include/Model/AbstractComponent.h +++ b/src/Model/include/Model/AbstractComponent.h @@ -142,6 +142,11 @@ class MODEL_API AbstractComponent m_data.reserve(size); } + void insert(typename Data::size_type count, const T &value) + { + m_data.insert(m_data.end(), count, value); + } + typename Data::size_type capacity() const noexcept { return m_data.capacity(); diff --git a/src/Model/include/Model/Aircraft.h b/src/Model/include/Model/Aircraft.h index e5301c78e..6a562d85c 100644 --- a/src/Model/include/Model/Aircraft.h +++ b/src/Model/include/Model/Aircraft.h @@ -35,6 +35,7 @@ class QDateTime; #include "Engine.h" class Position; +class Attitude; class PrimaryFlightControl; class SecondaryFlightControl; class AircraftHandle; @@ -56,6 +57,7 @@ class MODEL_API Aircraft final void setId(std::int64_t id) noexcept; Position &getPosition() const noexcept; + Attitude &getAttitude() const noexcept; Engine &getEngine() const noexcept; PrimaryFlightControl &getPrimaryFlightControl() const noexcept; SecondaryFlightControl &getSecondaryFlightControl() const noexcept; diff --git a/src/Model/include/Model/Attitude.h b/src/Model/include/Model/Attitude.h new file mode 100644 index 000000000..9b40770f1 --- /dev/null +++ b/src/Model/include/Model/Attitude.h @@ -0,0 +1,45 @@ +/** + * Sky Dolly - The Black Sheep for Your Flight Recordings + * + * Copyright (c) Oliver Knoll + * All rights reserved. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this + * software and associated documentation files (the "Software"), to deal in the Software + * without restriction, including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE + * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef ATTITUDE_H +#define ATTITUDE_H + +#include "AttitudeData.h" +#include "AircraftInfo.h" +#include "AbstractComponent.h" +#include "ModelLib.h" + +class MODEL_API Attitude final : public AbstractComponent +{ +public: + explicit Attitude(const AircraftInfo &aircraftInfo) noexcept; + + const AttitudeData &interpolate(std::int64_t timestamp, TimeVariableData::Access access) const noexcept override; + +private: + mutable AttitudeData m_currentData; +}; + +#endif // ATTITUDE_H + diff --git a/src/Model/include/Model/AttitudeData.h b/src/Model/include/Model/AttitudeData.h new file mode 100644 index 000000000..c74d0a3c1 --- /dev/null +++ b/src/Model/include/Model/AttitudeData.h @@ -0,0 +1,48 @@ +/** + * Sky Dolly - The Black Sheep for Your Flight Recordings + * + * Copyright (c) Oliver Knoll + * All rights reserved. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this + * software and associated documentation files (the "Software"), to deal in the Software + * without restriction, including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE + * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef ATTITUDEDATA_H +#define ATTITUDEDATA_H + +#include "TimeVariableData.h" +#include "ModelLib.h" + +struct MODEL_API AttitudeData final : public TimeVariableData +{ + // Attitude + double pitch {0.0}; + double bank {0.0}; + double trueHeading {0.0}; + + // Velocity + double velocityBodyX {0.0}; + double velocityBodyY {0.0}; + double velocityBodyZ {0.0}; + + bool onGround {false}; + + explicit AttitudeData(double pitch = 0.0, double bank = 0.0, double trueHeading = 0.0) noexcept; +}; + +#endif // ATTITUDEDATA_H diff --git a/src/Model/include/Model/InitialPosition.h b/src/Model/include/Model/InitialPosition.h index 790a5ab8f..2f8ee2994 100644 --- a/src/Model/include/Model/InitialPosition.h +++ b/src/Model/include/Model/InitialPosition.h @@ -26,13 +26,14 @@ #define INITIALPOSITION_H #include -#include #include #include #include #include "PositionData.h" +#include "AttitudeData.h" +#include "AttitudeData.h" #include "ModelLib.h" struct AircraftInfo; @@ -50,22 +51,22 @@ struct MODEL_API InitialPosition final bool onGround {false}; explicit InitialPosition(double latitude = 0.0, double longitude = 0.0, double altitude = 0.0) noexcept; - InitialPosition(const PositionData &positionData, const AircraftInfo &aircraftInfo) noexcept; + InitialPosition(const PositionData &positionData, const AttitudeData &attitudeData, const AircraftInfo &aircraftInfo) noexcept; inline bool isNull() const noexcept { return (indicatedAirspeed == InvalidIndicatedAirspeed); } - inline void fromPositionData(const PositionData &positionData) { + inline void fromPositionData(const PositionData &positionData, const AttitudeData &attitudeData) { latitude = positionData.latitude; longitude = positionData.longitude; altitude = positionData.altitude; - pitch = positionData.pitch; - bank = positionData.bank; - trueHeading = positionData.trueHeading; - const double trueAirspeed = Convert::feetPerSecondToKnots(positionData.velocityBodyZ); + pitch = attitudeData.pitch; + bank = attitudeData.bank; + trueHeading = attitudeData.trueHeading; + const auto trueAirspeed = Convert::feetPerSecondToKnots(attitudeData.velocityBodyZ); indicatedAirspeed = static_cast(std::round(Convert::trueToIndicatedAirspeed(trueAirspeed, positionData.altitude))); - onGround = false; + onGround = attitudeData.onGround; } static constexpr int InvalidIndicatedAirspeed = std::numeric_limits::min(); diff --git a/src/Model/include/Model/PositionData.h b/src/Model/include/Model/PositionData.h index 3f93b76da..56203fa0e 100644 --- a/src/Model/include/Model/PositionData.h +++ b/src/Model/include/Model/PositionData.h @@ -37,14 +37,6 @@ struct MODEL_API PositionData final : public TimeVariableData double altitude {0.0}; // Indicated pressure altitude (analytical purposes only) double indicatedAltitude {0.0}; - double pitch {0.0}; - double bank {0.0}; - double trueHeading {0.0}; - - // Velocity - double velocityBodyX {0.0}; - double velocityBodyY {0.0}; - double velocityBodyZ {0.0}; explicit PositionData(double latitude = 0.0, double longitude = 0.0, double altitude = 0.0) noexcept; }; diff --git a/src/Model/include/Model/TimeVariableData.h b/src/Model/include/Model/TimeVariableData.h index ff3e32bdd..e9de2190c 100644 --- a/src/Model/include/Model/TimeVariableData.h +++ b/src/Model/include/Model/TimeVariableData.h @@ -66,10 +66,10 @@ struct MODEL_API TimeVariableData */ ContinuousSeek, /*! - * The sampled data is accessed for export (in a linear way), but always starting from the first + * The sampled data is accessed for import or export (in a linear way), but always starting from the first * sample point (not taking the time offset of the Aircraft into account). */ - Export + NoTimeOffset }; static inline bool isSeek(Access access) noexcept diff --git a/src/Model/src/Aircraft.cpp b/src/Model/src/Aircraft.cpp index 183303540..84600a3d9 100644 --- a/src/Model/src/Aircraft.cpp +++ b/src/Model/src/Aircraft.cpp @@ -25,13 +25,14 @@ #include #include #include -#include #include #include "TimeVariableData.h" #include "AircraftInfo.h" #include "Position.h" #include "PositionData.h" +#include "Attitude.h" +#include "AttitudeData.h" #include "Engine.h" #include "EngineData.h" #include "PrimaryFlightControl.h" @@ -53,12 +54,13 @@ struct AircraftPrivate std::int64_t id {Const::InvalidId}; AircraftInfo aircraftInfo {id}; - Position position{aircraftInfo}; - Engine engine{aircraftInfo}; - PrimaryFlightControl primaryFlightControl{aircraftInfo}; - SecondaryFlightControl secondaryFlightControl{aircraftInfo}; - AircraftHandle aircraftHandle{aircraftInfo}; - Light light{aircraftInfo}; + Position position {aircraftInfo}; + Attitude attitude {aircraftInfo}; + Engine engine {aircraftInfo}; + PrimaryFlightControl primaryFlightControl {aircraftInfo}; + SecondaryFlightControl secondaryFlightControl {aircraftInfo}; + AircraftHandle aircraftHandle {aircraftInfo}; + Light light {aircraftInfo}; FlightPlan flightPlan; mutable std::int64_t duration {TimeVariableData::InvalidTime}; @@ -90,6 +92,11 @@ Position &Aircraft::getPosition() const noexcept return d->position; } +Attitude &Aircraft::getAttitude() const noexcept +{ + return d->attitude; +} + Engine &Aircraft::getEngine() const noexcept { return d->engine; @@ -151,7 +158,7 @@ void Aircraft::addTimeOffset(std::int64_t deltaOffset) noexcept { std::int64_t Aircraft::getDurationMSec() const noexcept { - const std::int64_t timeOffset = d->aircraftInfo.timeOffset; + const auto timeOffset = d->aircraftInfo.timeOffset; if (d->duration == TimeVariableData::InvalidTime) { d->duration = 0; // The timestamp offset indicates the time difference the given aircraft @@ -160,6 +167,9 @@ std::int64_t Aircraft::getDurationMSec() const noexcept if (d->position.count() > 0) { d->duration = std::max(d->position.getLast().timestamp - timeOffset, std::int64_t(0)); } + if (d->attitude.count() > 0) { + d->duration = std::max(d->attitude.getLast().timestamp - timeOffset, d->duration); + } if (d->engine.count() > 0) { d->duration = std::max(d->engine.getLast().timestamp - timeOffset, d->duration); } @@ -188,6 +198,7 @@ void Aircraft::clear() noexcept { d->aircraftInfo.clear(); d->position.clear(); + d->attitude.clear(); d->engine.clear(); d->primaryFlightControl.clear(); d->secondaryFlightControl.clear(); diff --git a/src/Model/src/AircraftHandle.cpp b/src/Model/src/AircraftHandle.cpp index ba2d144fb..13ec90d44 100644 --- a/src/Model/src/AircraftHandle.cpp +++ b/src/Model/src/AircraftHandle.cpp @@ -42,8 +42,8 @@ AircraftHandle::AircraftHandle(const AircraftInfo &aircraftInfo) noexcept const AircraftHandleData &AircraftHandle::interpolate(std::int64_t timestamp, TimeVariableData::Access access) const noexcept { const AircraftHandleData *p1 {nullptr}, *p2 {nullptr}; - const std::int64_t timeOffset = access != TimeVariableData::Access::Export ? getAircraftInfo().timeOffset : 0; - const std::int64_t adjustedTimestamp = std::max(timestamp + timeOffset, std::int64_t(0)); + const auto timeOffset = access != TimeVariableData::Access::NoTimeOffset ? getAircraftInfo().timeOffset : 0; + const auto adjustedTimestamp = std::max(timestamp + timeOffset, std::int64_t(0)); if (getCurrentTimestamp() != adjustedTimestamp || getCurrentAccess() != access) { @@ -52,7 +52,7 @@ const AircraftHandleData &AircraftHandle::interpolate(std::int64_t timestamp, Ti switch (access) { case TimeVariableData::Access::Linear: [[fallthrough]]; - case TimeVariableData::Access::Export: + case TimeVariableData::Access::NoTimeOffset: if (SkySearch::getLinearInterpolationSupportData(getData(), adjustedTimestamp, SkySearch::DefaultInterpolationWindow, currentIndex, &p1, &p2)) { tn = SkySearch::normaliseTimestamp(*p1, *p2, adjustedTimestamp); } diff --git a/src/Model/src/Attitude.cpp b/src/Model/src/Attitude.cpp new file mode 100644 index 000000000..89a6dc8ae --- /dev/null +++ b/src/Model/src/Attitude.cpp @@ -0,0 +1,92 @@ +/** + * Sky Dolly - The black sheep for your fposition recordings + * + * Copyright (c) Oliver Knoll + * All rights reserved. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this + * software and associated documentation files (the "Software"), to deal in the Software + * without restriction, including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE + * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include +#include + +#ifdef DEBUG +#include +#endif + +#include +#include "TimeVariableData.h" +#include "SkySearch.h" +#include "AircraftInfo.h" +#include "AttitudeData.h" +#include "Attitude.h" + +namespace +{ +constexpr double Tension = 0.0; +} + +// PUBLIC + +Attitude::Attitude(const AircraftInfo &aircraftInfo) noexcept + : AbstractComponent(aircraftInfo) +{} + +const AttitudeData &Attitude::interpolate(std::int64_t timestamp, TimeVariableData::Access access) const noexcept +{ + const AttitudeData *p0 {nullptr}, *p1 {nullptr}, *p2 {nullptr}, *p3 {nullptr}; + const auto timeOffset = access != TimeVariableData::Access::NoTimeOffset ? getAircraftInfo().timeOffset : 0; + const auto adjustedTimestamp = std::max(timestamp + timeOffset, std::int64_t(0)); + + if (getCurrentTimestamp() != adjustedTimestamp || getCurrentAccess() != access) { + int currentIndex = getCurrentIndex(); + double tn {0.0}; + // Attitude data is always interpolated within an "infinite" interpolation window, in order to + // take imported "sparse flight plans" into account + if (SkySearch::getCubicInterpolationSupportData(getData(), adjustedTimestamp, SkySearch::InfinitetInterpolationWindow, currentIndex, &p0, &p1, &p2, &p3)) { + tn = SkySearch::normaliseTimestamp(*p1, *p2, adjustedTimestamp); + } + if (p1 != nullptr) { + // Aircraft attitude + + // Pitch: [-90, 90] - no discontinuity at +/- 90 + m_currentData.pitch = SkyMath::interpolateHermite(p0->pitch, p1->pitch, p2->pitch, p3->pitch, tn, ::Tension); + // Bank: [-180, 180] - discontinuity at +/- 180 + m_currentData.bank = SkyMath::interpolateHermite180(p0->bank, p1->bank, p2->bank, p3->bank, tn, ::Tension); + // Heading: [0, 360] - discontinuity at 0/360 + m_currentData.trueHeading = SkyMath::interpolateHermite360(p0->trueHeading, p1->trueHeading, p2->trueHeading, p3->trueHeading, tn, ::Tension); + + // Velocity + m_currentData.velocityBodyX = SkyMath::interpolateLinear(p1->velocityBodyX, p2->velocityBodyX, tn); + m_currentData.velocityBodyY = SkyMath::interpolateLinear(p1->velocityBodyY, p2->velocityBodyY, tn); + m_currentData.velocityBodyZ = SkyMath::interpolateLinear(p1->velocityBodyZ, p2->velocityBodyZ, tn); + + m_currentData.timestamp = adjustedTimestamp; + } else { + // No recorded data, or the timestamp exceeds the timestamp of the last recorded data + m_currentData.reset(); + } + + setCurrentIndex(currentIndex); + setCurrentTimestamp(adjustedTimestamp); + setCurrentAccess(access); + } + return m_currentData; +} + +template class AbstractComponent; diff --git a/src/Model/src/AttitudeData.cpp b/src/Model/src/AttitudeData.cpp new file mode 100644 index 000000000..0db1d770e --- /dev/null +++ b/src/Model/src/AttitudeData.cpp @@ -0,0 +1,34 @@ +/** + * Sky Dolly - The Black Sheep for Your Flight Recordings + * + * Copyright (c) Oliver Knoll + * All rights reserved. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this + * software and associated documentation files (the "Software"), to deal in the Software + * without restriction, including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE + * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include "AttitudeData.h" + +// PUBLIC + +AttitudeData::AttitudeData(double pitch, double bank, double trueHeading) noexcept + : TimeVariableData(), + pitch(pitch), + bank(bank), + trueHeading(trueHeading) +{} diff --git a/src/Model/src/Engine.cpp b/src/Model/src/Engine.cpp index 609ab8f5e..1a6ffb88c 100644 --- a/src/Model/src/Engine.cpp +++ b/src/Model/src/Engine.cpp @@ -41,8 +41,8 @@ Engine::Engine(const AircraftInfo &aircraftInfo) noexcept const EngineData &Engine::interpolate(std::int64_t timestamp, TimeVariableData::Access access) const noexcept { const EngineData *p1 {nullptr}, *p2 {nullptr}; - const std::int64_t timeOffset = access != TimeVariableData::Access::Export ? getAircraftInfo().timeOffset : 0; - const std::int64_t adjustedTimestamp = std::max(timestamp + timeOffset, std::int64_t(0)); + const auto timeOffset = access != TimeVariableData::Access::NoTimeOffset ? getAircraftInfo().timeOffset : 0; + const auto adjustedTimestamp = std::max(timestamp + timeOffset, std::int64_t(0)); if (getCurrentTimestamp() != adjustedTimestamp || getCurrentAccess() != access) { int currentIndex = getCurrentIndex(); @@ -50,7 +50,7 @@ const EngineData &Engine::interpolate(std::int64_t timestamp, TimeVariableData:: switch (access) { case TimeVariableData::Access::Linear: [[fallthrough]]; - case TimeVariableData::Access::Export: + case TimeVariableData::Access::NoTimeOffset: if (SkySearch::getLinearInterpolationSupportData(getData(), adjustedTimestamp, SkySearch::DefaultInterpolationWindow, currentIndex, &p1, &p2)) { tn = SkySearch::normaliseTimestamp(*p1, *p2, adjustedTimestamp); } diff --git a/src/Model/src/EngineData.cpp b/src/Model/src/EngineData.cpp index 98a5ba2a1..c4ece0609 100644 --- a/src/Model/src/EngineData.cpp +++ b/src/Model/src/EngineData.cpp @@ -29,10 +29,10 @@ // PUBLIC -EngineData::EngineData(std::int16_t theThrottleLeverPosition1, std::int16_t thePropellerLeverPosition1, std::uint8_t theMixtureLeverPosition1, std::uint8_t theCowlFlapPosition1) noexcept +EngineData::EngineData(std::int16_t throttleLeverPosition1, std::int16_t propellerLeverPosition1, std::uint8_t mixtureLeverPosition1, std::uint8_t cowlFlapPosition1) noexcept : TimeVariableData(), - throttleLeverPosition1(theThrottleLeverPosition1), - propellerLeverPosition1(thePropellerLeverPosition1), - mixtureLeverPosition1(theMixtureLeverPosition1), - cowlFlapPosition1(theCowlFlapPosition1) + throttleLeverPosition1(throttleLeverPosition1), + propellerLeverPosition1(propellerLeverPosition1), + mixtureLeverPosition1(mixtureLeverPosition1), + cowlFlapPosition1(cowlFlapPosition1) {} diff --git a/src/Model/src/Flight.cpp b/src/Model/src/Flight.cpp index 54d8bd842..510106b65 100644 --- a/src/Model/src/Flight.cpp +++ b/src/Model/src/Flight.cpp @@ -163,7 +163,7 @@ void Flight::addAircraft(std::vector &&aircraft) noexcept Aircraft &Flight::addUserAircraft(std::int64_t aircraftId) noexcept { const int previousUserAircraftIndex = d->flightData.userAircraftIndex; - Aircraft &aircraft = d->flightData.addUserAircraft(aircraftId); + auto &aircraft = d->flightData.addUserAircraft(aircraftId); // First emit the aircraft added signal and... emit aircraftAdded(d->flightData.aircraft.back()); // ... then the user aircraft changed signal @@ -276,7 +276,7 @@ void Flight::setFlightCondition(FlightCondition flightCondition) noexcept FlightSummary Flight::getFlightSummary() const noexcept { - const Aircraft &aircraft = getUserAircraft(); + const auto &aircraft = getUserAircraft(); const AircraftInfo &aircraftInfo = aircraft.getAircraftInfo(); FlightSummary summary; diff --git a/src/Model/src/InitialPosition.cpp b/src/Model/src/InitialPosition.cpp index 42fe7889d..e3a402b64 100644 --- a/src/Model/src/InitialPosition.cpp +++ b/src/Model/src/InitialPosition.cpp @@ -23,6 +23,7 @@ * DEALINGS IN THE SOFTWARE. */ #include "PositionData.h" +#include "AttitudeData.h" #include "AircraftInfo.h" #include "InitialPosition.h" @@ -34,8 +35,8 @@ InitialPosition::InitialPosition(double theLatitude, double theLongitude, double altitude(theAltitude) {} -InitialPosition::InitialPosition(const PositionData &positionData, const AircraftInfo &aircraftInfo) noexcept +InitialPosition::InitialPosition(const PositionData &positionData, const AttitudeData &attitudeData, const AircraftInfo &aircraftInfo) noexcept : onGround(aircraftInfo.startOnGround) { - fromPositionData(positionData); + fromPositionData(positionData, attitudeData); } diff --git a/src/Model/src/Light.cpp b/src/Model/src/Light.cpp index 432e0cf45..1667b0096 100644 --- a/src/Model/src/Light.cpp +++ b/src/Model/src/Light.cpp @@ -42,15 +42,15 @@ Light::Light(const AircraftInfo &aircraftInfo) noexcept const LightData &Light::interpolate(std::int64_t timestamp, TimeVariableData::Access access) const noexcept { const LightData *p1 {nullptr}, *p2 {nullptr}; - const std::int64_t timeOffset = access != TimeVariableData::Access::Export ? getAircraftInfo().timeOffset : 0; - const std::int64_t adjustedTimestamp = std::max(timestamp + timeOffset, std::int64_t(0)); + const auto timeOffset = access != TimeVariableData::Access::NoTimeOffset ? getAircraftInfo().timeOffset : 0; + const auto adjustedTimestamp = std::max(timestamp + timeOffset, std::int64_t(0)); if (getCurrentTimestamp() != adjustedTimestamp || getCurrentAccess() != access) { int currentIndex = getCurrentIndex(); switch (access) { case TimeVariableData::Access::Linear: [[fallthrough]]; - case TimeVariableData::Access::Export: + case TimeVariableData::Access::NoTimeOffset: SkySearch::getLinearInterpolationSupportData(getData(), adjustedTimestamp, SkySearch::DefaultInterpolationWindow, currentIndex, &p1, &p2); break; case TimeVariableData::Access::DiscreteSeek: diff --git a/src/Model/src/Location.cpp b/src/Model/src/Location.cpp index c36eee750..3d7582ee2 100644 --- a/src/Model/src/Location.cpp +++ b/src/Model/src/Location.cpp @@ -27,11 +27,11 @@ // PUBLIC -Location::Location(double theLatitude, double theLongitude, double theAltitude) noexcept +Location::Location(double latitude, double longitude, double altitude) noexcept : Data(), - latitude(theLatitude), - longitude(theLongitude), - altitude(theAltitude) + latitude(latitude), + longitude(longitude), + altitude(altitude) {} Location::Location(const InitialPosition &initialPosition) noexcept diff --git a/src/Model/src/Position.cpp b/src/Model/src/Position.cpp index 2f06b003e..8014fcf83 100644 --- a/src/Model/src/Position.cpp +++ b/src/Model/src/Position.cpp @@ -50,8 +50,8 @@ Position::Position(const AircraftInfo &aircraftInfo) noexcept const PositionData &Position::interpolate(std::int64_t timestamp, TimeVariableData::Access access) const noexcept { const PositionData *p0 {nullptr}, *p1 {nullptr}, *p2 {nullptr}, *p3 {nullptr}; - const std::int64_t timeOffset = access != TimeVariableData::Access::Export ? getAircraftInfo().timeOffset : 0; - const std::int64_t adjustedTimestamp = std::max(timestamp + timeOffset, std::int64_t(0)); + const auto timeOffset = access != TimeVariableData::Access::NoTimeOffset ? getAircraftInfo().timeOffset : 0; + const auto adjustedTimestamp = std::max(timestamp + timeOffset, std::int64_t(0)); if (getCurrentTimestamp() != adjustedTimestamp || getCurrentAccess() != access) { int currentIndex = getCurrentIndex(); @@ -62,7 +62,7 @@ const PositionData &Position::interpolate(std::int64_t timestamp, TimeVariableDa tn = SkySearch::normaliseTimestamp(*p1, *p2, adjustedTimestamp); } if (p1 != nullptr) { - // Aircraft position & attitude + // Aircraft position // Latitude: [-90, 90] - no discontinuity at +/- 90 m_currentData.latitude = SkyMath::interpolateHermite(p0->latitude, p1->latitude, p2->latitude, p3->latitude, tn); @@ -73,17 +73,6 @@ const PositionData &Position::interpolate(std::int64_t timestamp, TimeVariableDa // The indicated altitude is not used for replay - only for display and analytical purposes, // so linear interpolation is sufficient m_currentData.indicatedAltitude = SkyMath::interpolateLinear(p1->indicatedAltitude, p2->indicatedAltitude, tn); - // Pitch: [-90, 90] - no discontinuity at +/- 90 - m_currentData.pitch = SkyMath::interpolateHermite(p0->pitch, p1->pitch, p2->pitch, p3->pitch, tn, ::Tension); - // Bank: [-180, 180] - discontinuity at +/- 180 - m_currentData.bank = SkyMath::interpolateHermite180(p0->bank, p1->bank, p2->bank, p3->bank, tn, ::Tension); - // Heading: [0, 360] - discontinuity at 0/360 - m_currentData.trueHeading = SkyMath::interpolateHermite360(p0->trueHeading, p1->trueHeading, p2->trueHeading, p3->trueHeading, tn, ::Tension); - - // Velocity - m_currentData.velocityBodyX = SkyMath::interpolateLinear(p1->velocityBodyX, p2->velocityBodyX, tn); - m_currentData.velocityBodyY = SkyMath::interpolateLinear(p1->velocityBodyY, p2->velocityBodyY, tn); - m_currentData.velocityBodyZ = SkyMath::interpolateLinear(p1->velocityBodyZ, p2->velocityBodyZ, tn); m_currentData.timestamp = adjustedTimestamp; } else { diff --git a/src/Model/src/PositionData.cpp b/src/Model/src/PositionData.cpp index 6b7107831..ee4ec1164 100644 --- a/src/Model/src/PositionData.cpp +++ b/src/Model/src/PositionData.cpp @@ -22,15 +22,14 @@ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ -#include "SimType.h" #include "PositionData.h" // PUBLIC -PositionData::PositionData(double theLatitude, double theLongitude, double theAltitdue) noexcept +PositionData::PositionData(double latitude, double longitude, double altitude) noexcept : TimeVariableData(), - latitude(theLatitude), - longitude(theLongitude), - altitude(theAltitdue), - indicatedAltitude(theAltitdue) + latitude(latitude), + longitude(longitude), + altitude(altitude), + indicatedAltitude(altitude) {} diff --git a/src/Model/src/PrimaryFlightControl.cpp b/src/Model/src/PrimaryFlightControl.cpp index cee248df3..44812d631 100644 --- a/src/Model/src/PrimaryFlightControl.cpp +++ b/src/Model/src/PrimaryFlightControl.cpp @@ -42,8 +42,8 @@ PrimaryFlightControl::PrimaryFlightControl(const AircraftInfo &aircraftInfo) noe const PrimaryFlightControlData &PrimaryFlightControl::interpolate(std::int64_t timestamp, TimeVariableData::Access access) const noexcept { const PrimaryFlightControlData *p1 {nullptr}, *p2 {nullptr}; - const std::int64_t timeOffset = access != TimeVariableData::Access::Export ? getAircraftInfo().timeOffset : 0; - const std::int64_t adjustedTimestamp = std::max(timestamp + timeOffset, std::int64_t(0)); + const auto timeOffset = access != TimeVariableData::Access::NoTimeOffset ? getAircraftInfo().timeOffset : 0; + const auto adjustedTimestamp = std::max(timestamp + timeOffset, std::int64_t(0)); if (getCurrentTimestamp() != adjustedTimestamp || getCurrentAccess() != access) { int currentIndex = getCurrentIndex(); @@ -51,7 +51,7 @@ const PrimaryFlightControlData &PrimaryFlightControl::interpolate(std::int64_t t switch (access) { case TimeVariableData::Access::Linear: [[fallthrough]]; - case TimeVariableData::Access::Export: + case TimeVariableData::Access::NoTimeOffset: if (SkySearch::getLinearInterpolationSupportData(getData(), adjustedTimestamp, SkySearch::DefaultInterpolationWindow, currentIndex, &p1, &p2)) { tn = SkySearch::normaliseTimestamp(*p1, *p2, adjustedTimestamp); } diff --git a/src/Model/src/SecondaryFlightControl.cpp b/src/Model/src/SecondaryFlightControl.cpp index 25687ff86..4d17c9ee7 100644 --- a/src/Model/src/SecondaryFlightControl.cpp +++ b/src/Model/src/SecondaryFlightControl.cpp @@ -42,8 +42,8 @@ SecondaryFlightControl::SecondaryFlightControl(const AircraftInfo &aircraftInfo) const SecondaryFlightControlData &SecondaryFlightControl::interpolate(std::int64_t timestamp, TimeVariableData::Access access) const noexcept { const SecondaryFlightControlData *p1 {nullptr}, *p2 {nullptr}; - const std::int64_t timeOffset = access != TimeVariableData::Access::Export ? getAircraftInfo().timeOffset : 0; - const std::int64_t adjustedTimestamp = std::max(timestamp + timeOffset, std::int64_t(0)); + const auto timeOffset = access != TimeVariableData::Access::NoTimeOffset ? getAircraftInfo().timeOffset : 0; + const auto adjustedTimestamp = std::max(timestamp + timeOffset, std::int64_t(0)); if (getCurrentTimestamp() != adjustedTimestamp || getCurrentAccess() != access) { int currentIndex = getCurrentIndex(); @@ -51,7 +51,7 @@ const SecondaryFlightControlData &SecondaryFlightControl::interpolate(std::int64 switch (access) { case TimeVariableData::Access::Linear: [[fallthrough]]; - case TimeVariableData::Access::Export: + case TimeVariableData::Access::NoTimeOffset: if (SkySearch::getLinearInterpolationSupportData(getData(), adjustedTimestamp, SkySearch::DefaultInterpolationWindow, currentIndex, &p1, &p2)) { tn = SkySearch::normaliseTimestamp(*p1, *p2, adjustedTimestamp); } diff --git a/src/Model/src/Waypoint.cpp b/src/Model/src/Waypoint.cpp index d7a5cf44c..41180fac4 100644 --- a/src/Model/src/Waypoint.cpp +++ b/src/Model/src/Waypoint.cpp @@ -26,11 +26,11 @@ // PUBLIC -Waypoint::Waypoint(float theLatitude, float theLongitude, float theAltitude) noexcept +Waypoint::Waypoint(float latitude, float longitude, float altitude) noexcept : TimeVariableData(), - latitude(theLatitude), - longitude(theLongitude), - altitude(theAltitude) + latitude(latitude), + longitude(longitude), + altitude(altitude) {} bool Waypoint::isValid() const noexcept diff --git a/src/Persistence/CMakeLists.txt b/src/Persistence/CMakeLists.txt index 4bf5d4395..c74962dfc 100644 --- a/src/Persistence/CMakeLists.txt +++ b/src/Persistence/CMakeLists.txt @@ -23,6 +23,7 @@ target_sources(${LIBRARY_NAME} src/Dao/AircraftDaoIntf.h src/Dao/AircraftTypeDaoIntf.h src/Dao/PositionDaoIntf.h + src/Dao/AttitudeDaoIntf.h src/Dao/EngineDaoIntf.h src/Dao/PrimaryFlightControlDaoIntf.h src/Dao/SecondaryFlightControlDaoIntf.h @@ -38,6 +39,7 @@ target_sources(${LIBRARY_NAME} src/Dao/SQLite/SQLiteAircraftDao.h src/Dao/SQLite/SQLiteAircraftDao.cpp src/Dao/SQLite/SQLiteAircraftTypeDao.h src/Dao/SQLite/SQLiteAircraftTypeDao.cpp src/Dao/SQLite/SQLitePositionDao.h src/Dao/SQLite/SQLitePositionDao.cpp + src/Dao/SQLite/SQLiteAttitudeDao.h src/Dao/SQLite/SQLiteAttitudeDao.cpp src/Dao/SQLite/SQLiteEngineDao.h src/Dao/SQLite/SQLiteEngineDao.cpp src/Dao/SQLite/SQLitePrimaryFlightControlDao.h src/Dao/SQLite/SQLitePrimaryFlightControlDao.cpp src/Dao/SQLite/SQLiteSecondaryFlightControlDao.h src/Dao/SQLite/SQLiteSecondaryFlightControlDao.cpp diff --git a/src/Persistence/src/Dao/AttitudeDaoIntf.h b/src/Persistence/src/Dao/AttitudeDaoIntf.h new file mode 100644 index 000000000..5bd0859fa --- /dev/null +++ b/src/Persistence/src/Dao/AttitudeDaoIntf.h @@ -0,0 +1,58 @@ +/** + * Sky Dolly - The Black Sheep for Your Flight Recordings + * + * Copyright (c) Oliver Knoll + * All rights reserved. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this + * software and associated documentation files (the "Software"), to deal in the Software + * without restriction, including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE + * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef ATTITUDEDAOINTF_H +#define ATTITUDEDAOINTF_H + +#include +#include + +struct AttitudeData; + +class AttitudeDaoIntf +{ +public: + AttitudeDaoIntf() = default; + AttitudeDaoIntf(const AttitudeDaoIntf &rhs) = delete; + AttitudeDaoIntf(AttitudeDaoIntf &&rhs) = default; + AttitudeDaoIntf &operator=(const AttitudeDaoIntf &rhs) = delete; + AttitudeDaoIntf &operator=(AttitudeDaoIntf &&rhs) = default; + virtual ~AttitudeDaoIntf() = default; + + /*! + * Persists the \c data. + * + * \param aircraftId + * the aircraft the \c data belongs to + * \param data + * the AttitudeData to be persisted + * \return \c true on success; \c false else + */ + virtual bool add(std::int64_t aircraftId, const AttitudeData &data) const noexcept = 0; + virtual std::vector getByAircraftId(std::int64_t aircraftId, bool *ok = nullptr) const noexcept = 0; + virtual bool deleteByFlightId(std::int64_t flightId) const noexcept = 0; + virtual bool deleteByAircraftId(std::int64_t aircraftId) const noexcept = 0; +}; + +#endif // ATTITUDEDAOINTF_H diff --git a/src/Persistence/src/Dao/SQLite/SQLiteAttitudeDao.cpp b/src/Persistence/src/Dao/SQLite/SQLiteAttitudeDao.cpp new file mode 100644 index 000000000..c85da3728 --- /dev/null +++ b/src/Persistence/src/Dao/SQLite/SQLiteAttitudeDao.cpp @@ -0,0 +1,222 @@ +/** + * Sky Dolly - The Black Sheep for Your Flight Recordings + * + * Copyright (c) Oliver Knoll + * All rights reserved. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this + * software and associated documentation files (the "Software"), to deal in the Software + * without restriction, including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE + * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef DEBUG +#include +#endif + +#include +#include +#include "SQLiteAttitudeDao.h" + +namespace +{ + // The initial capacity of the attitude vector (e.g. SQLite does not support returning + // the result count for the given SELECT query) + // Samples at 30 Hz for an assumed flight duration of 2 * 60 seconds = 2 minutes + constexpr int DefaultCapacity = 30 * 2 * 60; +} + +struct SQLiteAttitudeDaoPrivate +{ + SQLiteAttitudeDaoPrivate(QString connectionName) noexcept + : connectionName(std::move(connectionName)) + {} + + QString connectionName; +}; + +// PUBLIC + +SQLiteAttitudeDao::SQLiteAttitudeDao(QString connectionName) noexcept + : d(std::make_unique(std::move(connectionName))) +{} + +SQLiteAttitudeDao::SQLiteAttitudeDao(SQLiteAttitudeDao &&rhs) noexcept = default; +SQLiteAttitudeDao &SQLiteAttitudeDao::operator=(SQLiteAttitudeDao &&rhs) noexcept = default; +SQLiteAttitudeDao::~SQLiteAttitudeDao() = default; + +bool SQLiteAttitudeDao::add(std::int64_t aircraftId, const AttitudeData &attitude) const noexcept +{ + const QSqlDatabase db {QSqlDatabase::database(d->connectionName)}; + QSqlQuery query {db}; + query.prepare( + "insert into attitude (" + " aircraft_id," + " timestamp," + " pitch," + " bank," + " true_heading," + " velocity_x," + " velocity_y," + " velocity_z," + " on_ground" + ") values (" + " :aircraft_id," + " :timestamp," + " :latitude," + " :longitude," + " :altitude," + " :indicated_altitude," + " :pitch," + " :bank," + " :true_heading," + " :velocity_x," + " :velocity_y," + " :velocity_z," + " :on_ground" + ");" + ); + query.bindValue(":aircraft_id", QVariant::fromValue(aircraftId)); + query.bindValue(":timestamp", QVariant::fromValue(attitude.timestamp)); + query.bindValue(":pitch", attitude.pitch); + query.bindValue(":bank", attitude.bank); + query.bindValue(":true_heading", attitude.trueHeading); + query.bindValue(":velocity_x", attitude.velocityBodyX); + query.bindValue(":velocity_y", attitude.velocityBodyY); + query.bindValue(":velocity_z", attitude.velocityBodyZ); + + const bool ok = query.exec(); + +#ifdef DEBUG + if (!ok) { + qDebug() << "SQLiteAttitudeDao::add: SQL error" << query.lastError().text() << "- error code:" << query.lastError().nativeErrorCode(); + } +#endif + return ok; +} + +std::vector SQLiteAttitudeDao::getByAircraftId(std::int64_t aircraftId, bool *ok) const noexcept +{ + std::vector attitudeData; + const QSqlDatabase db {QSqlDatabase::database(d->connectionName)}; + QSqlQuery query {db}; + query.setForwardOnly(true); + query.prepare( + "select * " + "from attitude a " + "where a.aircraft_id = :aircraft_id " + "order by a.timestamp asc;" + ); + + query.bindValue(":aircraft_id", QVariant::fromValue(aircraftId)); + const bool success = query.exec(); + if (success) { + const QSqlDatabase db {QSqlDatabase::database(d->connectionName)}; + const bool querySizeFeature = db.driver()->hasFeature(QSqlDriver::QuerySize); + if (querySizeFeature) { + attitudeData.reserve(query.size()); + } else { + attitudeData.reserve(::DefaultCapacity); + } + QSqlRecord record = query.record(); + const auto timestampIdx = record.indexOf("timestamp"); + const auto pitchIdx = record.indexOf("pitch"); + const auto bankIdx = record.indexOf("bank"); + const auto trueHeadingIdx = record.indexOf("true_heading"); + const auto velocitXIdx = record.indexOf("velocity_x"); + const auto velocitYIdx = record.indexOf("velocity_y"); + const auto velocitZIdx = record.indexOf("velocity_z"); + const auto onGroundIdx = record.indexOf("on_ground"); + while (query.next()) { + AttitudeData data; + data.timestamp = query.value(timestampIdx).toLongLong(); + data.pitch = query.value(pitchIdx).toDouble(); + data.bank = query.value(bankIdx).toDouble(); + data.trueHeading = query.value(trueHeadingIdx).toDouble(); + data.velocityBodyX = query.value(velocitXIdx).toDouble(); + data.velocityBodyY = query.value(velocitYIdx).toDouble(); + data.velocityBodyZ = query.value(velocitZIdx).toDouble(); + data.onGround = query.value(onGroundIdx).toBool(); + + attitudeData.push_back(std::move(data)); + } +#ifdef DEBUG + } else { + qDebug() << "SQLiteAttitudeDao::getByAircraftId: SQL error" << query.lastError().text() << "- error code:" << query.lastError().nativeErrorCode(); +#endif + } + + if (ok != nullptr) { + *ok = success; + } + return attitudeData; +} + +bool SQLiteAttitudeDao::deleteByFlightId(std::int64_t flightId) const noexcept +{ + const QSqlDatabase db {QSqlDatabase::database(d->connectionName)}; + QSqlQuery query {db}; + query.prepare( + "delete " + "from attitude " + "where aircraft_id in (select a.id " + " from aircraft a" + " where a.flight_id = :flight_id" + " );" + ); + + query.bindValue(":flight_id", QVariant::fromValue(flightId)); + const bool ok = query.exec(); +#ifdef DEBUG + if (!ok) { + qDebug() << "SQLiteAttitudeDao::deleteByFlightId: SQL error" << query.lastError().text() << "- error code:" << query.lastError().nativeErrorCode(); + } +#endif + return ok; +} + +bool SQLiteAttitudeDao::deleteByAircraftId(std::int64_t aircraftId) const noexcept +{ + const QSqlDatabase db {QSqlDatabase::database(d->connectionName)}; + QSqlQuery query {db}; + query.prepare( + "delete " + "from attitude " + "where aircraft_id = :aircraft_id;" + ); + + query.bindValue(":aircraft_id", QVariant::fromValue(aircraftId)); + const bool ok = query.exec(); +#ifdef DEBUG + if (!ok) { + qDebug() << "SQLiteAttitudeDao::deleteByAircraftId: SQL error" << query.lastError().text() << "- error code:" << query.lastError().nativeErrorCode(); + } +#endif + return true; +} diff --git a/src/Persistence/src/Dao/SQLite/SQLiteAttitudeDao.h b/src/Persistence/src/Dao/SQLite/SQLiteAttitudeDao.h new file mode 100644 index 000000000..3dfcd7fd8 --- /dev/null +++ b/src/Persistence/src/Dao/SQLite/SQLiteAttitudeDao.h @@ -0,0 +1,58 @@ +/** + * Sky Dolly - The Black Sheep for Your Flight Recordings + * + * Copyright (c) Oliver Knoll + * All rights reserved. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this + * software and associated documentation files (the "Software"), to deal in the Software + * without restriction, including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE + * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef SQLITEATTITUDEDAO_H +#define SQLITEATTITUDEDAO_H + +#include +#include +#include + +class QString; + +#include "../AttitudeDaoIntf.h" + +struct AttitudeData; +struct SQLiteAttitudeDaoPrivate; + +class SQLiteAttitudeDao final : public AttitudeDaoIntf +{ +public: + SQLiteAttitudeDao(QString connectionName) noexcept; + SQLiteAttitudeDao(const SQLiteAttitudeDao &rhs) = delete; + SQLiteAttitudeDao(SQLiteAttitudeDao &&rhs) noexcept; + SQLiteAttitudeDao &operator=(const SQLiteAttitudeDao &rhs) = delete; + SQLiteAttitudeDao &operator=(SQLiteAttitudeDao &&rhs) noexcept; + ~SQLiteAttitudeDao() override; + + bool add(std::int64_t aircraftId, const AttitudeData &data) const noexcept override; + std::vector getByAircraftId(std::int64_t aircraftId, bool *ok = nullptr) const noexcept override; + bool deleteByFlightId(std::int64_t flightId) const noexcept override; + bool deleteByAircraftId(std::int64_t aircraftId) const noexcept override; + +private: + std::unique_ptr d; +}; + +#endif // SQLITEATTITUDEDAO_H diff --git a/src/Persistence/src/Dao/SQLite/SQLitePositionDao.cpp b/src/Persistence/src/Dao/SQLite/SQLitePositionDao.cpp index 07fae6204..5ba4528c3 100644 --- a/src/Persistence/src/Dao/SQLite/SQLitePositionDao.cpp +++ b/src/Persistence/src/Dao/SQLite/SQLitePositionDao.cpp @@ -75,47 +75,29 @@ bool SQLitePositionDao::add(std::int64_t aircraftId, const PositionData &positio { const QSqlDatabase db {QSqlDatabase::database(d->connectionName)}; QSqlQuery query {db}; - query.prepare(QStringLiteral( + query.prepare( "insert into position (" " aircraft_id," " timestamp," " latitude," " longitude," " altitude," - " indicated_altitude," - " pitch," - " bank," - " true_heading," - " velocity_x," - " velocity_y," - " velocity_z" + " indicated_altitude" ") values (" " :aircraft_id," " :timestamp," " :latitude," " :longitude," " :altitude," - " :indicated_altitude," - " :pitch," - " :bank," - " :true_heading," - " :velocity_x," - " :velocity_y," - " :velocity_z" + " :indicated_altitude" ");" - )); - query.bindValue(QStringLiteral(":aircraft_id"), QVariant::fromValue(aircraftId)); - query.bindValue(QStringLiteral(":timestamp"), QVariant::fromValue(position.timestamp)); - query.bindValue(QStringLiteral(":latitude"), position.latitude); - query.bindValue(QStringLiteral(":longitude"), position.longitude); - query.bindValue(QStringLiteral(":altitude"), position.altitude); - query.bindValue(QStringLiteral(":indicated_altitude"), position.indicatedAltitude); - query.bindValue(QStringLiteral(":pitch"), position.pitch); - query.bindValue(QStringLiteral(":bank"), position.bank); - query.bindValue(QStringLiteral(":true_heading"), position.trueHeading); - query.bindValue(QStringLiteral(":velocity_x"), position.velocityBodyX); - query.bindValue(QStringLiteral(":velocity_y"), position.velocityBodyY); - query.bindValue(QStringLiteral(":velocity_z"), position.velocityBodyZ); + ); + query.bindValue(":aircraft_id", QVariant::fromValue(aircraftId)); + query.bindValue(":timestamp", QVariant::fromValue(position.timestamp)); + query.bindValue(":latitude", position.latitude); + query.bindValue(":longitude", position.longitude); + query.bindValue(":altitude", position.altitude); + query.bindValue(":indicated_altitude", position.indicatedAltitude); const bool ok = query.exec(); @@ -133,14 +115,14 @@ std::vector SQLitePositionDao::getByAircraftId(std::int64_t aircra const QSqlDatabase db {QSqlDatabase::database(d->connectionName)}; QSqlQuery query {db}; query.setForwardOnly(true); - query.prepare(QStringLiteral( + query.prepare( "select * " "from position p " "where p.aircraft_id = :aircraft_id " "order by p.timestamp asc;" - )); + ); - query.bindValue(QStringLiteral(":aircraft_id"), QVariant::fromValue(aircraftId)); + query.bindValue(":aircraft_id", QVariant::fromValue(aircraftId)); const bool success = query.exec(); if (success) { const QSqlDatabase db {QSqlDatabase::database(d->connectionName)}; @@ -151,17 +133,11 @@ std::vector SQLitePositionDao::getByAircraftId(std::int64_t aircra positionData.reserve(::DefaultCapacity); } QSqlRecord record = query.record(); - const int timestampIdx = record.indexOf(QStringLiteral("timestamp")); - const int latitudeIdx = record.indexOf(QStringLiteral("latitude")); - const int longitudeIdx = record.indexOf(QStringLiteral("longitude")); - const int altitudeIdx = record.indexOf(QStringLiteral("altitude")); - const int indicatedAltitudeIdx = record.indexOf(QStringLiteral("indicated_altitude")); - const int pitchIdx = record.indexOf(QStringLiteral("pitch")); - const int bankIdx = record.indexOf(QStringLiteral("bank")); - const int trueHeadingIdx = record.indexOf(QStringLiteral("true_heading")); - const int velocitXIdx = record.indexOf(QStringLiteral("velocity_x")); - const int velocitYIdx = record.indexOf(QStringLiteral("velocity_y")); - const int velocitZIdx = record.indexOf(QStringLiteral("velocity_z")); + const auto timestampIdx = record.indexOf("timestamp"); + const auto latitudeIdx = record.indexOf("latitude"); + const auto longitudeIdx = record.indexOf("longitude"); + const auto altitudeIdx = record.indexOf("altitude"); + const auto indicatedAltitudeIdx = record.indexOf("indicated_altitude"); while (query.next()) { PositionData data; data.timestamp = query.value(timestampIdx).toLongLong(); @@ -169,12 +145,6 @@ std::vector SQLitePositionDao::getByAircraftId(std::int64_t aircra data.longitude = query.value(longitudeIdx).toDouble(); data.altitude = query.value(altitudeIdx).toDouble(); data.indicatedAltitude = query.value(indicatedAltitudeIdx).toDouble(); - data.pitch = query.value(pitchIdx).toDouble(); - data.bank = query.value(bankIdx).toDouble(); - data.trueHeading = query.value(trueHeadingIdx).toDouble(); - data.velocityBodyX = query.value(velocitXIdx).toDouble(); - data.velocityBodyY = query.value(velocitYIdx).toDouble(); - data.velocityBodyZ = query.value(velocitZIdx).toDouble(); positionData.push_back(std::move(data)); } @@ -194,16 +164,16 @@ bool SQLitePositionDao::deleteByFlightId(std::int64_t flightId) const noexcept { const QSqlDatabase db {QSqlDatabase::database(d->connectionName)}; QSqlQuery query {db}; - query.prepare(QStringLiteral( + query.prepare( "delete " "from position " "where aircraft_id in (select a.id " " from aircraft a" " where a.flight_id = :flight_id" " );" - )); + ); - query.bindValue(QStringLiteral(":flight_id"), QVariant::fromValue(flightId)); + query.bindValue(":flight_id", QVariant::fromValue(flightId)); const bool ok = query.exec(); #ifdef DEBUG if (!ok) { @@ -217,13 +187,13 @@ bool SQLitePositionDao::deleteByAircraftId(std::int64_t aircraftId) const noexce { const QSqlDatabase db {QSqlDatabase::database(d->connectionName)}; QSqlQuery query {db}; - query.prepare(QStringLiteral( + query.prepare( "delete " "from position " "where aircraft_id = :aircraft_id;" - )); + ); - query.bindValue(QStringLiteral(":aircraft_id"), QVariant::fromValue(aircraftId)); + query.bindValue(":aircraft_id", QVariant::fromValue(aircraftId)); const bool ok = query.exec(); #ifdef DEBUG if (!ok) { diff --git a/src/PluginManager/include/PluginManager/Connect/SkyConnectIntf.h b/src/PluginManager/include/PluginManager/Connect/SkyConnectIntf.h index b1923afad..21ffb1536 100644 --- a/src/PluginManager/include/PluginManager/Connect/SkyConnectIntf.h +++ b/src/PluginManager/include/PluginManager/Connect/SkyConnectIntf.h @@ -42,6 +42,7 @@ class Flight; class Aircraft; struct PositionData; +struct AttitudeData; class MSFSSimConnectPlugin; class PLUGINMANAGER_API SkyConnectIntf : public QObject, public PluginWithOptionWidgetIntf, public PluginIntf @@ -146,7 +147,7 @@ class PLUGINMANAGER_API SkyConnectIntf : public QObject, public PluginWithOption virtual int getRemainingReconnectTime() const noexcept = 0; virtual bool setUserAircraftInitialPosition(const InitialPosition &initialPosition) noexcept = 0; - virtual bool setUserAircraftPosition(const PositionData &positionData) noexcept = 0; + virtual bool setUserAircraftPositionAndAttitude(const PositionData &positionData, const AttitudeData &attitudeData) noexcept = 0; virtual bool freezeUserAircraft(bool enable) const noexcept = 0; virtual bool sendSimulationEvent(SimulationEvent event, float arg1) noexcept = 0; diff --git a/src/PluginManager/include/PluginManager/SkyConnectManager.h b/src/PluginManager/include/PluginManager/SkyConnectManager.h index 70338099d..24ff7c787 100644 --- a/src/PluginManager/include/PluginManager/SkyConnectManager.h +++ b/src/PluginManager/include/PluginManager/SkyConnectManager.h @@ -49,6 +49,8 @@ class QUuid; struct FlightSimulatorShortcuts; struct SkyConnectManagerPrivate; +struct PositionData; +struct AttitudeData; /// \todo Gradually implement all methods from the SkyConnectIntf and then finally inherit from it class PLUGINMANAGER_API SkyConnectManager final : public QObject @@ -124,7 +126,7 @@ class PLUGINMANAGER_API SkyConnectManager final : public QObject int getRemainingReconnectTime() const noexcept; bool setUserAircraftInitialPosition(const InitialPosition &initialPosition) noexcept; - bool setUserAircraftPosition(const PositionData & positionData) noexcept; + bool setUserAircraftPositionAndAttitude(const PositionData &positionData, const AttitudeData &attitudeData) noexcept; bool freezeUserAircraft(bool enable) noexcept; bool sendSimulationEvent(SkyConnectIntf::SimulationEvent event, float arg1 = 0.0f) noexcept; diff --git a/src/PluginManager/src/Connect/AbstractSkyConnect.cpp b/src/PluginManager/src/Connect/AbstractSkyConnect.cpp index 23045b826..c4f1551b5 100644 --- a/src/PluginManager/src/Connect/AbstractSkyConnect.cpp +++ b/src/PluginManager/src/Connect/AbstractSkyConnect.cpp @@ -47,6 +47,8 @@ #include #include #include +#include +#include #include #include #include @@ -254,7 +256,7 @@ void AbstractSkyConnect::startRecording(RecordingMode recordingMode, const Initi void AbstractSkyConnect::stopRecording() noexcept { onStopRecording(); - Aircraft &aircraft = d->currentFlight.getUserAircraft(); + auto &aircraft = d->currentFlight.getUserAircraft(); aircraft.invalidateDuration(); d->recordingTimer.stop(); // Only go into "recording stopped" state once the aircraft duration has been invalidated, in @@ -625,12 +627,12 @@ void AbstractSkyConnect::updateUserAircraft(int newUserAircraftIndex, int previo // Check if the new user aircraft has just been newly added, which is indicated // by an invalid ID (= it has never been persisted). In such a case it does not // have an associated AI object either - const Aircraft &userAircraft = d->currentFlight[newUserAircraftIndex]; + const auto &userAircraft = d->currentFlight[newUserAircraftIndex]; if (userAircraft.getId() != Const::InvalidId) { removeAiObject(userAircraft.getId()); } if (previousUserAircraftIndex != Const::InvalidIndex) { - const Aircraft &aircraft = d->currentFlight[previousUserAircraftIndex]; + const auto &aircraft = d->currentFlight[previousUserAircraftIndex]; addAiObject(aircraft); } sendAircraftData(d->currentTimestamp, TimeVariableData::Access::DiscreteSeek, AircraftSelection::UserAircraft); @@ -846,13 +848,14 @@ bool AbstractSkyConnect::setupInitialReplayPosition(InitialPosition flyWithForma [[fallthrough]]; case ReplayMode::Normal: if (d->currentTimestamp == 0) { - const Aircraft &userAircraft = getCurrentFlight().getUserAircraft(); + const auto &aircraft = getCurrentFlight().getUserAircraft(); // Make sure recorded position data exists - ok = userAircraft.getPosition().count() > 0; + ok = aircraft.getPosition().count() > 0; if (ok) { - const PositionData &positionData = userAircraft.getPosition().getFirst(); - const AircraftInfo aircraftInfo = userAircraft.getAircraftInfo(); - const InitialPosition initialPosition = InitialPosition(positionData, aircraftInfo); + const auto &positionData = aircraft.getPosition().getFirst(); + const AttitudeData &attitudeData = aircraft.getAttitude().getFirst(); + const AircraftInfo aircraftInfo = aircraft.getAircraftInfo(); + const InitialPosition initialPosition = InitialPosition(positionData, attitudeData, aircraftInfo); ok = onInitialPositionSetup(initialPosition); } } diff --git a/src/PluginManager/src/Export.cpp b/src/PluginManager/src/Export.cpp index c39b31414..05703ed21 100644 --- a/src/PluginManager/src/Export.cpp +++ b/src/PluginManager/src/Export.cpp @@ -61,7 +61,7 @@ QString Export::suggestFlightFilePath(const Flight &flight, QStringView extensio const QString &title = flight.getTitle(); if (title.isNull()) { if (flight.count() > 0) { - const Aircraft &aircraft = flight.getUserAircraft(); + const auto &aircraft = flight.getUserAircraft(); suggestedFileName = aircraft.getAircraftInfo().aircraftType.type; } else { suggestedFileName = Version::getApplicationName(); @@ -93,7 +93,7 @@ std::vector Export::resamplePositionDataForExport(const Aircraft & const std::int64_t deltaTime = Enum::underly(resamplingPeriod); std::int64_t timestamp = 0; while (timestamp <= duration) { - const PositionData &data = position.interpolate(timestamp, TimeVariableData::Access::Export); + const PositionData &data = position.interpolate(timestamp, TimeVariableData::Access::NoTimeOffset); if (!data.isNull()) { interpolatedData.push_back(data); } @@ -112,14 +112,14 @@ std::vector Export::resampleEngineDataForExport(const Aircraft &airc { std::vector interpolatedData; // Position data - Engine &engine = aircraft.getEngine(); + auto &engine = aircraft.getEngine(); if (engine.count() > 0) { if (resamplingPeriod != SampleRate::ResamplingPeriod::Original) { const std::int64_t duration = engine.getLast().timestamp; const std::int64_t deltaTime = Enum::underly(resamplingPeriod); std::int64_t timestamp = 0; while (timestamp <= duration) { - const EngineData &data = engine.interpolate(timestamp, TimeVariableData::Access::Export); + const EngineData &data = engine.interpolate(timestamp, TimeVariableData::Access::NoTimeOffset); if (!data.isNull()) { interpolatedData.push_back(data); } @@ -138,14 +138,14 @@ std::vector Export::resamplePrimaryFlightControlDataFo { std::vector interpolatedData; // Position data - PrimaryFlightControl &primaryFlightControl = aircraft.getPrimaryFlightControl(); + auto &primaryFlightControl = aircraft.getPrimaryFlightControl(); if (primaryFlightControl.count() > 0) { if (resamplingPeriod != SampleRate::ResamplingPeriod::Original) { const std::int64_t duration = primaryFlightControl.getLast().timestamp; const std::int64_t deltaTime = Enum::underly(resamplingPeriod); std::int64_t timestamp = 0; while (timestamp <= duration) { - const PrimaryFlightControlData &data = primaryFlightControl.interpolate(timestamp, TimeVariableData::Access::Export); + const PrimaryFlightControlData &data = primaryFlightControl.interpolate(timestamp, TimeVariableData::Access::NoTimeOffset); if (!data.isNull()) { interpolatedData.push_back(data); } @@ -164,14 +164,14 @@ std::vector Export::resampleSecondaryFlightControlDa { std::vector interpolatedData; // Position data - SecondaryFlightControl &secondaryFlightControl = aircraft.getSecondaryFlightControl(); + auto &secondaryFlightControl = aircraft.getSecondaryFlightControl(); if (secondaryFlightControl.count() > 0) { if (resamplingPeriod != SampleRate::ResamplingPeriod::Original) { const std::int64_t duration = secondaryFlightControl.getLast().timestamp; const std::int64_t deltaTime = Enum::underly(resamplingPeriod); std::int64_t timestamp = 0; while (timestamp <= duration) { - const SecondaryFlightControlData &data = secondaryFlightControl.interpolate(timestamp, TimeVariableData::Access::Export); + const SecondaryFlightControlData &data = secondaryFlightControl.interpolate(timestamp, TimeVariableData::Access::NoTimeOffset); if (!data.isNull()) { interpolatedData.push_back(data); } @@ -190,14 +190,14 @@ std::vector Export::resampleAircraftHandleDataForExport(cons { std::vector interpolatedData; // Position data - AircraftHandle &aircraftHandle = aircraft.getAircraftHandle(); + auto &aircraftHandle = aircraft.getAircraftHandle(); if (aircraftHandle.count() > 0) { if (resamplingPeriod != SampleRate::ResamplingPeriod::Original) { const std::int64_t duration = aircraftHandle.getLast().timestamp; const std::int64_t deltaTime = Enum::underly(resamplingPeriod); std::int64_t timestamp = 0; while (timestamp <= duration) { - const AircraftHandleData &data = aircraftHandle.interpolate(timestamp, TimeVariableData::Access::Export); + const AircraftHandleData &data = aircraftHandle.interpolate(timestamp, TimeVariableData::Access::NoTimeOffset); if (!data.isNull()) { interpolatedData.push_back(data); } @@ -216,14 +216,14 @@ std::vector Export::resampleLightDataForExport(const Aircraft &aircra { std::vector interpolatedData; // Position data - Light &light = aircraft.getLight(); + auto &light = aircraft.getLight(); if (light.count() > 0) { if (resamplingPeriod != SampleRate::ResamplingPeriod::Original) { const std::int64_t duration = light.getLast().timestamp; const std::int64_t deltaTime = Enum::underly(resamplingPeriod); std::int64_t timestamp = 0; while (timestamp <= duration) { - const LightData &data = light.interpolate(timestamp, TimeVariableData::Access::Export); + const LightData &data = light.interpolate(timestamp, TimeVariableData::Access::NoTimeOffset); if (!data.isNull()) { interpolatedData.push_back(data); } diff --git a/src/PluginManager/src/Flight/FlightImportPluginBase.cpp b/src/PluginManager/src/Flight/FlightImportPluginBase.cpp index 729501622..b9e025143 100644 --- a/src/PluginManager/src/Flight/FlightImportPluginBase.cpp +++ b/src/PluginManager/src/Flight/FlightImportPluginBase.cpp @@ -49,6 +49,8 @@ #include #include #include +#include +#include #include #include #include @@ -260,7 +262,7 @@ void FlightImportPluginBase::enrichFlightCondition(FlightData &flightData) const } if (!(flightCondition.endLocalTime.isValid() && flightCondition.endZuluTime.isValid())) { - const Aircraft &aircraft = flightData.getUserAircraft(); + const auto &aircraft = flightData.getUserAircraft(); const Position &position = aircraft.getPosition(); const PositionData &lastPositionData = position.getLast(); flightCondition.endLocalTime = flightCondition.startLocalTime.addMSecs(lastPositionData.timestamp); @@ -276,10 +278,12 @@ void FlightImportPluginBase::enrichAircraftInfo(FlightData &flightData) const no aircraftInfo.aircraftType = d->selectedAircraftType; } - const Position &position = aircraft.getPosition(); + const auto &position = aircraft.getPosition(); + const auto &attitude = aircraft.getAttitude(); if (position.count() > 0) { - const PositionData &firstPositionData = position.getFirst(); - aircraftInfo.initialAirspeed = static_cast(std::round(Convert::feetPerSecondToKnots(firstPositionData.velocityBodyZ))); + const auto &firstPositionData = position.getFirst(); + const auto &firstAttitudeData = attitude.getFirst(); + aircraftInfo.initialAirspeed = static_cast(std::round(Convert::feetPerSecondToKnots(firstAttitudeData.velocityBodyZ))); // Add default waypoints (first and last position) in case none are present in the imported data FlightPlan &flightPlan = aircraft.getFlightPlan(); @@ -295,7 +299,7 @@ void FlightImportPluginBase::enrichAircraftInfo(FlightData &flightData) const no departure.timestamp = firstPositionData.timestamp; flightPlan.add(std::move(departure)); - const PositionData &lastPositionData = position.getLast(); + const auto &lastPositionData = position.getLast(); Waypoint arrival; arrival.identifier = Waypoint::CustomArrivalIdentifier; arrival.latitude = static_cast(lastPositionData.latitude); @@ -317,8 +321,8 @@ void FlightImportPluginBase::enrichAircraftInfo(FlightData &flightData) const no bool FlightImportPluginBase::augmentFlights(std::vector &flightData) const noexcept { bool ok {false}; - for (FlightData &flight : flightData) { - for (Aircraft &aircraft : flight) { + for (FlightData &data : flightData) { + for (Aircraft &aircraft : data) { if (aircraft.getPosition().count() > 0) { d->flightAugmentation.setProcedures(getAugmentationProcedures()); d->flightAugmentation.setAspects(getAugmentationAspects()); @@ -333,7 +337,7 @@ bool FlightImportPluginBase::addAndStoreAircraftToCurrentFlight(const QString &s std::size_t &totalFlightsStored, std::size_t &totalAircraftStored, bool &continueWithDirectoryImport) noexcept { bool ok {true}; - bool newFlight = !currentFlight.hasRecording(); + bool newFlight {!currentFlight.hasRecording()}; bool doAdd {true}; if (importedFlights.size() > 1) { confirmMultiFlightImport(sourceFilePath, importedFlights.size(), doAdd, continueWithDirectoryImport); diff --git a/src/PluginManager/src/SkyConnectManager.cpp b/src/PluginManager/src/SkyConnectManager.cpp index cded59b5c..ad1370ac2 100644 --- a/src/PluginManager/src/SkyConnectManager.cpp +++ b/src/PluginManager/src/SkyConnectManager.cpp @@ -42,6 +42,8 @@ #include #include #include +#include +#include #include #include #include @@ -184,10 +186,10 @@ bool SkyConnectManager::setUserAircraftInitialPosition(const InitialPosition &in return skyConnect ? skyConnect->get().setUserAircraftInitialPosition(initialPosition) : false; } -bool SkyConnectManager::setUserAircraftPosition(const PositionData & positionData) noexcept +bool SkyConnectManager::setUserAircraftPositionAndAttitude(const PositionData &positionData, const AttitudeData &attitudeData) noexcept { std::optional> skyConnect = getCurrentSkyConnect(); - return skyConnect ? skyConnect->get().setUserAircraftPosition(positionData) : false; + return skyConnect ? skyConnect->get().setUserAircraftPositionAndAttitude(positionData, attitudeData) : false; } bool SkyConnectManager::freezeUserAircraft(bool enable) noexcept diff --git a/src/Plugins/Connect/MSFSSimConnectPlugin/src/MSFSSimConnectPlugin.cpp b/src/Plugins/Connect/MSFSSimConnectPlugin/src/MSFSSimConnectPlugin.cpp index 28d09a05c..e90a322b2 100644 --- a/src/Plugins/Connect/MSFSSimConnectPlugin/src/MSFSSimConnectPlugin.cpp +++ b/src/Plugins/Connect/MSFSSimConnectPlugin/src/MSFSSimConnectPlugin.cpp @@ -126,7 +126,7 @@ MSFSSimConnectPlugin::~MSFSSimConnectPlugin() noexcept closeConnection(); } -bool MSFSSimConnectPlugin::setUserAircraftPosition(const PositionData &positionData) noexcept +bool MSFSSimConnectPlugin::setUserAircraftPositionAndAttitude(const PositionData &positionData) noexcept { SimConnectPositionUser simConnectPositionUser {positionData}; const HRESULT result = ::SimConnect_SetDataOnSimObject(d->simConnectHandle, Enum::underly(SimConnectType::DataDefinition::PositionUser), @@ -277,7 +277,7 @@ void MSFSSimConnectPlugin::onStopRecording() noexcept // Update flight plan Flight &flight = getCurrentFlight(); - const Aircraft &userAircraft = flight.getUserAircraft(); + const auto &aircraft = flight.getUserAircraft(); FlightPlan &flightPlan = userAircraft.getFlightPlan(); for (const auto &it : d->flightPlan) { flight.addWaypoint(it.second); @@ -401,7 +401,7 @@ bool MSFSSimConnectPlugin::sendAircraftData(std::int64_t currentTimestamp, TimeV if (objectId != SimConnectAi::InvalidObjectId) { ok = true; - const PositionData &positionData = aircraft.getPosition().interpolate(currentTimestamp, access); + const auto &positionData = aircraft.getPosition().interpolate(currentTimestamp, access); if (!positionData.isNull()) { SimConnectPositionAll simConnnectPositionAll {positionData}; if (isUserAircraft) { diff --git a/src/Plugins/Connect/MSFSSimConnectPlugin/src/MSFSSimConnectPlugin.h b/src/Plugins/Connect/MSFSSimConnectPlugin/src/MSFSSimConnectPlugin.h index fdac37753..8207316f3 100644 --- a/src/Plugins/Connect/MSFSSimConnectPlugin/src/MSFSSimConnectPlugin.h +++ b/src/Plugins/Connect/MSFSSimConnectPlugin/src/MSFSSimConnectPlugin.h @@ -60,7 +60,7 @@ class MSFSSimConnectPlugin : public AbstractSkyConnect MSFSSimConnectPlugin &operator=(MSFSSimConnectPlugin &&rhs) = delete; ~MSFSSimConnectPlugin() noexcept override; - bool setUserAircraftPosition(const PositionData &positionData) noexcept override; + bool setUserAircraftPositionAndAttitude(const PositionData &positionData) noexcept override; protected: ConnectPluginBaseSettings &getPluginSettings() const noexcept override; diff --git a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimConnectAi.cpp b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimConnectAi.cpp index ef1115157..4148379b4 100644 --- a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimConnectAi.cpp +++ b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimConnectAi.cpp @@ -74,7 +74,7 @@ void SimConnectAi::addObject(const Aircraft &aircraft, std::int64_t timestamp) n if (aircraft.getId() != Const::InvalidId) { const AircraftInfo &aircraftInfo = aircraft.getAircraftInfo(); Position &position = aircraft.getPosition(); - const PositionData &positioNData = position.interpolate(timestamp, TimeVariableData::Access::DiscreteSeek); + const auto &positionData = position.interpolate(timestamp, TimeVariableData::Access::DiscreteSeek); const ::SIMCONNECT_DATA_INITPOSITION initialPosition = SimConnectPositionAll::toInitialPosition(positioNData, aircraftInfo.startOnGround, aircraftInfo.initialAirspeed); const ::SIMCONNECT_DATA_REQUEST_ID requestId = Enum::underly(SimConnectType::DataRequest::AiObjectBase) + d->lastAiCreateRequestId; diff --git a/src/Plugins/Connect/PathCreator/src/PathCreatorPlugin.cpp b/src/Plugins/Connect/PathCreator/src/PathCreatorPlugin.cpp index d54f1007a..f3fc59994 100644 --- a/src/Plugins/Connect/PathCreator/src/PathCreatorPlugin.cpp +++ b/src/Plugins/Connect/PathCreator/src/PathCreatorPlugin.cpp @@ -47,6 +47,8 @@ #include #include #include +#include +#include #include #include #include @@ -109,7 +111,7 @@ PathCreatorPlugin::~PathCreatorPlugin() closeConnection(); } -bool PathCreatorPlugin::setUserAircraftPosition([[maybe_unused]] const PositionData &positionData) noexcept +bool PathCreatorPlugin::setUserAircraftPositionAndAttitude([[maybe_unused]] const PositionData &positionData, [[maybe_unused]] const AttitudeData &attitudeData) noexcept { return true; } @@ -187,7 +189,7 @@ void PathCreatorPlugin::onStopRecording() noexcept flightCondition.endZuluTime = QDateTime::currentDateTimeUtc(); flight.setFlightCondition(flightCondition); - Aircraft &aircraft = flight.getUserAircraft(); + auto &aircraft = flight.getUserAircraft(); FlightPlan &flightPlan = aircraft.getFlightPlan(); int waypointCount = static_cast(flightPlan.count()); if (waypointCount > 1) { @@ -343,22 +345,29 @@ void PathCreatorPlugin::frenchConnection() noexcept void PathCreatorPlugin::recordPositionData(std::int64_t timestamp) noexcept { - PositionData aircraftData; - aircraftData.latitude = -90.0 + d->randomGenerator->bounded(180); - aircraftData.longitude = -180.0 + d->randomGenerator->bounded(360.0); - aircraftData.altitude = d->randomGenerator->bounded(60000.0); - aircraftData.indicatedAltitude = d->randomGenerator->bounded(20000.0); - aircraftData.pitch = -90.0 + d->randomGenerator->bounded(180.0); - aircraftData.bank = -180.0 + d->randomGenerator->bounded(360.0); - aircraftData.trueHeading = -180.0 + d->randomGenerator->bounded(360.0); + auto &aircraft = getCurrentFlight().getUserAircraft(); - aircraftData.velocityBodyX = d->randomGenerator->bounded(1.0); - aircraftData.velocityBodyY = d->randomGenerator->bounded(1.0); - aircraftData.velocityBodyZ = d->randomGenerator->bounded(1.0); + PositionData positionData; + positionData.latitude = -90.0 + d->randomGenerator->bounded(180); + positionData.longitude = -180.0 + d->randomGenerator->bounded(360.0); + positionData.altitude = d->randomGenerator->bounded(60000.0); + positionData.indicatedAltitude = d->randomGenerator->bounded(20000.0); - aircraftData.timestamp = timestamp; - Aircraft &aircraft = getCurrentFlight().getUserAircraft(); - aircraft.getPosition().upsertLast(aircraftData); + positionData.timestamp = timestamp; + aircraft.getPosition().upsertLast(positionData); + + AttitudeData attitudeData; + attitudeData.pitch = -90.0 + d->randomGenerator->bounded(180.0); + attitudeData.bank = -180.0 + d->randomGenerator->bounded(360.0); + attitudeData.trueHeading = -180.0 + d->randomGenerator->bounded(360.0); + + attitudeData.velocityBodyX = d->randomGenerator->bounded(1.0); + attitudeData.velocityBodyY = d->randomGenerator->bounded(1.0); + attitudeData.velocityBodyZ = d->randomGenerator->bounded(1.0); + attitudeData.onGround = false; + + positionData.timestamp = timestamp; + aircraft.getAttitude().upsertLast(attitudeData); } void PathCreatorPlugin::recordEngineData(std::int64_t timestamp) noexcept @@ -394,23 +403,23 @@ void PathCreatorPlugin::recordEngineData(std::int64_t timestamp) noexcept engineData.generalEngineCombustion4 = d->randomGenerator->bounded(2) < 1 ? false : true; engineData.timestamp = timestamp; - Aircraft &aircraft = getCurrentFlight().getUserAircraft(); + auto &aircraft = getCurrentFlight().getUserAircraft(); aircraft.getEngine().upsertLast(engineData); } void PathCreatorPlugin::recordPrimaryControls(std::int64_t timestamp) noexcept { PrimaryFlightControlData primaryFlightControlData; - primaryFlightControlData.rudderDeflection = Convert::degreesToRadians(-45.0 + d->randomGenerator->bounded(90.0)); - primaryFlightControlData.elevatorDeflection = Convert::degreesToRadians(-45.0 + d->randomGenerator->bounded(90.0)); - primaryFlightControlData.leftAileronDeflection = Convert::degreesToRadians(-45.0 + d->randomGenerator->bounded(90.0)); - primaryFlightControlData.rightAileronDeflection = Convert::degreesToRadians(-45.0 + d->randomGenerator->bounded(90.0)); + primaryFlightControlData.rudderDeflection = static_cast(Convert::degreesToRadians(-45.0 + d->randomGenerator->bounded(90.0))); + primaryFlightControlData.elevatorDeflection = static_cast(Convert::degreesToRadians(-45.0 + d->randomGenerator->bounded(90.0))); + primaryFlightControlData.leftAileronDeflection = static_cast(Convert::degreesToRadians(-45.0 + d->randomGenerator->bounded(90.0))); + primaryFlightControlData.rightAileronDeflection = static_cast(Convert::degreesToRadians(-45.0 + d->randomGenerator->bounded(90.0))); primaryFlightControlData.rudderPosition = SkyMath::fromNormalisedPosition(-1.0 + d->randomGenerator->bounded(2.0)); primaryFlightControlData.elevatorPosition = SkyMath::fromNormalisedPosition(-1.0 + d->randomGenerator->bounded(2.0)); primaryFlightControlData.aileronPosition = SkyMath::fromNormalisedPosition(-1.0 + d->randomGenerator->bounded(2.0)); primaryFlightControlData.timestamp = timestamp; - Aircraft &aircraft = getCurrentFlight().getUserAircraft(); + auto &aircraft = getCurrentFlight().getUserAircraft(); aircraft.getPrimaryFlightControl().upsertLast(primaryFlightControlData); } @@ -427,7 +436,7 @@ void PathCreatorPlugin::recordSecondaryControls(std::int64_t timestamp) noexcept secondaryFlightControlData.flapsHandleIndex = static_cast(d->randomGenerator->bounded(5)); secondaryFlightControlData.timestamp = timestamp; - Aircraft &aircraft = getCurrentFlight().getUserAircraft(); + auto &aircraft = getCurrentFlight().getUserAircraft(); aircraft.getSecondaryFlightControl().upsertLast(secondaryFlightControlData); } @@ -447,7 +456,7 @@ void PathCreatorPlugin::recordAircraftHandle(std::int64_t timestamp) noexcept aircraftHandleData.smokeEnabled = d->randomGenerator->bounded(2) < 1 ? false : true; aircraftHandleData.timestamp = timestamp; - Aircraft &aircraft = getCurrentFlight().getUserAircraft(); + auto &aircraft = getCurrentFlight().getUserAircraft(); aircraft.getAircraftHandle().upsertLast(aircraftHandleData); } @@ -459,7 +468,7 @@ void PathCreatorPlugin::recordLights(std::int64_t timestamp) noexcept lights = ++lights % 0b1111111111; lightData.timestamp = timestamp; - Aircraft &aircraft = getCurrentFlight().getUserAircraft(); + auto &aircraft = getCurrentFlight().getUserAircraft(); aircraft.getLight().upsertLast(lightData); } @@ -514,7 +523,7 @@ void PathCreatorPlugin::recordFlightCondition() noexcept void PathCreatorPlugin::recordAircraftInfo() noexcept { Flight &flight = getCurrentFlight(); - Aircraft &aircraft = flight.getUserAircraft(); + auto &aircraft = flight.getUserAircraft(); AircraftInfo info(aircraft.getId()); switch (d->randomGenerator->bounded(5)) { diff --git a/src/Plugins/Connect/PathCreator/src/PathCreatorPlugin.h b/src/Plugins/Connect/PathCreator/src/PathCreatorPlugin.h index 01b13cfac..a314307ab 100644 --- a/src/Plugins/Connect/PathCreator/src/PathCreatorPlugin.h +++ b/src/Plugins/Connect/PathCreator/src/PathCreatorPlugin.h @@ -39,6 +39,7 @@ class Flight; class Aircraft; struct PositionData; +struct AttitudeData; struct InitialPosition; class ConnectPluginBaseSettings; class OptionWidgetIntf; @@ -57,7 +58,7 @@ class PathCreatorPlugin : public AbstractSkyConnect PathCreatorPlugin &operator=(PathCreatorPlugin &&rhs) = delete; ~PathCreatorPlugin() override; - bool setUserAircraftPosition(const PositionData &positionData) noexcept override; + bool setUserAircraftPositionAndAttitude(const PositionData &positionData, const AttitudeData &attitudeData) noexcept override; protected: ConnectPluginBaseSettings &getPluginSettings() const noexcept override; diff --git a/src/Plugins/Flight/Export/CsvExport/src/FlightRadar24CsvWriter.cpp b/src/Plugins/Flight/Export/CsvExport/src/FlightRadar24CsvWriter.cpp index 8aa8b8f71..405e92b4c 100644 --- a/src/Plugins/Flight/Export/CsvExport/src/FlightRadar24CsvWriter.cpp +++ b/src/Plugins/Flight/Export/CsvExport/src/FlightRadar24CsvWriter.cpp @@ -24,7 +24,6 @@ */ #include #include -#include #include #include @@ -39,6 +38,9 @@ #include #include #include +#include +#include +#include #include #include #include "CsvExportSettings.h" @@ -79,8 +81,8 @@ bool FlightRadar24CsvWriter::write(const FlightData &flightData, const Aircraft ::CallsignColumn % Csv::CommaSep % ::PositionColumn % Csv::CommaSep % ::AltitudeColumn % Csv::CommaSep % - ::SpeedColumn % Csv::CommaSep - % ::DirectionColumn % Csv::Ln; + ::SpeedColumn % Csv::CommaSep % + ::DirectionColumn % Csv::Ln; bool ok = io.write(csv.toUtf8()); if (ok) { @@ -88,15 +90,16 @@ bool FlightRadar24CsvWriter::write(const FlightData &flightData, const Aircraft const QString callSign = flightData.flightNumber; const std::vector interpolatedPositionData = Export::resamplePositionDataForExport(aircraft, d->pluginSettings.getResamplingPeriod()); for (const auto &positionData : interpolatedPositionData) { - const QDateTime dateTimeUtc = startDateTimeUtc.addMSecs(positionData.timestamp); - const std::int64_t secsSinceEpoch = dateTimeUtc.toSecsSinceEpoch(); + const auto &attitudeData = aircraft.getAttitude().interpolate(positionData.timestamp, TimeVariableData::Access::NoTimeOffset); + const auto dateTimeUtc = startDateTimeUtc.addMSecs(positionData.timestamp); + const auto secsSinceEpoch = dateTimeUtc.toSecsSinceEpoch(); const QString csv = QString::number(secsSinceEpoch) % Csv::CommaSep % dateTimeUtc.toString(Qt::ISODate) % Csv::CommaSep % callSign % Csv::CommaSep % formatPosition(positionData) % Csv::CommaSep % QString::number(static_cast(std::round(positionData.altitude))) % Csv::CommaSep % - QString::number(static_cast(std::round(positionData.velocityBodyZ))) % Csv::CommaSep % - QString::number(static_cast(std::round(positionData.trueHeading))) % Csv::Ln; + QString::number(static_cast(std::round(attitudeData.velocityBodyZ))) % Csv::CommaSep % + QString::number(static_cast(std::round(attitudeData.trueHeading))) % Csv::Ln; ok = io.write(csv.toUtf8()); if (!ok) { break; diff --git a/src/Plugins/Flight/Export/CsvExport/src/PositionAndAttitudeCsvWriter.cpp b/src/Plugins/Flight/Export/CsvExport/src/PositionAndAttitudeCsvWriter.cpp index 397e2f125..4bec97124 100644 --- a/src/Plugins/Flight/Export/CsvExport/src/PositionAndAttitudeCsvWriter.cpp +++ b/src/Plugins/Flight/Export/CsvExport/src/PositionAndAttitudeCsvWriter.cpp @@ -40,6 +40,8 @@ #include #include #include +#include +#include #include #include #include "CsvExportSettings.h" @@ -89,19 +91,20 @@ bool PositionAndAttitudeCsvWriter::write(const FlightData &flightData, const Air bool ok = io.write(csv.toUtf8()); if (ok) { - const QDateTime startDateTimeUtc = flightData.getAircraftStartZuluTime(aircraft); - const std::vector interpolatedPositionData = Export::resamplePositionDataForExport(aircraft, d->pluginSettings.getResamplingPeriod()); + const auto startDateTimeUtc = flightData.getAircraftStartZuluTime(aircraft); + const auto interpolatedPositionData = Export::resamplePositionDataForExport(aircraft, d->pluginSettings.getResamplingPeriod()); for (const auto &positionData : interpolatedPositionData) { - const QDateTime dateTimeUtc = startDateTimeUtc.addMSecs(positionData.timestamp); + const auto attitudeData = aircraft.getAttitude().interpolate(positionData.timestamp, TimeVariableData::Access::NoTimeOffset); + const auto dateTimeUtc = startDateTimeUtc.addMSecs(positionData.timestamp); const QString csv = QString::number(positionData.timestamp) % Csv::CommaSep % dateTimeUtc.toString(Qt::ISODate) % Csv::CommaSep % Export::formatCoordinate(positionData.latitude) % Csv::CommaSep % Export::formatCoordinate(positionData.longitude) % Csv::CommaSep % QString::number(static_cast(std::round(positionData.altitude))) % Csv::CommaSep % - QString::number(static_cast(std::round(positionData.velocityBodyZ))) % Csv::CommaSep % - QString::number(static_cast(std::round(positionData.pitch))) % Csv::CommaSep % - QString::number(static_cast(std::round(positionData.bank))) % Csv::CommaSep % - QString::number(static_cast(std::round(positionData.trueHeading))) % Csv::Ln; + QString::number(static_cast(std::round(attitudeData.velocityBodyZ))) % Csv::CommaSep % + QString::number(static_cast(std::round(attitudeData.pitch))) % Csv::CommaSep % + QString::number(static_cast(std::round(attitudeData.bank))) % Csv::CommaSep % + QString::number(static_cast(std::round(attitudeData.trueHeading))) % Csv::Ln; ok = io.write(csv.toUtf8()); if (!ok) { break; diff --git a/src/Plugins/Flight/Export/IgcExport/src/IgcExportPlugin.cpp b/src/Plugins/Flight/Export/IgcExport/src/IgcExportPlugin.cpp index eec18b1a0..9ab4d1a6b 100644 --- a/src/Plugins/Flight/Export/IgcExport/src/IgcExportPlugin.cpp +++ b/src/Plugins/Flight/Export/IgcExport/src/IgcExportPlugin.cpp @@ -28,7 +28,6 @@ #include #include -// Implements the % operator for string concatenation #include #include #include @@ -50,10 +49,13 @@ #include #include #include +#include +#include #include #include #include #include +#include #include #include "IgcExportOptionWidget.h" #include "IgcExportSettings.h" @@ -244,13 +246,13 @@ inline bool IgcExportPlugin::exportCRecord(const FlightData &flightData, const A while (ok && i < count) { const Waypoint &waypoint = flightPlan[i]; if (i == 0) { - const PositionData &positionData = position.getFirst(); + const auto &positionData = position.getFirst(); record = IgcExportPluginPrivate::CRecord % formatPosition(waypoint.latitude, waypoint.longitude); record = record % ::TakeoffPoint % " " % waypoint.identifier.toLatin1() % ::LineEnd; record = record % IgcExportPluginPrivate::CRecord % formatPosition(positionData.latitude, positionData.longitude); record = record % ::StartPoint % ::LineEnd; } else if (i == count - 1) { - const PositionData &positionData = position.getLast(); + const auto &positionData = position.getLast(); record = IgcExportPluginPrivate::CRecord % formatPosition(positionData.latitude, positionData.longitude); record = record % ::FinishPoint % ::LineEnd; record = record % IgcExportPluginPrivate::CRecord % formatPosition(waypoint.latitude, waypoint.longitude); @@ -273,7 +275,7 @@ inline bool IgcExportPlugin::exportFixes(const FlightData &flightData, const Air QDateTime lastKFixTime; Convert convert; - Engine &engine = aircraft.getEngine(); + auto &engine = aircraft.getEngine(); const std::vector interpolatedPositionData = Export::resamplePositionDataForExport(aircraft, d->pluginSettings.getResamplingPeriod()); bool ok {true}; for (const auto &positionData : interpolatedPositionData) { @@ -300,11 +302,13 @@ inline bool IgcExportPlugin::exportFixes(const FlightData &flightData, const Air ok = io.write(bRecord); if (ok && (lastKFixTime.isNull() || lastKFixTime.secsTo(currentTime) >= ::KRecordIntervalSec)) { - const double trueAirspeed = Convert::feetPerSecondToKilometersPerHour(positionData.velocityBodyZ); - const double indicatedAirspeed = Convert::trueToIndicatedAirspeed(trueAirspeed, positionData.altitude); + const auto &attitude = aircraft.getAttitude(); + const auto &attitudeData = attitude.interpolate(positionData.timestamp, TimeVariableData::Access::NoTimeOffset); + const auto trueAirspeed = Convert::feetPerSecondToKilometersPerHour(attitudeData.velocityBodyZ); + const auto indicatedAirspeed = Convert::trueToIndicatedAirspeed(trueAirspeed, positionData.altitude); const QByteArray kRecord = IgcExportPluginPrivate::KRecord % formatTime(currentTime) % - formatNumber(static_cast(std::round(positionData.trueHeading)), 3) % + formatNumber(static_cast(std::round(attitudeData.trueHeading)), 3) % // IAS: km/h formatNumber(static_cast(std::round(indicatedAirspeed)), 3) % ::LineEnd; diff --git a/src/Plugins/Flight/Export/KmlExport/src/KmlExportPlugin.cpp b/src/Plugins/Flight/Export/KmlExport/src/KmlExportPlugin.cpp index e0b86a039..1588c43ce 100644 --- a/src/Plugins/Flight/Export/KmlExport/src/KmlExportPlugin.cpp +++ b/src/Plugins/Flight/Export/KmlExport/src/KmlExportPlugin.cpp @@ -44,6 +44,8 @@ #include #include #include +#include +#include #include #include #include "KmlExportOptionWidget.h" @@ -178,10 +180,11 @@ bool KmlExportPlugin::exportHeader(const QString &title, QIODevice &io) const no bool KmlExportPlugin::exportFlightInfo(const FlightData &flightData, QIODevice &io) const noexcept { bool ok {true}; - const Aircraft &aircraft = flightData.getUserAircraftConst(); + const auto &aircraft = flightData.getUserAircraftConst(); if (aircraft.getPosition().count() > 0) { - const PositionData &positionData = aircraft.getPosition().getFirst(); - ok = exportPlacemark(io, KmlStyleExport::Icon::Airport, flightData.title, getFlightDescription(flightData), positionData); + const auto &positionData = aircraft.getPosition().getFirst(); + const AttitudeData &attitudeData = aircraft.getAttitude().getFirst(); + ok = exportPlacemark(io, KmlStyleExport::Icon::Airport, flightData.title, getFlightDescription(flightData), positionData, attitudeData); } else { ok = false; } @@ -345,12 +348,11 @@ QString KmlExportPlugin::getWaypointDescription(const Waypoint &waypoint) const QObject::tr("Altitude") % ": " % d->unit.formatFeet(waypoint.altitude) % "\n"; } -inline bool KmlExportPlugin::exportPlacemark(QIODevice &io, KmlStyleExport::Icon icon, const QString &name, const QString &description, const PositionData &positionData) const noexcept +inline bool KmlExportPlugin::exportPlacemark(QIODevice &io, KmlStyleExport::Icon icon, const QString &name, const QString &description, const PositionData &positionData, const AttitudeData &attitudeData) const noexcept { - bool ok = !positionData.isNull(); + bool ok {!positionData.isNull() && !attitudeData.isNull()}; if (ok) { - ok = exportPlacemark(io, icon, name, description, - positionData.longitude, positionData.latitude, positionData.altitude, positionData.trueHeading); + ok = exportPlacemark(io, icon, name, description, positionData.longitude, positionData.latitude, positionData.altitude, attitudeData.trueHeading); } return ok; } diff --git a/src/Plugins/Flight/Export/KmlExport/src/KmlExportPlugin.h b/src/Plugins/Flight/Export/KmlExport/src/KmlExportPlugin.h index 200b2e446..3f3481053 100644 --- a/src/Plugins/Flight/Export/KmlExport/src/KmlExportPlugin.h +++ b/src/Plugins/Flight/Export/KmlExport/src/KmlExportPlugin.h @@ -42,6 +42,7 @@ class QString; struct FlightData; class Aircraft; struct PositionData; +struct AttitudeData; class FlightPlan; struct Waypoint; class FlightExportPluginBaseSettings; @@ -83,7 +84,7 @@ class KmlExportPlugin : public FlightExportPluginBase QString getWaypointDescription(const Waypoint &waypoint) const noexcept; inline bool exportPlacemark(QIODevice &io, KmlStyleExport::Icon icon, const QString &name, const QString &description, - const PositionData &positionData) const noexcept; + const PositionData &positionData, const AttitudeData &attitudeData) const noexcept; inline bool exportPlacemark(QIODevice &io, KmlStyleExport::Icon icon, const QString &name, const QString &description, double longitude, double latitude, double altitudeInFeet, double heading) const noexcept; }; diff --git a/src/Plugins/Flight/Import/CsvImport/src/FlightRadar24CsvParser.cpp b/src/Plugins/Flight/Import/CsvImport/src/FlightRadar24CsvParser.cpp index 3a15fa8d9..88c42d8af 100644 --- a/src/Plugins/Flight/Import/CsvImport/src/FlightRadar24CsvParser.cpp +++ b/src/Plugins/Flight/Import/CsvImport/src/FlightRadar24CsvParser.cpp @@ -23,6 +23,7 @@ * DEALINGS IN THE SOFTWARE. */ #include +#include #include #include @@ -40,6 +41,8 @@ #include #include #include +#include +#include #include #include "FlightRadar24CsvParser.h" @@ -98,20 +101,22 @@ FlightData FlightRadar24CsvParser::parse(QIODevice &io, bool &ok) noexcept CsvParser csvParser; QTextStream textStream(&io); textStream.setEncoding(QStringConverter::Utf8); - CsvParser::Rows rows = csvParser.parse(textStream, QString::fromLatin1(Header::FlightRadar24Csv)); + CsvParser::Rows rows = csvParser.parse(textStream, Header::FlightRadar24Csv); d->headers = csvParser.getHeaders(); ok = validateHeaders(); if (ok) { ok = CsvParser::validate(rows, d->headers.size()); } if (ok) { - Aircraft &aircraft = flightData.addUserAircraft(); - Position &position = aircraft.getPosition(); + auto &aircraft = flightData.addUserAircraft(); + auto &position = aircraft.getPosition(); + auto &attitude = aircraft.getAttitude(); position.reserve(rows.size()); for (const auto &row : rows) { - const PositionData positionData = parsePosition(row, firstDateTimeUtc, flightNumber, ok); + const auto positionAndAttitude = parsePosition(row, firstDateTimeUtc, flightNumber, ok); if (ok) { - position.upsertLast(positionData); + position.upsertLast(positionAndAttitude.first); + attitude.upsertLast(positionAndAttitude.second); } else { break; } @@ -133,7 +138,7 @@ bool FlightRadar24CsvParser::validateHeaders() const noexcept { bool ok {true}; for (const auto val : d->HeaderNames) { - ok = d->headers.contains(QString::fromLatin1(val)); + ok = d->headers.contains(val); if (!ok) { break; } @@ -141,22 +146,23 @@ bool FlightRadar24CsvParser::validateHeaders() const noexcept return ok; } -inline PositionData FlightRadar24CsvParser::parsePosition(const CsvParser::Row &row, QDateTime &firstDateTimeUtc, QString &flightNumber, bool &ok) const noexcept +inline std::pair FlightRadar24CsvParser::parsePosition(const CsvParser::Row &row, QDateTime &firstDateTimeUtc, QString &flightNumber, bool &ok) const noexcept { PositionData positionData; + AttitudeData attitudeData; std::int64_t timestamp {0}; QDateTime currentDateTimeUtc; currentDateTimeUtc.setTimeZone(QTimeZone::utc()); ok = true; // In seconds after 1970-01-01 UTC - const std::int64_t unixTimestamp = row.at(d->headers.at(QString::fromLatin1(Header::Timestamp))).toLongLong(&ok); + const std::int64_t unixTimestamp = row.at(d->headers.at(Header::Timestamp)).toLongLong(&ok); if (ok) { if (firstDateTimeUtc.isNull()) { firstDateTimeUtc.setSecsSinceEpoch(unixTimestamp); currentDateTimeUtc = firstDateTimeUtc; timestamp = 0; - flightNumber = row.at(d->headers.at(QString::fromLatin1(Header::Callsign))); + flightNumber = row.at(d->headers.at(Header::Callsign)); } else { currentDateTimeUtc.setSecsSinceEpoch(unixTimestamp); timestamp = firstDateTimeUtc.msecsTo(currentDateTimeUtc); @@ -164,7 +170,7 @@ inline PositionData FlightRadar24CsvParser::parsePosition(const CsvParser::Row & } if (ok) { positionData.timestamp = timestamp; - const QString &position = row.at(d->headers.at(QString::fromLatin1(Header::Position))); + const QString &position = row.at(d->headers.at(Header::Position)); const QStringList coordinates = position.split(','); if (coordinates.size() == 2) { positionData.latitude = coordinates.first().toDouble(&ok); @@ -176,14 +182,15 @@ inline PositionData FlightRadar24CsvParser::parsePosition(const CsvParser::Row & } } if (ok) { - positionData.altitude = row.at(d->headers.at(QString::fromLatin1(Header::Altitude))).toDouble(&ok); + positionData.altitude = row.at(d->headers.at(Header::Altitude)).toDouble(&ok); positionData.indicatedAltitude = positionData.altitude; } if (ok) { - positionData.velocityBodyZ = row.at(d->headers.at(QString::fromLatin1(Header::Speed))).toDouble(&ok); + attitudeData.timestamp = timestamp; + attitudeData.velocityBodyZ = row.at(d->headers.at(Header::Speed)).toDouble(&ok); } if (ok) { - positionData.trueHeading = row.at(d->headers.at(QString::fromLatin1(Header::Direction))).toDouble(&ok); + attitudeData.trueHeading = row.at(d->headers.at(Header::Direction)).toDouble(&ok); } - return positionData; + return std::make_pair(positionData, attitudeData); } diff --git a/src/Plugins/Flight/Import/CsvImport/src/FlightRadar24CsvParser.h b/src/Plugins/Flight/Import/CsvImport/src/FlightRadar24CsvParser.h index c6915bd09..712a5b166 100644 --- a/src/Plugins/Flight/Import/CsvImport/src/FlightRadar24CsvParser.h +++ b/src/Plugins/Flight/Import/CsvImport/src/FlightRadar24CsvParser.h @@ -26,6 +26,7 @@ #define FLIGHTRADAR24CSVPARSER_H #include +#include class QIODevice; class QDateTime; @@ -33,6 +34,7 @@ class QString; #include #include +#include #include "CsvParserIntf.h" struct FlightData; @@ -53,7 +55,7 @@ class FlightRadar24CsvParser : public CsvParserIntf const std::unique_ptr d; bool validateHeaders() const noexcept; - inline PositionData parsePosition(const CsvParser::Row &row, QDateTime &firstDateTimeUtc, QString &flightNumber, bool &ok) const noexcept; + inline std::pair parsePosition(const CsvParser::Row &row, QDateTime &firstDateTimeUtc, QString &flightNumber, bool &ok) const noexcept; }; #endif // FLIGHTRADAR24CSVPARSER_H diff --git a/src/Plugins/Flight/Import/CsvImport/src/FlightRecorderCsvParser.cpp b/src/Plugins/Flight/Import/CsvImport/src/FlightRecorderCsvParser.cpp index feab87372..b9faca250 100644 --- a/src/Plugins/Flight/Import/CsvImport/src/FlightRecorderCsvParser.cpp +++ b/src/Plugins/Flight/Import/CsvImport/src/FlightRecorderCsvParser.cpp @@ -45,6 +45,8 @@ #include #include #include +#include +#include #include #include #include @@ -132,49 +134,49 @@ struct FlightRecorderCsvParserPrivate CsvParser::Headers headers; static constexpr std::array HeaderNames { - Header::Milliseconds, - Header::Latitude, - Header::Longitude, - Header::Altitude, - Header::Pitch, - Header::Bank, - Header::TrueHeading, - Header::VelocityBodyX, - Header::VelocityBodyY, - Header::VelocityBodyZ, - Header::RotationVelocityBodyX, - Header::RotationVelocityBodyY, - Header::RotationVelocityBodyZ, - Header::ThrottleLeverPosition1, - Header::ThrottleLeverPosition2, - Header::ThrottleLeverPosition3, - Header::ThrottleLeverPosition4, - Header::PropellerLeverPosition1, - Header::PropellerLeverPosition2, - Header::PropellerLeverPosition3, - Header::PropellerLeverPosition4, - Header::RudderPosition, - Header::ElevatorPosition, - Header::AileronPosition, - Header::LeadingEdgeFlapsLeftPercent, - Header::LeadingEdgeFlapsRightPercent, - Header::TrailingEdgeFlapsLeftPercent, - Header::TrailingEdgeFlapsRightPercent, - Header::SpoilerHandlePosition, - Header::FlapsHandleIndex, - Header::BrakeLeftPosition, - Header::BrakeRightPosition, - Header::WaterRudderHandlePosition, - Header::GearHandlePosition, - Header::LightTaxi, - Header::LightLanding, - Header::LightStrobe, - Header::LightBeacon, - Header::LightNav, - Header::LightWing, - Header::LightLogo, - Header::LightRecognition, - Header::LightCabin + Header::Milliseconds, + Header::Latitude, + Header::Longitude, + Header::Altitude, + Header::Pitch, + Header::Bank, + Header::TrueHeading, + Header::VelocityBodyX, + Header::VelocityBodyY, + Header::VelocityBodyZ, + Header::RotationVelocityBodyX, + Header::RotationVelocityBodyY, + Header::RotationVelocityBodyZ, + Header::ThrottleLeverPosition1, + Header::ThrottleLeverPosition2, + Header::ThrottleLeverPosition3, + Header::ThrottleLeverPosition4, + Header::PropellerLeverPosition1, + Header::PropellerLeverPosition2, + Header::PropellerLeverPosition3, + Header::PropellerLeverPosition4, + Header::RudderPosition, + Header::ElevatorPosition, + Header::AileronPosition, + Header::LeadingEdgeFlapsLeftPercent, + Header::LeadingEdgeFlapsRightPercent, + Header::TrailingEdgeFlapsLeftPercent, + Header::TrailingEdgeFlapsRightPercent, + Header::SpoilerHandlePosition, + Header::FlapsHandleIndex, + Header::BrakeLeftPosition, + Header::BrakeRightPosition, + Header::WaterRudderHandlePosition, + Header::GearHandlePosition, + Header::LightTaxi, + Header::LightLanding, + Header::LightStrobe, + Header::LightBeacon, + Header::LightNav, + Header::LightWing, + Header::LightLogo, + Header::LightRecognition, + Header::LightCabin }; }; @@ -199,7 +201,7 @@ FlightData FlightRecorderCsvParser::parse(QIODevice &io, bool &ok) noexcept ok = CsvParser::validate(rows, d->headers.size()); } if (ok) { - Aircraft &aircraft = flightData.addUserAircraft(); + auto &aircraft = flightData.addUserAircraft(); aircraft.getPosition().reserve(rows.size()); aircraft.getEngine().reserve(rows.size()); aircraft.getPrimaryFlightControl().reserve(rows.size()); @@ -207,7 +209,7 @@ FlightData FlightRecorderCsvParser::parse(QIODevice &io, bool &ok) noexcept aircraft.getAircraftHandle().reserve(rows.size()); aircraft.getLight().reserve(rows.size()); #ifdef DEBUG - qDebug() << "parse::parse, total CSV rows:" << rows.size() << "\n" + qDebug() << "FlightRecorderCsvParser::parse, total CSV rows:" << rows.size() << "\n" << "Position size:" << aircraft.getPosition().capacity() << "\n" << "Engine size:" << aircraft.getEngine().capacity() << "\n" << "Primary flight controls size:" << aircraft.getPrimaryFlightControl().capacity() << "\n" @@ -252,16 +254,18 @@ FlightData FlightRecorderCsvParser::parse(QIODevice &io, bool &ok) noexcept bool FlightRecorderCsvParser::parseRow(const CsvParser::Row &row, FlightData &flightData) noexcept { - const Aircraft &aircraft = flightData.getUserAircraftConst(); - Position &position = aircraft.getPosition(); - Engine &engine = aircraft.getEngine(); - PrimaryFlightControl &primaryFlightControl = aircraft.getPrimaryFlightControl(); - SecondaryFlightControl &secondaryFlightControl = aircraft.getSecondaryFlightControl(); - AircraftHandle &aircraftHandle = aircraft.getAircraftHandle(); - Light &light = aircraft.getLight(); + const auto &aircraft = flightData.getUserAircraftConst(); + auto &position = aircraft.getPosition(); + auto &attitude = aircraft.getAttitude(); + auto &engine = aircraft.getEngine(); + auto &primaryFlightControl = aircraft.getPrimaryFlightControl(); + auto &secondaryFlightControl = aircraft.getSecondaryFlightControl(); + auto &aircraftHandle = aircraft.getAircraftHandle(); + auto &light = aircraft.getLight(); // Position PositionData positionData; + AttitudeData attitudeData; bool ok {true}; const std::int64_t timestamp = row.at(d->headers.at(QString::fromLatin1(Header::Milliseconds))).toLongLong(&ok) - d->timestampDelta; if (ok) { @@ -276,26 +280,28 @@ bool FlightRecorderCsvParser::parseRow(const CsvParser::Row &row, FlightData &fl positionData.indicatedAltitude = positionData.altitude; } if (ok) { - positionData.pitch = row.at(d->headers.at(QString::fromLatin1(Header::Pitch))).toDouble(&ok); + attitudeData.timestamp = timestamp; + attitudeData.pitch = row.at(d->headers.at(QString::fromLatin1(Header::Pitch))).toDouble(&ok); } if (ok) { - positionData.bank = row.at(d->headers.at(QString::fromLatin1(Header::Bank))).toDouble(&ok); + attitudeData.bank = row.at(d->headers.at(QString::fromLatin1(Header::Bank))).toDouble(&ok); } if (ok) { - positionData.trueHeading = row.at(d->headers.at(QString::fromLatin1(Header::TrueHeading))).toDouble(&ok); + attitudeData.trueHeading = row.at(d->headers.at(QString::fromLatin1(Header::TrueHeading))).toDouble(&ok); } if (ok) { - positionData.velocityBodyX = row.at(d->headers.at(QString::fromLatin1(Header::VelocityBodyX))).toDouble(&ok); + attitudeData.velocityBodyX = row.at(d->headers.at(QString::fromLatin1(Header::VelocityBodyX))).toDouble(&ok); } if (ok) { - positionData.velocityBodyY = row.at(d->headers.at(QString::fromLatin1(Header::VelocityBodyY))).toDouble(&ok); + attitudeData.velocityBodyY = row.at(d->headers.at(QString::fromLatin1(Header::VelocityBodyY))).toDouble(&ok); } if (ok) { - positionData.velocityBodyZ = row.at(d->headers.at(QString::fromLatin1(Header::VelocityBodyZ))).toDouble(&ok); + attitudeData.velocityBodyZ = row.at(d->headers.at(QString::fromLatin1(Header::VelocityBodyZ))).toDouble(&ok); } if (ok) { position.upsertLast(positionData); + attitude.upsertLast(attitudeData); } // Engine diff --git a/src/Plugins/Flight/Import/GpxImport/src/GpxParser.cpp b/src/Plugins/Flight/Import/GpxImport/src/GpxParser.cpp index 06d7db698..bb10da1a2 100644 --- a/src/Plugins/Flight/Import/GpxImport/src/GpxParser.cpp +++ b/src/Plugins/Flight/Import/GpxImport/src/GpxParser.cpp @@ -140,7 +140,7 @@ void GpxParser::parseWaypoint(FlightData &flightData) noexcept double latitude {0.0}, longitude {0.0}, altitude {0.0}; QString identifier; QDateTime currentDateTimeUtc; - Aircraft &aircraft = flightData.getUserAircraft(); + auto &aircraft = flightData.getUserAircraft(); if (d->pluginSettings.getWaypointSelection() == GpxImportSettings::GPXElement::Waypoint || d->pluginSettings.getPositionSelection() == GpxImportSettings::GPXElement::Waypoint) { @@ -210,7 +210,7 @@ void GpxParser::parseRoutePoint(FlightData &flightData) noexcept double latitude {0.0}, longitude {0.0}, altitude {0.0}; QString identifier; QDateTime currentDateTimeUtc; - Aircraft &aircraft = flightData.getUserAircraft(); + auto &aircraft = flightData.getUserAircraft(); if (d->pluginSettings.getWaypointSelection() == GpxImportSettings::GPXElement::Route || d->pluginSettings.getPositionSelection() == GpxImportSettings::GPXElement::Route) { @@ -288,7 +288,7 @@ inline void GpxParser::parseTrackPoint(FlightData &flightData) noexcept double latitude {0.0}, longitude {0.0}, altitude {0.0}; QString identifier; QDateTime currentDateTimeUtc; - Aircraft &aircraft = flightData.getUserAircraft(); + auto &aircraft = flightData.getUserAircraft(); if (d->pluginSettings.getWaypointSelection() == GpxImportSettings::GPXElement::Track || diff --git a/src/Plugins/Flight/Import/IgcImport/src/IgcImportPlugin.cpp b/src/Plugins/Flight/Import/IgcImport/src/IgcImportPlugin.cpp index bcf4a0fb6..acee614c9 100644 --- a/src/Plugins/Flight/Import/IgcImport/src/IgcImportPlugin.cpp +++ b/src/Plugins/Flight/Import/IgcImport/src/IgcImportPlugin.cpp @@ -100,12 +100,12 @@ std::vector IgcImportPlugin::importFlightData(QIODevice &io, bool &o ok = d->igcParser.parse(io); if (ok) { FlightData flightData; - Aircraft &aircraft = flightData.addUserAircraft(); + auto &aircraft = flightData.addUserAircraft(); // Now "upsert" the position data, taking possible duplicate timestamps into account Position &position = aircraft.getPosition(); // Engine - Engine &engine = aircraft.getEngine(); + auto &engine = aircraft.getEngine(); EngineData engineData; IgcImportPluginPrivate::EngineState engineState = IgcImportPluginPrivate::EngineState::Unknown; const double enlThresholdNorm = static_cast(d->pluginSettings.getEnlThresholdPercent()) / 100.0; diff --git a/src/Plugins/Flight/Import/KmlImport/src/FlightAwareKmlParser.cpp b/src/Plugins/Flight/Import/KmlImport/src/FlightAwareKmlParser.cpp index cbaab58fc..e3350c750 100644 --- a/src/Plugins/Flight/Import/KmlImport/src/FlightAwareKmlParser.cpp +++ b/src/Plugins/Flight/Import/KmlImport/src/FlightAwareKmlParser.cpp @@ -94,7 +94,7 @@ void FlightAwareKmlParser::parsePlacemark(FlightData &flightData) noexcept void FlightAwareKmlParser::parseWaypoint(FlightData &flightData, QString icaoOrName) noexcept { - Aircraft &aircraft = flightData.getUserAircraft(); + auto &aircraft = flightData.getUserAircraft(); QXmlStreamReader *xml = getXmlStreamReader(); bool ok {true}; while (xml->readNextStartElement()) { diff --git a/src/Plugins/Flight/Import/KmlImport/src/FlightRadar24KmlParser.cpp b/src/Plugins/Flight/Import/KmlImport/src/FlightRadar24KmlParser.cpp index 15a6e426c..1426a7c26 100644 --- a/src/Plugins/Flight/Import/KmlImport/src/FlightRadar24KmlParser.cpp +++ b/src/Plugins/Flight/Import/KmlImport/src/FlightRadar24KmlParser.cpp @@ -37,6 +37,8 @@ #include #include #include +#include +#include #include #include "Kml.h" #include "FlightRadar24KmlParser.h" @@ -89,7 +91,7 @@ std::vector FlightRadar24KmlParser::parse(QXmlStreamReader &xmlStrea { std::vector flights; FlightData flightData; - Aircraft &aircraft = flightData.addUserAircraft(); + auto &aircraft = flightData.addUserAircraft(); d->xml = &xmlStreamReader; d->trackData.clear(); @@ -109,13 +111,19 @@ std::vector FlightRadar24KmlParser::parse(QXmlStreamReader &xmlStrea flightData.creationTime = d->firstDateTimeUtc; // Now "upsert" the position data, taking duplicate timestamps into account - Position &position = aircraft.getPosition(); + auto &position = aircraft.getPosition(); + auto &attitude = aircraft.getAttitude(); for (const auto &trackItem : d->trackData) { - PositionData positionData {trackItem.latitude, trackItem.longitude, trackItem.altitude}; + // Positition + PositionData positionData {trackItem.latitude, trackItem.longitude, trackItem.altitude}; positionData.timestamp = trackItem.timestamp; - positionData.velocityBodyZ = trackItem.speed; - positionData.trueHeading = trackItem.heading; position.upsertLast(positionData); + + // Attitude + AttitudeData attitudeData {0, 0, static_cast(trackItem.heading)}; + attitudeData.velocityBodyZ = trackItem.speed; + attitudeData.timestamp = trackItem.timestamp; + attitude.upsertLast(attitudeData); } flights.push_back(std::move(flightData)); return flights; diff --git a/src/Plugins/Module/Formation/src/Formation.cpp b/src/Plugins/Module/Formation/src/Formation.cpp index bde2fb597..fa1a453ed 100644 --- a/src/Plugins/Module/Formation/src/Formation.cpp +++ b/src/Plugins/Module/Formation/src/Formation.cpp @@ -23,6 +23,7 @@ * DEALINGS IN THE SOFTWARE. */ #include +#include #include #include @@ -30,42 +31,34 @@ #include #include #include - +#include +#include +#include #include "Formation.h" -InitialPosition Formation::calculateInitialRelativePositionToUserAircraft(HorizontalDistance horizontalDistance, VerticalDistance verticalDistance, Bearing relativePosition, std::int64_t timestamp) noexcept +InitialPosition Formation::calculateInitialRelativePositionToUserAircraft(HorizontalDistance horizontalDistance, VerticalDistance verticalDistance, Bearing bearing, std::int64_t timestamp) noexcept { InitialPosition initialPosition; - const PositionData &relativePositionData = calculateRelativePositionToUserAircraft(horizontalDistance, verticalDistance, relativePosition, timestamp); - if (!relativePositionData.isNull()) { - initialPosition.fromPositionData(relativePositionData); - if (timestamp == 0) { - const auto &flight = Logbook::getInstance().getCurrentFlight(); - const Aircraft &userAircraft = flight.getUserAircraft(); - const AircraftInfo &aircraftInfo = userAircraft.getAircraftInfo(); - initialPosition.onGround = aircraftInfo.startOnGround; - } else { - initialPosition.onGround = false; - } + const auto relativePosition = calculateRelativePositionToUserAircraft(horizontalDistance, verticalDistance, bearing, timestamp); + if (!relativePosition.first.isNull()) { + initialPosition.fromPositionData(relativePosition.first, relativePosition.second); } return initialPosition; } -PositionData Formation::calculateRelativePositionToUserAircraft(HorizontalDistance horizontalDistance, VerticalDistance verticalDistance, Bearing relativePosition, std::int64_t timestamp) noexcept +std::pair Formation::calculateRelativePositionToUserAircraft(HorizontalDistance horizontalDistance, VerticalDistance verticalDistance, Bearing bearing, std::int64_t timestamp) noexcept { PositionData initialPosition; + AttitudeData attitudeData; const auto &flight = Logbook::getInstance().getCurrentFlight(); - const Aircraft &userAircraft = flight.getUserAircraft(); - Position &position = userAircraft.getPosition(); + const auto &aircraft = flight.getUserAircraft(); + Position &position = aircraft.getPosition(); if (position.count() > 0) { - const PositionData &positionData = timestamp == 0 ? position.getFirst() : position.interpolate(timestamp, TimeVariableData::Access::DiscreteSeek); - const AircraftInfo &aircraftInfo = userAircraft.getAircraftInfo(); - const AircraftType &aircraftType = aircraftInfo.aircraftType; - - // Copy pitch, bank, heading and velocity - initialPosition = positionData; + const auto &positionData = position.interpolate(timestamp, TimeVariableData::Access::DiscreteSeek); + const auto &aircraftInfo = aircraft.getAircraftInfo(); + const auto &aircraftType = aircraftInfo.aircraftType; // Horizontal distance [feet] double distance {0.0}; @@ -115,59 +108,60 @@ PositionData Formation::calculateRelativePositionToUserAircraft(HorizontalDistan const double altitude = positionData.altitude + deltaAltitude; // Degrees - double bearing {0.0}; - switch (relativePosition) { + double bearingDegrees {0.0}; + switch (bearing) { case Bearing::North: - bearing = 0.0; + bearingDegrees = 0.0; break; case Bearing::NorthNorthEast: - bearing = 22.5; + bearingDegrees = 22.5; break; case Bearing::NorthEast: - bearing = 45.0; + bearingDegrees = 45.0; break; case Bearing::EastNorthEast: - bearing = 67.5; + bearingDegrees = 67.5; break; case Bearing::East: - bearing = 90.0; + bearingDegrees = 90.0; break; case Bearing::EastSouthEast: - bearing = 112.5; + bearingDegrees = 112.5; break; case Bearing::SouthEast: - bearing = 135.0; + bearingDegrees = 135.0; break; case Bearing::SouthSouthEast: - bearing = 157.5; + bearingDegrees = 157.5; break; case Bearing::South: - bearing = 180.0; + bearingDegrees = 180.0; break; case Bearing::SouthSouthWest: - bearing = 202.5; + bearingDegrees = 202.5; break; case Bearing::SouthWest: - bearing = 225.0; + bearingDegrees = 225.0; break; case Bearing::WestSouthWest: - bearing = 247.5; + bearingDegrees = 247.5; break; case Bearing::West: - bearing = 270.0; + bearingDegrees = 270.0; break; case Bearing::WestNorthWest: - bearing = 292.5; + bearingDegrees = 292.5; break; case Bearing::NorthWest: - bearing = 315.0; + bearingDegrees = 315.0; break; case Bearing::NorthNorthWest: - bearing = 337.5; + bearingDegrees = 337.5; break; } - bearing += positionData.trueHeading; - SkyMath::Coordinate initial = SkyMath::relativePosition(sourcePosition, bearing, Convert::feetToMeters(distance)); + attitudeData = aircraft.getAttitude().interpolate(timestamp, TimeVariableData::Access::DiscreteSeek); + bearingDegrees += attitudeData.trueHeading; + SkyMath::Coordinate initial = SkyMath::relativePosition(sourcePosition, bearingDegrees, Convert::feetToMeters(distance)); initialPosition.latitude = initial.first; initialPosition.longitude = initial.second; @@ -175,5 +169,5 @@ PositionData Formation::calculateRelativePositionToUserAircraft(HorizontalDistan } // position count > 0 - return initialPosition; + return std::make_pair(initialPosition, attitudeData); } diff --git a/src/Plugins/Module/Formation/src/Formation.h b/src/Plugins/Module/Formation/src/Formation.h index cde5738b1..124813729 100644 --- a/src/Plugins/Module/Formation/src/Formation.h +++ b/src/Plugins/Module/Formation/src/Formation.h @@ -26,9 +26,11 @@ #define FORMATION_H #include +#include #include #include +#include namespace Formation { @@ -88,8 +90,8 @@ namespace Formation Last = NorthNorthWest }; - InitialPosition calculateInitialRelativePositionToUserAircraft(HorizontalDistance horizontalDistance, VerticalDistance verticalDistance, Bearing relativePosition, std::int64_t timestamp) noexcept; - PositionData calculateRelativePositionToUserAircraft(HorizontalDistance horizontalDistance, VerticalDistance verticalDistance, Bearing relativePosition, std::int64_t timestamp) noexcept; + InitialPosition calculateInitialRelativePositionToUserAircraft(HorizontalDistance horizontalDistance, VerticalDistance verticalDistance, Bearing bearing, std::int64_t timestamp) noexcept; + std::pair calculateRelativePositionToUserAircraft(HorizontalDistance horizontalDistance, VerticalDistance verticalDistance, Bearing bearing, std::int64_t timestamp) noexcept; }; #endif // FORMATION_H diff --git a/src/Plugins/Module/Formation/src/FormationWidget.cpp b/src/Plugins/Module/Formation/src/FormationWidget.cpp index 301f20726..bfe9d5429 100644 --- a/src/Plugins/Module/Formation/src/FormationWidget.cpp +++ b/src/Plugins/Module/Formation/src/FormationWidget.cpp @@ -57,6 +57,8 @@ #include #include #include +#include +#include #include #include #include @@ -487,8 +489,8 @@ void FormationWidget::updateTimeOffsetUi() noexcept ui->timeOffsetSpinBox->blockSignals(true); if (enabled) { const auto &flight = Logbook::getInstance().getCurrentFlight(); - const Aircraft &aircraft = flight[d->selectedAircraftIndex]; - const std::int64_t timeOffset = aircraft.getAircraftInfo().timeOffset; + const auto &aircraft = flight[d->selectedAircraftIndex]; + const auto timeOffset = aircraft.getAircraftInfo().timeOffset; const double timeOffsetSec = static_cast(timeOffset) / 1000.0; ui->timeOffsetSpinBox->setValue(timeOffsetSec); } else { @@ -531,9 +533,9 @@ void FormationWidget::updateToolTips() noexcept // Time offset if (d->selectedAircraftIndex != Const::InvalidId) { auto &flight = Logbook::getInstance().getCurrentFlight(); - Aircraft &aircraft = flight[d->selectedAircraftIndex]; + auto &aircraft = flight[d->selectedAircraftIndex]; - const std::int64_t timeOffset = aircraft.getTimeOffset(); + const auto timeOffset = aircraft.getTimeOffset(); if (timeOffset < 0) { ui->timeOffsetSpinBox->setToolTip(tr("The aircraft is %1 behind its recorded schedule.").arg(d->unit.formatElapsedTime(timeOffset))); } else if (timeOffset > 0) { @@ -707,10 +709,13 @@ void FormationWidget::updateAndSendUserAircraftPosition() const noexcept if (!skyConnectManager.isInRecordingState()) { auto &flight = Logbook::getInstance().getCurrentFlight(); // Also update the manually flown user aircraft position - const Aircraft &aircraft = flight.getUserAircraft(); - Position &position = aircraft.getPosition(); - const PositionData &positionData = position.interpolate(skyConnectManager.getCurrentTimestamp(), TimeVariableData::Access::DiscreteSeek); - skyConnectManager.setUserAircraftPosition(positionData); + const auto &aircraft = flight.getUserAircraft(); + const auto timestamp = skyConnectManager.getCurrentTimestamp(); + const auto &position = aircraft.getPosition(); + const auto &positionData = position.interpolate(timestamp, TimeVariableData::Access::DiscreteSeek); + const auto &attitude = aircraft.getAttitude(); + const auto &attitudeData = attitude.interpolate(timestamp, TimeVariableData::Access::DiscreteSeek); + skyConnectManager.setUserAircraftPositionAndAttitude(positionData, attitudeData); } break; } @@ -719,11 +724,13 @@ void FormationWidget::updateAndSendUserAircraftPosition() const noexcept const Formation::HorizontalDistance horizontalDistance {getHorizontalDistance()}; const Formation::VerticalDistance verticalDistance {getVerticalDistance()}; const Formation::Bearing relativePosition {getRelativePosition()}; - const PositionData positionData = Formation::calculateRelativePositionToUserAircraft(horizontalDistance, - verticalDistance, - relativePosition, - skyConnectManager.getCurrentTimestamp()); - skyConnectManager.setUserAircraftPosition(positionData); + const auto positionAndAtitude = Formation::calculateRelativePositionToUserAircraft( + horizontalDistance, + verticalDistance, + relativePosition, + skyConnectManager.getCurrentTimestamp() + ); + skyConnectManager.setUserAircraftPositionAndAttitude(positionAndAtitude.first, positionAndAtitude.second); } break; } @@ -739,21 +746,26 @@ void FormationWidget::updateUserAircraftPosition(SkyConnectIntf::ReplayMode repl case SkyConnectIntf::ReplayMode::UserAircraftManualControl: { auto &flight = Logbook::getInstance().getCurrentFlight(); - const Aircraft &aircraft = flight.getUserAircraft(); - Position &position = aircraft.getPosition(); - const PositionData &positionData = position.interpolate(skyConnectManager.getCurrentTimestamp(), TimeVariableData::Access::DiscreteSeek); - skyConnectManager.setUserAircraftPosition(positionData); + const auto &aircraft = flight.getUserAircraft(); + const auto timestamp = skyConnectManager.getCurrentTimestamp(); + const auto &position = aircraft.getPosition(); + const auto &positionData = position.interpolate(timestamp, TimeVariableData::Access::DiscreteSeek); + const auto &attitude = aircraft.getAttitude(); + const auto &attitudeData = attitude.interpolate(timestamp, TimeVariableData::Access::DiscreteSeek); + skyConnectManager.setUserAircraftPositionAndAttitude(positionData, attitudeData); break; } case SkyConnectIntf::ReplayMode::FlyWithFormation: const Formation::HorizontalDistance horizontalDistance {getHorizontalDistance()}; const Formation::VerticalDistance verticalDistance {getVerticalDistance()}; const Formation::Bearing relativePosition {getRelativePosition()}; - const PositionData positionData = Formation::calculateRelativePositionToUserAircraft(horizontalDistance, - verticalDistance, - relativePosition, - skyConnectManager.getCurrentTimestamp()); - skyConnectManager.setUserAircraftPosition(positionData); + const auto positionAndAtitude = Formation::calculateRelativePositionToUserAircraft( + horizontalDistance, + verticalDistance, + relativePosition, + skyConnectManager.getCurrentTimestamp() + ); + skyConnectManager.setUserAircraftPositionAndAttitude(positionAndAtitude.first, positionAndAtitude.second); break; } } @@ -867,7 +879,7 @@ void FormationWidget::onCellSelected(int row, [[maybe_unused]] int column) noexc void FormationWidget::onCellChanged(int row, int column) noexcept { auto &flight = Logbook::getInstance().getCurrentFlight(); - Aircraft &aircraft = flight[d->selectedAircraftIndex]; + auto &aircraft = flight[d->selectedAircraftIndex]; if (column == FormationWidgetPrivate::tailNumberColumn) { QTableWidgetItem *item = ui->aircraftTableWidget->item(row, column); const QString tailNumber = item->data(Qt::EditRole).toString(); @@ -989,7 +1001,7 @@ void FormationWidget::changeTimeOffset(const std::int64_t timeOffset) noexcept { if (d->selectedAircraftIndex != Const::InvalidIndex) { auto &flight = Logbook::getInstance().getCurrentFlight(); - Aircraft &aircraft = flight[d->selectedAircraftIndex]; + auto &aircraft = flight[d->selectedAircraftIndex]; const std::int64_t newTimeOffset = aircraft.getTimeOffset() + timeOffset; d->aircraftService->changeTimeOffset(aircraft, newTimeOffset); @@ -1001,7 +1013,7 @@ void FormationWidget::onTimeOffsetValueChanged() noexcept { if (d->selectedAircraftIndex != Const::InvalidIndex) { auto &flight = Logbook::getInstance().getCurrentFlight(); - Aircraft &aircraft = flight[d->selectedAircraftIndex]; + auto &aircraft = flight[d->selectedAircraftIndex]; const double timeOffsetSec = ui->timeOffsetSpinBox->value(); auto timeOffset = static_cast(std::round(timeOffsetSec * 1000.0)); diff --git a/src/UserInterface/src/MainWindow.cpp b/src/UserInterface/src/MainWindow.cpp index e33f19045..a62352a05 100644 --- a/src/UserInterface/src/MainWindow.cpp +++ b/src/UserInterface/src/MainWindow.cpp @@ -1298,7 +1298,7 @@ void MainWindow::updateUi() noexcept void MainWindow::updateControlUi() noexcept { - const Aircraft &aircraft = Logbook::getInstance().getCurrentFlight().getUserAircraft(); + const auto &aircraft = Logbook::getInstance().getCurrentFlight().getUserAircraft(); const bool hasRecording = aircraft.hasRecording(); const auto &skyConnectManager = SkyConnectManager::getInstance(); @@ -1527,7 +1527,7 @@ void MainWindow::updateReplayDuration() noexcept void MainWindow::updateFileMenu() noexcept { - const Aircraft &aircraft = Logbook::getInstance().getCurrentFlight().getUserAircraft(); + const auto &aircraft = Logbook::getInstance().getCurrentFlight().getUserAircraft(); const bool hasRecording = aircraft.hasRecording(); if (SkyConnectManager::getInstance().isInRecordingState()) { ui->newLogbookAction->setEnabled(false); diff --git a/src/UserInterface/src/Widget/AbstractSimulationVariableWidget.cpp b/src/UserInterface/src/Widget/AbstractSimulationVariableWidget.cpp index f4418245e..56d72e1e5 100644 --- a/src/UserInterface/src/Widget/AbstractSimulationVariableWidget.cpp +++ b/src/UserInterface/src/Widget/AbstractSimulationVariableWidget.cpp @@ -22,9 +22,6 @@ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ -#include -#include - #include #include @@ -47,13 +44,12 @@ void AbstractSimulationVariableWidget::showEvent(QShowEvent *event) noexcept { QWidget::showEvent(event); - auto &skyConnectManager = SkyConnectManager::getInstance(); + const auto &skyConnectManager = SkyConnectManager::getInstance(); connect(&skyConnectManager, &SkyConnectManager::timestampChanged, this, &AbstractSimulationVariableWidget::updateUi); - std::optional> skyConnect = skyConnectManager.getCurrentSkyConnect(); - if (skyConnect) { - updateUi(skyConnect->get().getCurrentTimestamp(), TimeVariableData::Access::DiscreteSeek); - } + + updateUi(skyConnectManager.getCurrentTimestamp(), TimeVariableData::Access::DiscreteSeek); + auto &flight = Logbook::getInstance().getCurrentFlight(); connect(&flight, &Flight::userAircraftChanged, this, &AbstractSimulationVariableWidget::updateUiWithCurrentTime); @@ -63,7 +59,7 @@ void AbstractSimulationVariableWidget::hideEvent(QHideEvent *event) noexcept { QWidget::hideEvent(event); - auto &skyConnectManager = SkyConnectManager::getInstance(); + const auto &skyConnectManager = SkyConnectManager::getInstance(); disconnect(&skyConnectManager, &SkyConnectManager::timestampChanged, this, &AbstractSimulationVariableWidget::updateUi); auto &flight = Logbook::getInstance().getCurrentFlight(); @@ -75,8 +71,6 @@ void AbstractSimulationVariableWidget::hideEvent(QHideEvent *event) noexcept void AbstractSimulationVariableWidget::updateUiWithCurrentTime() noexcept { - const std::optional> skyConnect = SkyConnectManager::getInstance().getCurrentSkyConnect(); - if (skyConnect) { - updateUi(skyConnect->get().getCurrentTimestamp(), TimeVariableData::Access::DiscreteSeek); - } + const auto &skyConnectManager = SkyConnectManager::getInstance(); + updateUi(skyConnectManager.getCurrentTimestamp(), TimeVariableData::Access::DiscreteSeek); } diff --git a/src/UserInterface/src/Widget/AircraftHandleWidget.cpp b/src/UserInterface/src/Widget/AircraftHandleWidget.cpp index 4b6dea3dd..54032cd93 100644 --- a/src/UserInterface/src/Widget/AircraftHandleWidget.cpp +++ b/src/UserInterface/src/Widget/AircraftHandleWidget.cpp @@ -90,23 +90,21 @@ void AircraftHandleWidget::initUi() noexcept AircraftHandleData AircraftHandleWidget::getCurrentAircraftHandleData(std::int64_t timestamp, TimeVariableData::Access access) const noexcept { - AircraftHandleData aircraftHandleData; - const Aircraft &aircraft = Logbook::getInstance().getCurrentFlight().getUserAircraft(); - const std::optional> skyConnect = SkyConnectManager::getInstance().getCurrentSkyConnect(); - if (skyConnect) { - if (skyConnect->get().getState() == Connect::State::Recording) { - if (aircraft.getAircraftHandle().count() > 0) { - aircraftHandleData = aircraft.getAircraftHandle().getLast(); - } - } else { - if (timestamp != TimeVariableData::InvalidTime) { - aircraftHandleData = aircraft.getAircraftHandle().interpolate(timestamp, access); - } else { - aircraftHandleData = aircraft.getAircraftHandle().interpolate(skyConnect->get().getCurrentTimestamp(), access); - } - }; - } - return aircraftHandleData; + AircraftHandleData data; + const auto &aircraft = Logbook::getInstance().getCurrentFlight().getUserAircraft(); + const auto &aircraftHandle = aircraft.getAircraftHandle(); + const auto &skyConnectManager = SkyConnectManager::getInstance(); + + if (skyConnectManager.getState() == Connect::State::Recording) { + if (aircraftHandle.count() > 0) { + data = aircraftHandle.getLast(); + } + } else { + const auto t = timestamp != TimeVariableData::InvalidTime ? timestamp : skyConnectManager.getCurrentTimestamp(); + data = aircraftHandle.interpolate(t, access); + }; + + return data; } // PRIVATE SLOTS diff --git a/src/UserInterface/src/Widget/AircraftInfoWidget.cpp b/src/UserInterface/src/Widget/AircraftInfoWidget.cpp index c3ecbc716..66e5be391 100644 --- a/src/UserInterface/src/Widget/AircraftInfoWidget.cpp +++ b/src/UserInterface/src/Widget/AircraftInfoWidget.cpp @@ -118,7 +118,7 @@ void AircraftInfoWidget::initUi() noexcept void AircraftInfoWidget::updateUi() noexcept { const auto &flight = Logbook::getInstance().getCurrentFlight(); - const Aircraft &aircraft = flight.getUserAircraft(); + const auto &aircraft = flight.getUserAircraft(); const AircraftInfo &aircraftInfo = aircraft.getAircraftInfo(); ui->nameLineEdit->setText(aircraftInfo.aircraftType.type); diff --git a/src/UserInterface/src/Widget/AircraftWidget.cpp b/src/UserInterface/src/Widget/AircraftWidget.cpp index 73afd30e2..bd4dcda1d 100644 --- a/src/UserInterface/src/Widget/AircraftWidget.cpp +++ b/src/UserInterface/src/Widget/AircraftWidget.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -38,6 +39,8 @@ #include #include #include +#include +#include #include #include #include @@ -73,47 +76,59 @@ AircraftWidget::~AircraftWidget() = default; void AircraftWidget::updateUi(std::int64_t timestamp, TimeVariableData::Access access) noexcept { - const PositionData &positionData = getCurrentPositionData(timestamp, access); - QString colorName; + const auto positionAndAttitude = getCurrentPositionData(timestamp, access); + QString positionColorName; + QString attitudeColorName; - if (!positionData.isNull()) { + const auto &position = positionAndAttitude.first; + const auto &attitude = positionAndAttitude.second; + if (!position.isNull()) { // Position - ui->latitudeLineEdit->setText(d->unit.formatCoordinate(positionData.latitude) % " (" % d->unit.formatLatitudeDMS(positionData.latitude) % ")"); - ui->longitudeLineEdit->setText(d->unit.formatCoordinate(positionData.longitude) % " (" % d->unit.formatLongitudeDMS(positionData.longitude) % ")"); - ui->altitudeLineEdit->setText(d->unit.formatFeet(positionData.altitude)); - ui->indicatedAltitudeLineEdit->setText(d->unit.formatFeet(positionData.indicatedAltitude)); - ui->pitchLineEdit->setText(d->unit.formatDegrees(positionData.pitch)); - ui->bankLineEdit->setText(d->unit.formatDegrees(positionData.bank)); - ui->headingLineEdit->setText(d->unit.formatDegrees(positionData.trueHeading)); + ui->latitudeLineEdit->setText(d->unit.formatCoordinate(position.latitude) % " (" % d->unit.formatLatitudeDMS(position.latitude) % ")"); + ui->longitudeLineEdit->setText(d->unit.formatCoordinate(position.longitude) % " (" % d->unit.formatLongitudeDMS(position.longitude) % ")"); + ui->altitudeLineEdit->setText(d->unit.formatFeet(position.altitude)); + ui->indicatedAltitudeLineEdit->setText(d->unit.formatFeet(position.indicatedAltitude)); + + positionColorName = d->ActiveTextColor.name(); + } else { + positionColorName = d->DisabledTextColor.name(); + } + + if (!attitude.isNull()) { + ui->pitchLineEdit->setText(d->unit.formatDegrees(attitude.pitch)); + ui->bankLineEdit->setText(d->unit.formatDegrees(attitude.bank)); + ui->headingLineEdit->setText(d->unit.formatDegrees(attitude.trueHeading)); // Velocity - double speedFeetPerSec = positionData.velocityBodyX; + double speedFeetPerSec = attitude.velocityBodyX; double speedKnots = Convert::feetPerSecondToKnots(speedFeetPerSec); ui->velocityXLineEdit->setText(d->unit.formatKnots(speedKnots) % " (" % d->unit.formatSpeedInFeetPerSecond(speedFeetPerSec) % ")"); - speedFeetPerSec = positionData.velocityBodyY; + speedFeetPerSec = attitude.velocityBodyY; speedKnots = Convert::feetPerSecondToKnots(speedFeetPerSec); ui->velocityYLineEdit->setText(d->unit.formatKnots(speedKnots) % " (" % d->unit.formatSpeedInFeetPerSecond(speedFeetPerSec) % ")"); - speedFeetPerSec = positionData.velocityBodyZ; + speedFeetPerSec = attitude.velocityBodyZ; speedKnots = Convert::feetPerSecondToKnots(speedFeetPerSec); ui->velocityZLineEdit->setText(d->unit.formatKnots(speedKnots) % " (" % d->unit.formatSpeedInFeetPerSecond(speedFeetPerSec) % ")"); - colorName = d->ActiveTextColor.name(); + attitudeColorName = d->ActiveTextColor.name(); } else { - colorName = d->DisabledTextColor.name(); + attitudeColorName = d->DisabledTextColor.name(); } - const QString css{QStringLiteral("color: %1;").arg(colorName)}; - ui->latitudeLineEdit->setStyleSheet(css); - ui->longitudeLineEdit->setStyleSheet(css); - ui->altitudeLineEdit->setStyleSheet(css); - ui->indicatedAltitudeLineEdit->setStyleSheet(css); - ui->pitchLineEdit->setStyleSheet(css); - ui->bankLineEdit->setStyleSheet(css); - ui->headingLineEdit->setStyleSheet(css); - ui->headingLineEdit->setStyleSheet(css); - ui->velocityXLineEdit->setStyleSheet(css); - ui->velocityYLineEdit->setStyleSheet(css); - ui->velocityZLineEdit->setStyleSheet(css); + const QString positionCss {QStringLiteral("color: %1;").arg(positionColorName)}; + ui->latitudeLineEdit->setStyleSheet(positionCss); + ui->longitudeLineEdit->setStyleSheet(positionCss); + ui->altitudeLineEdit->setStyleSheet(positionCss); + ui->indicatedAltitudeLineEdit->setStyleSheet(positionCss); + + const QString attitudeCss {QStringLiteral("color: %1;").arg(attitudeColorName)}; + ui->pitchLineEdit->setStyleSheet(attitudeCss); + ui->bankLineEdit->setStyleSheet(attitudeCss); + ui->headingLineEdit->setStyleSheet(attitudeCss); + ui->headingLineEdit->setStyleSheet(attitudeCss); + ui->velocityXLineEdit->setStyleSheet(attitudeCss); + ui->velocityYLineEdit->setStyleSheet(attitudeCss); + ui->velocityZLineEdit->setStyleSheet(attitudeCss); } // PRIVATE @@ -135,23 +150,34 @@ void AircraftWidget::initUi() noexcept ui->velocityZLineEdit->setToolTip(QString::fromLatin1(SimVar::VelocityBodyZ)); } -PositionData AircraftWidget::getCurrentPositionData(std::int64_t timestamp, TimeVariableData::Access access) const noexcept +std::pair AircraftWidget::getCurrentPositionData(std::int64_t timestamp, TimeVariableData::Access access) const noexcept { PositionData positionData; - const Aircraft &aircraft = Logbook::getInstance().getCurrentFlight().getUserAircraft(); - const std::optional> skyConnect = SkyConnectManager::getInstance().getCurrentSkyConnect(); - if (skyConnect) { - if (skyConnect->get().getState() == Connect::State::Recording) { - if (aircraft.getPosition().count() > 0) { - positionData = aircraft.getPosition().getLast(); - } - } else { - if (timestamp != TimeVariableData::InvalidTime) { - positionData = aircraft.getPosition().interpolate(timestamp, access); - } else { - positionData = aircraft.getPosition().interpolate(skyConnect->get().getCurrentTimestamp(), access); - } - }; - } - return positionData; + AttitudeData attitudeData; + const auto &aircraft = Logbook::getInstance().getCurrentFlight().getUserAircraft(); + const auto &position = aircraft.getPosition(); + const auto &attitude = aircraft.getAttitude(); + const auto &skyConnectManager = SkyConnectManager::getInstance(); + + // Position + if (skyConnectManager.getState() == Connect::State::Recording) { + if (position.count() > 0) { + positionData = position.getLast(); + } + } else { + const auto t = timestamp != TimeVariableData::InvalidTime ? timestamp : skyConnectManager.getCurrentTimestamp(); + positionData = position.interpolate(t, access); + }; + + // Attitude + if (skyConnectManager.getState() == Connect::State::Recording) { + if (attitude.count() > 0) { + attitudeData = attitude.getLast(); + } + } else { + const auto t = timestamp != TimeVariableData::InvalidTime ? timestamp : skyConnectManager.getCurrentTimestamp(); + attitudeData = attitude.interpolate(t, access); + }; + + return std::make_pair(positionData, attitudeData); } diff --git a/src/UserInterface/src/Widget/AircraftWidget.h b/src/UserInterface/src/Widget/AircraftWidget.h index ff8f8bc82..a98444f58 100644 --- a/src/UserInterface/src/Widget/AircraftWidget.h +++ b/src/UserInterface/src/Widget/AircraftWidget.h @@ -26,6 +26,7 @@ #define AIRCRAFTVARIABLESWIDGET_H #include +#include #include @@ -37,6 +38,7 @@ class QHideEvent; class SkyConnectIntf; struct PositionData; +struct AttitudeData; struct AircraftWidgetPrivate; namespace Ui { @@ -62,7 +64,7 @@ protected slots: const std::unique_ptr d; void initUi() noexcept; - PositionData getCurrentPositionData(std::int64_t timestamp, TimeVariableData::Access access) const noexcept; + std::pair getCurrentPositionData(std::int64_t timestamp, TimeVariableData::Access access) const noexcept; }; #endif // AIRCRAFTVARIABLESWIDGET_H diff --git a/src/UserInterface/src/Widget/EngineWidget.cpp b/src/UserInterface/src/Widget/EngineWidget.cpp index 384e924a4..c71cc9ff9 100644 --- a/src/UserInterface/src/Widget/EngineWidget.cpp +++ b/src/UserInterface/src/Widget/EngineWidget.cpp @@ -209,21 +209,19 @@ void EngineWidget::initUi() noexcept EngineData EngineWidget::getCurrentEngineData(std::int64_t timestamp, TimeVariableData::Access access) const noexcept { - EngineData engineData; - const Aircraft &aircraft = Logbook::getInstance().getCurrentFlight().getUserAircraft(); - const std::optional> skyConnect = SkyConnectManager::getInstance().getCurrentSkyConnect(); - if (skyConnect) { - if (skyConnect->get().getState() == Connect::State::Recording) { - if (aircraft.getEngine().count() > 0) { - engineData = aircraft.getEngine().getLast(); - } - } else { - if (timestamp != TimeVariableData::InvalidTime) { - engineData = aircraft.getEngine().interpolate(timestamp, access); - } else { - engineData = aircraft.getEngine().interpolate(skyConnect->get().getCurrentTimestamp(), access); - } - }; - } - return engineData; + EngineData data; + const auto &aircraft = Logbook::getInstance().getCurrentFlight().getUserAircraft(); + const auto &engine = aircraft.getEngine(); + const auto &skyConnectManager = SkyConnectManager::getInstance(); + + if (skyConnectManager.getState() == Connect::State::Recording) { + if (engine.count() > 0) { + data = engine.getLast(); + } + } else { + const auto t = timestamp != TimeVariableData::InvalidTime ? timestamp : skyConnectManager.getCurrentTimestamp(); + data = engine.interpolate(t, access); + }; + + return data; } diff --git a/src/UserInterface/src/Widget/LightWidget.cpp b/src/UserInterface/src/Widget/LightWidget.cpp index c1b109f8f..67099f660 100644 --- a/src/UserInterface/src/Widget/LightWidget.cpp +++ b/src/UserInterface/src/Widget/LightWidget.cpp @@ -111,23 +111,21 @@ void LightWidget::initUi() noexcept LightData LightWidget::getCurrentLightData(std::int64_t timestamp, TimeVariableData::Access access) const noexcept { - LightData lightData; - const Aircraft &aircraft = Logbook::getInstance().getCurrentFlight().getUserAircraft(); - const std::optional> skyConnect = SkyConnectManager::getInstance().getCurrentSkyConnect(); - if (skyConnect) { - if (skyConnect->get().getState() == Connect::State::Recording) { - if (aircraft.getLight().count() > 0) { - lightData = aircraft.getLight().getLast(); - } - } else { - if (timestamp != TimeVariableData::InvalidTime) { - lightData = aircraft.getLight().interpolate(timestamp, access); - } else { - lightData = aircraft.getLight().interpolate(skyConnect->get().getCurrentTimestamp(), access); - } - }; - } - return lightData; + LightData data; + const auto &aircraft = Logbook::getInstance().getCurrentFlight().getUserAircraft(); + const auto &light = aircraft.getLight(); + const auto &skyConnectManager = SkyConnectManager::getInstance(); + + if (skyConnectManager.getState() == Connect::State::Recording) { + if (light.count() > 0) { + data = light.getLast(); + } + } else { + const auto t = timestamp != TimeVariableData::InvalidTime ? timestamp : skyConnectManager.getCurrentTimestamp(); + data = light.interpolate(t, access); + }; + + return data; } // PRIVATE SLOTS diff --git a/src/UserInterface/src/Widget/PrimaryFlightControlWidget.cpp b/src/UserInterface/src/Widget/PrimaryFlightControlWidget.cpp index a4f65d0df..0443d8ddb 100644 --- a/src/UserInterface/src/Widget/PrimaryFlightControlWidget.cpp +++ b/src/UserInterface/src/Widget/PrimaryFlightControlWidget.cpp @@ -118,21 +118,19 @@ void PrimaryFlightControlWidget::initUi() PrimaryFlightControlData PrimaryFlightControlWidget::getCurrentPrimaryFlightControlData(std::int64_t timestamp, TimeVariableData::Access access) const noexcept { - PrimaryFlightControlData primaryFlightControlData; - const Aircraft &aircraft = Logbook::getInstance().getCurrentFlight().getUserAircraft(); - const std::optional> skyConnect = SkyConnectManager::getInstance().getCurrentSkyConnect(); - if (skyConnect) { - if (skyConnect->get().getState() == Connect::State::Recording) { - if (aircraft.getPrimaryFlightControl().count() > 0) { - primaryFlightControlData = aircraft.getPrimaryFlightControl().getLast(); - } - } else { - if (timestamp != TimeVariableData::InvalidTime) { - primaryFlightControlData = aircraft.getPrimaryFlightControl().interpolate(timestamp, access); - } else { - primaryFlightControlData = aircraft.getPrimaryFlightControl().interpolate(skyConnect->get().getCurrentTimestamp(), access); - } - }; - } - return primaryFlightControlData; + PrimaryFlightControlData data; + const auto &aircraft = Logbook::getInstance().getCurrentFlight().getUserAircraft(); + const auto &primaryFlightControl = aircraft.getPrimaryFlightControl(); + const auto &skyConnectManager = SkyConnectManager::getInstance(); + + if (skyConnectManager.getState() == Connect::State::Recording) { + if (primaryFlightControl.count() > 0) { + data = primaryFlightControl.getLast(); + } + } else { + const auto t = timestamp != TimeVariableData::InvalidTime ? timestamp : skyConnectManager.getCurrentTimestamp(); + data = primaryFlightControl.interpolate(t, access); + }; + + return data; } diff --git a/src/UserInterface/src/Widget/SecondaryFlightControlWidget.cpp b/src/UserInterface/src/Widget/SecondaryFlightControlWidget.cpp index e4112a258..5ea4db091 100644 --- a/src/UserInterface/src/Widget/SecondaryFlightControlWidget.cpp +++ b/src/UserInterface/src/Widget/SecondaryFlightControlWidget.cpp @@ -128,21 +128,19 @@ void SecondaryFlightControlWidget::initUi() noexcept SecondaryFlightControlData SecondaryFlightControlWidget::getCurrentSecondaryFlightControlData(std::int64_t timestamp, TimeVariableData::Access access) const noexcept { - SecondaryFlightControlData secondaryFlightControlData; - const Aircraft &aircraft = Logbook::getInstance().getCurrentFlight().getUserAircraft(); - const std::optional> skyConnect = SkyConnectManager::getInstance().getCurrentSkyConnect(); - if (skyConnect) { - if (skyConnect->get().getState() == Connect::State::Recording) { - if (aircraft.getSecondaryFlightControl().count() > 0) { - secondaryFlightControlData = aircraft.getSecondaryFlightControl().getLast(); - } - } else { - if (timestamp != TimeVariableData::InvalidTime) { - secondaryFlightControlData = aircraft.getSecondaryFlightControl().interpolate(timestamp, access); - } else { - secondaryFlightControlData = aircraft.getSecondaryFlightControl().interpolate(skyConnect->get().getCurrentTimestamp(), access); - } - }; - } - return secondaryFlightControlData; + SecondaryFlightControlData data; + const auto &aircraft = Logbook::getInstance().getCurrentFlight().getUserAircraft(); + const auto &secondaryFlightControl = aircraft.getSecondaryFlightControl(); + const auto &skyConnectManager = SkyConnectManager::getInstance(); + + if (skyConnectManager.getState() == Connect::State::Recording) { + if (secondaryFlightControl.count() > 0) { + data = secondaryFlightControl.getLast(); + } + } else { + const auto t = timestamp != TimeVariableData::InvalidTime ? timestamp : skyConnectManager.getCurrentTimestamp(); + data = secondaryFlightControl.interpolate(t, access); + }; + + return data; } diff --git a/test/KernelTest/src/SkyMathTest.cpp b/test/KernelTest/src/SkyMathTest.cpp index 8ca02dab9..8c9a8afc1 100644 --- a/test/KernelTest/src/SkyMathTest.cpp +++ b/test/KernelTest/src/SkyMathTest.cpp @@ -742,7 +742,7 @@ void SkyMathTest::calculateTimeOffset() QFETCH(std::int64_t, expectedTimeOffset); // Exercise - const std::int64_t timeOffset = SkyMath::calculateTimeOffset(timeOffsetSync, fromDateTime, toDateTime); + const auto timeOffset = SkyMath::calculateTimeOffset(timeOffsetSync, fromDateTime, toDateTime); // Verify QCOMPARE(timeOffset, expectedTimeOffset); diff --git a/test/PluginManagerTest/src/AbstractFlightImportTest.cpp b/test/PluginManagerTest/src/AbstractFlightImportTest.cpp index 16774df60..f8ce5ce5b 100644 --- a/test/PluginManagerTest/src/AbstractFlightImportTest.cpp +++ b/test/PluginManagerTest/src/AbstractFlightImportTest.cpp @@ -120,7 +120,7 @@ void AbstractFlightImportTest::importSelectedFlights() noexcept std::size_t nofAircraft = firstFlight.count(); QCOMPARE_EQ(static_cast(nofAircraft), expectedNofAircraftInFirstFlight); if (nofAircraft > 0) { - const Aircraft &aircraft = firstFlight.getUserAircraftConst(); + const auto &aircraft = firstFlight.getUserAircraftConst(); QCOMPARE_EQ(static_cast(aircraft.getPosition().count()), expectedNofUserAircraftPositionInFirstFlight); } } From 5716ae187250fc1a72b57e180c76e72e24e2c5a1 Mon Sep 17 00:00:00 2001 From: Oliver Knoll Date: Sat, 11 May 2024 17:49:21 +0200 Subject: [PATCH 2/9] Adjust MSFSSimConnectPlugin to separate position and attitude - Sample position only at 1 Hz (to smoothen the sample data) --- src/Model/include/Model/SimVar.h | 4 +- .../MSFSSimConnectPlugin/CMakeLists.txt | 8 +- .../src/MSFSSimConnectPlugin.cpp | 212 ++++++++++++------ .../src/MSFSSimConnectPlugin.h | 3 +- .../MSFSSimConnectPlugin/src/SimConnectAi.cpp | 9 +- .../SimConnectAircraftHandleInfo.h | 4 +- .../SimVar/Attitude/SimConnectAttitudeAll.h | 76 +++++++ .../Attitude/SimConnectAttitudeCommon.h | 113 ++++++++++ .../SimVar/Attitude/SimConnectAttitudeInfo.h | 80 +++++++ .../src/SimVar/Light/SimConnectLightEvent.h | 21 +- .../SimVar/Position/SimConnectPositionAll.h | 51 +---- .../Position/SimConnectPositionCommon.h | 33 --- .../SimConnectPositionAndAttitudeAi.h} | 40 +++- .../SimConnectPositionAndAttitudeAll.h | 124 ++++++++++ .../SimConnectPositionAndAttitudeUser.h} | 44 ++-- ...imConnectSecondaryFlightControlAnimation.h | 2 +- .../SimConnectSecondaryFlightControlEvent.h | 2 +- .../src/SimVar/SimConnectType.h | 9 +- .../src/SimVar/SimConnectVariables.cpp | 20 +- .../src/SimVar/SimConnectVariables.h | 1 - .../src/SimVar/SimulationVariables.h | 5 +- 21 files changed, 644 insertions(+), 217 deletions(-) create mode 100644 src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/Attitude/SimConnectAttitudeAll.h create mode 100644 src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/Attitude/SimConnectAttitudeCommon.h create mode 100644 src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/Attitude/SimConnectAttitudeInfo.h rename src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/{Position/SimConnectPositionAi.h => PositionAndAttitude/SimConnectPositionAndAttitudeAi.h} (59%) create mode 100644 src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/PositionAndAttitude/SimConnectPositionAndAttitudeAll.h rename src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/{Position/SimConnectPositionUser.h => PositionAndAttitude/SimConnectPositionAndAttitudeUser.h} (56%) diff --git a/src/Model/include/Model/SimVar.h b/src/Model/include/Model/SimVar.h index 106b5efd6..8054a04b5 100644 --- a/src/Model/include/Model/SimVar.h +++ b/src/Model/include/Model/SimVar.h @@ -35,13 +35,14 @@ namespace SimVar constexpr const char *Longitude = "Plane Longitude"; constexpr const char *Altitude = "Plane Altitude"; constexpr const char *IndicatedAltitude = "Indicated Altitude"; + constexpr const char *Pitch = "Plane Pitch Degrees"; constexpr const char *Bank = "Plane Bank Degrees"; constexpr const char *TrueHeading = "Plane Heading Degrees True"; - constexpr const char *VelocityBodyX = "Velocity Body X"; constexpr const char *VelocityBodyY = "Velocity Body Y"; constexpr const char *VelocityBodyZ = "Velocity Body Z"; + constexpr const char *SimOnGround = "Sim On Ground"; constexpr const char *AileronLeftDeflection = "Aileron Left Deflection"; constexpr const char *AileronRightDeflection = "Aileron Right Deflection"; @@ -120,7 +121,6 @@ namespace SimVar constexpr const char *ATCFlightNumber = "ATC Flight Number"; constexpr const char *Category = "Category"; - constexpr const char *SimOnGround = "Sim On Ground"; constexpr const char *PlaneAltAboveGround = "Plane Alt Above Ground"; constexpr const char *AirspeedTrue = "Airspeed True"; constexpr const char *AirspeedIndicated = "Airspeed Indicated"; diff --git a/src/Plugins/Connect/MSFSSimConnectPlugin/CMakeLists.txt b/src/Plugins/Connect/MSFSSimConnectPlugin/CMakeLists.txt index f621a6a47..1b73b7989 100644 --- a/src/Plugins/Connect/MSFSSimConnectPlugin/CMakeLists.txt +++ b/src/Plugins/Connect/MSFSSimConnectPlugin/CMakeLists.txt @@ -9,9 +9,13 @@ target_sources(${MODULE_NAME} PRIVATE src/SimVar/Position/SimConnectPositionCommon.h src/SimVar/Position/SimConnectPositionInfo.h - src/SimVar/Position/SimConnectPositionUser.h - src/SimVar/Position/SimConnectPositionAi.h src/SimVar/Position/SimConnectPositionAll.h + src/SimVar/Attitude/SimConnectAttitudeCommon.h + src/SimVar/Attitude/SimConnectAttitudeInfo.h + src/SimVar/Attitude/SimConnectAttitudeAll.h + src/SimVar/PositionAndAttitude/SimConnectPositionAndAttitudeUser.h + src/SimVar/PositionAndAttitude/SimConnectPositionAndAttitudeAi.h + src/SimVar/PositionAndAttitude/SimConnectPositionAndAttitudeAll.h src/SimVar/Engine/SimConnectEngineCommon.h src/SimVar/Engine/SimConnectEngineCore.h src/SimVar/Engine/SimConnectEngineEvent.h diff --git a/src/Plugins/Connect/MSFSSimConnectPlugin/src/MSFSSimConnectPlugin.cpp b/src/Plugins/Connect/MSFSSimConnectPlugin/src/MSFSSimConnectPlugin.cpp index e90a322b2..38794a926 100644 --- a/src/Plugins/Connect/MSFSSimConnectPlugin/src/MSFSSimConnectPlugin.cpp +++ b/src/Plugins/Connect/MSFSSimConnectPlugin/src/MSFSSimConnectPlugin.cpp @@ -49,6 +49,8 @@ #include #include #include +#include +#include #include #include #include @@ -87,6 +89,7 @@ struct SkyConnectPrivate MSFSSimConnectSettings pluginSettings; PositionData currentPositionData; + AttitudeData currentAttitudeData; EngineData currentEngineData; PrimaryFlightControlData currentPrimaryFlightControlData; SecondaryFlightControlData currentSecondaryFlightControlData; @@ -126,12 +129,14 @@ MSFSSimConnectPlugin::~MSFSSimConnectPlugin() noexcept closeConnection(); } -bool MSFSSimConnectPlugin::setUserAircraftPositionAndAttitude(const PositionData &positionData) noexcept +bool MSFSSimConnectPlugin::setUserAircraftPositionAndAttitude(const PositionData &positionData, const AttitudeData &attitudeData) noexcept { - SimConnectPositionUser simConnectPositionUser {positionData}; - const HRESULT result = ::SimConnect_SetDataOnSimObject(d->simConnectHandle, Enum::underly(SimConnectType::DataDefinition::PositionUser), - ::SIMCONNECT_OBJECT_ID_USER, ::SIMCONNECT_DATA_SET_FLAG_DEFAULT, 0, - sizeof(SimConnectPositionUser), &simConnectPositionUser); + SimConnectPositionAndAttitudeUser simConnectPositionAndAttitudeUser {positionData, attitudeData}; + const HRESULT result = ::SimConnect_SetDataOnSimObject( + d->simConnectHandle, Enum::underly(SimConnectType::DataDefinition::PositionAndAttitudeUser), + ::SIMCONNECT_OBJECT_ID_USER, ::SIMCONNECT_DATA_SET_FLAG_DEFAULT, 0, + sizeof(SimConnectPositionAndAttitudeUser), &simConnectPositionAndAttitudeUser + ); return result == S_OK; } @@ -165,10 +170,12 @@ bool MSFSSimConnectPlugin::onSetupFlightSimulatorShortcuts() noexcept bool MSFSSimConnectPlugin::onInitialPositionSetup(const InitialPosition &initialPosition) noexcept { - SIMCONNECT_DATA_INITPOSITION initialSimConnectPosition = SimConnectPositionAll::toInitialPosition(initialPosition); - HRESULT result = ::SimConnect_SetDataOnSimObject(d->simConnectHandle, Enum::underly(SimConnectType::DataDefinition::InitialPosition), - ::SIMCONNECT_OBJECT_ID_USER, ::SIMCONNECT_DATA_SET_FLAG_DEFAULT, 0, sizeof(::SIMCONNECT_DATA_INITPOSITION), - &initialSimConnectPosition); + SIMCONNECT_DATA_INITPOSITION initialSimConnectPosition = SimConnectPositionAndAttitudeAll::toInitialPosition(initialPosition); + HRESULT result = ::SimConnect_SetDataOnSimObject( + d->simConnectHandle, Enum::underly(SimConnectType::DataDefinition::InitialPosition), + ::SIMCONNECT_OBJECT_ID_USER, ::SIMCONNECT_DATA_SET_FLAG_DEFAULT, 0, + sizeof(::SIMCONNECT_DATA_INITPOSITION), &initialSimConnectPosition + ); return result == S_OK; } @@ -278,7 +285,7 @@ void MSFSSimConnectPlugin::onStopRecording() noexcept // Update flight plan Flight &flight = getCurrentFlight(); const auto &aircraft = flight.getUserAircraft(); - FlightPlan &flightPlan = userAircraft.getFlightPlan(); + FlightPlan &flightPlan = aircraft.getFlightPlan(); for (const auto &it : d->flightPlan) { flight.addWaypoint(it.second); } @@ -291,9 +298,9 @@ void MSFSSimConnectPlugin::onStopRecording() noexcept waypoint.zuluTime = d->currentZuluDateTime; waypoint.timestamp = getCurrentTimestamp(); flight.updateWaypoint(waypointCount - 1, waypoint); - } else if (waypointCount == 0 && userAircraft.getPosition().count() > 0) { + } else if (waypointCount == 0 && aircraft.getPosition().count() > 0) { Waypoint departureWaypoint; - const PositionData &firstPosition = userAircraft.getPosition().getFirst(); + const PositionData &firstPosition = aircraft.getPosition().getFirst(); departureWaypoint.identifier = Waypoint::CustomDepartureIdentifier; departureWaypoint.latitude = static_cast(firstPosition.latitude); departureWaypoint.longitude = static_cast(firstPosition.longitude); @@ -304,7 +311,7 @@ void MSFSSimConnectPlugin::onStopRecording() noexcept flight.addWaypoint(departureWaypoint); Waypoint arrivalWaypoint; - const PositionData &lastPosition = userAircraft.getPosition().getLast(); + const PositionData &lastPosition = aircraft.getPosition().getLast(); arrivalWaypoint.identifier = Waypoint::CustomArrivalIdentifier; arrivalWaypoint.latitude = static_cast(lastPosition.latitude); arrivalWaypoint.longitude = static_cast(lastPosition.longitude); @@ -402,19 +409,24 @@ bool MSFSSimConnectPlugin::sendAircraftData(std::int64_t currentTimestamp, TimeV ok = true; const auto &positionData = aircraft.getPosition().interpolate(currentTimestamp, access); + const auto &attitudeData = aircraft.getAttitude().interpolate(currentTimestamp, access); if (!positionData.isNull()) { - SimConnectPositionAll simConnnectPositionAll {positionData}; + SimConnectPositionAndAttitudeAll simConnnectPositionAndAttitudeAll {positionData, attitudeData}; if (isUserAircraft) { - SimConnectPositionUser positionUser {simConnnectPositionAll.user()}; - const HRESULT res = ::SimConnect_SetDataOnSimObject(d->simConnectHandle, Enum::underly(SimConnectType::DataDefinition::PositionUser), - objectId, ::SIMCONNECT_DATA_SET_FLAG_DEFAULT, 0, - sizeof(SimConnectPositionUser), &positionUser); + SimConnectPositionAndAttitudeUser simConnectPositionAndAttitudeUser {simConnnectPositionAndAttitudeAll.user()}; + const HRESULT res = ::SimConnect_SetDataOnSimObject( + d->simConnectHandle, Enum::underly(SimConnectType::DataDefinition::PositionAndAttitudeUser), + objectId, ::SIMCONNECT_DATA_SET_FLAG_DEFAULT, 0, + sizeof(SimConnectPositionAndAttitudeUser), &simConnectPositionAndAttitudeUser + ); ok = res == S_OK; } else { - SimConnectPositionAi positionAi {simConnnectPositionAll.ai()}; - const HRESULT res = ::SimConnect_SetDataOnSimObject(d->simConnectHandle, Enum::underly(SimConnectType::DataDefinition::PositionAi), - objectId, ::SIMCONNECT_DATA_SET_FLAG_DEFAULT, 0, - sizeof(SimConnectPositionAi), &positionAi); + SimConnectPositionAndAttitudeAi simConnectPositionAndAttitudeAi {simConnnectPositionAndAttitudeAll.ai()}; + const HRESULT res = ::SimConnect_SetDataOnSimObject( + d->simConnectHandle, Enum::underly(SimConnectType::DataDefinition::PositionAndAttitudeAi), + objectId, ::SIMCONNECT_DATA_SET_FLAG_DEFAULT, 0, + sizeof(SimConnectPositionAndAttitudeAi), &simConnectPositionAndAttitudeAi + ); ok = res == S_OK; } } @@ -426,18 +438,22 @@ bool MSFSSimConnectPlugin::sendAircraftData(std::int64_t currentTimestamp, TimeV SimConnectEngineAll simConnectEngineAll {engineData}; if (isUserAircraft) { SimConnectEngineUser engineUser {simConnectEngineAll.user()}; - const HRESULT res = ::SimConnect_SetDataOnSimObject(d->simConnectHandle, Enum::underly(SimConnectType::DataDefinition::EngineUser), - objectId, ::SIMCONNECT_DATA_SET_FLAG_DEFAULT, 0, - sizeof(SimConnectEngineUser), &engineUser); + const HRESULT res = ::SimConnect_SetDataOnSimObject( + d->simConnectHandle, Enum::underly(SimConnectType::DataDefinition::EngineUser), + objectId, ::SIMCONNECT_DATA_SET_FLAG_DEFAULT, 0, + sizeof(SimConnectEngineUser), &engineUser + ); ok = res == S_OK; if (ok) { ok = d->eventStateHandler->sendEngine(simConnectEngineAll, access); } } else { SimConnectEngineAi engineAi {simConnectEngineAll.ai()}; - const HRESULT res = ::SimConnect_SetDataOnSimObject(d->simConnectHandle, Enum::underly(SimConnectType::DataDefinition::EngineAi), - objectId, ::SIMCONNECT_DATA_SET_FLAG_DEFAULT, 0, - sizeof(SimConnectEngineAi), &engineAi); + const HRESULT res = ::SimConnect_SetDataOnSimObject( + d->simConnectHandle, Enum::underly(SimConnectType::DataDefinition::EngineAi), + objectId, ::SIMCONNECT_DATA_SET_FLAG_DEFAULT, 0, + sizeof(SimConnectEngineAi), &engineAi + ); ok = res == S_OK; } } @@ -452,9 +468,11 @@ bool MSFSSimConnectPlugin::sendAircraftData(std::int64_t currentTimestamp, TimeV ok = d->eventStateHandler->sendPrimaryFlightControl(simConnectPrimaryFlightControlAll.event); } else { SimConnectPrimaryFlightControlAi simConnectPrimaryFlightControlAi {simConnectPrimaryFlightControlAll.ai()}; - HRESULT res = ::SimConnect_SetDataOnSimObject(d->simConnectHandle, Enum::underly(SimConnectType::DataDefinition::PrimaryFlightControlAi), - objectId, ::SIMCONNECT_DATA_SET_FLAG_DEFAULT, 0, - sizeof(SimConnectPrimaryFlightControlAi), &simConnectPrimaryFlightControlAi); + HRESULT res = ::SimConnect_SetDataOnSimObject( + d->simConnectHandle, Enum::underly(SimConnectType::DataDefinition::PrimaryFlightControlAi), + objectId, ::SIMCONNECT_DATA_SET_FLAG_DEFAULT, 0, + sizeof(SimConnectPrimaryFlightControlAi), &simConnectPrimaryFlightControlAi + ); ok = res == S_OK; } } @@ -469,9 +487,11 @@ bool MSFSSimConnectPlugin::sendAircraftData(std::int64_t currentTimestamp, TimeV ok = d->eventStateHandler->sendSecondaryFlightControl(simConnectSecondaryFlightControlAll.event, access); } else { SimConnectSecondaryFlightControlAi simConnectSecondaryFlightControlAi {simConnectSecondaryFlightControlAll.ai()}; - const HRESULT res = ::SimConnect_SetDataOnSimObject(d->simConnectHandle, Enum::underly(SimConnectType::DataDefinition::SecondaryFlightControlAi), - objectId, ::SIMCONNECT_DATA_SET_FLAG_DEFAULT, 0, - sizeof(SimConnectSecondaryFlightControlAi), &simConnectSecondaryFlightControlAi); + const HRESULT res = ::SimConnect_SetDataOnSimObject( + d->simConnectHandle, Enum::underly(SimConnectType::DataDefinition::SecondaryFlightControlAi), + objectId, ::SIMCONNECT_DATA_SET_FLAG_DEFAULT, 0, + sizeof(SimConnectSecondaryFlightControlAi), &simConnectSecondaryFlightControlAi + ); ok = res == S_OK; } } @@ -484,9 +504,11 @@ bool MSFSSimConnectPlugin::sendAircraftData(std::int64_t currentTimestamp, TimeV SimConnectAircraftHandleAll simConnectAircraftHandleAll {aircraftHandleData}; if (isUserAircraft) { SimConnectAircraftHandleUser simConnectAircraftHandleUser {simConnectAircraftHandleAll.user()}; - const HRESULT res = ::SimConnect_SetDataOnSimObject(d->simConnectHandle, Enum::underly(SimConnectType::DataDefinition::AircraftHandleUser), - objectId, ::SIMCONNECT_DATA_SET_FLAG_DEFAULT, 0, - sizeof(SimConnectAircraftHandleUser), &simConnectAircraftHandleUser); + const HRESULT res = ::SimConnect_SetDataOnSimObject( + d->simConnectHandle, Enum::underly(SimConnectType::DataDefinition::AircraftHandleUser), + objectId, ::SIMCONNECT_DATA_SET_FLAG_DEFAULT, 0, + sizeof(SimConnectAircraftHandleUser), &simConnectAircraftHandleUser + ); ok = res == S_OK; if (ok) { ok = d->eventStateHandler->sendAircraftHandle(simConnectAircraftHandleAll); @@ -494,9 +516,11 @@ bool MSFSSimConnectPlugin::sendAircraftData(std::int64_t currentTimestamp, TimeV } else { SimConnectAircraftHandleAi simConnectAircraftHandleAi {simConnectAircraftHandleAll.ai()}; - const HRESULT res = ::SimConnect_SetDataOnSimObject(d->simConnectHandle, Enum::underly(SimConnectType::DataDefinition::AircraftHandleAi), - objectId, ::SIMCONNECT_DATA_SET_FLAG_DEFAULT, 0, - sizeof(SimConnectAircraftHandleAi), &simConnectAircraftHandleAi); + const HRESULT res = ::SimConnect_SetDataOnSimObject( + d->simConnectHandle, Enum::underly(SimConnectType::DataDefinition::AircraftHandleAi), + objectId, ::SIMCONNECT_DATA_SET_FLAG_DEFAULT, 0, + sizeof(SimConnectAircraftHandleAi), &simConnectAircraftHandleAi + ); ok = res == S_OK; } @@ -512,9 +536,11 @@ bool MSFSSimConnectPlugin::sendAircraftData(std::int64_t currentTimestamp, TimeV ok = d->eventStateHandler->sendLight(simConnectLightAll.event); } else { SimConnectLightAi simConnectLightAi {simConnectLightAll.ai()}; - const HRESULT res = ::SimConnect_SetDataOnSimObject(d->simConnectHandle, Enum::underly(SimConnectType::DataDefinition::LightAi), - objectId, ::SIMCONNECT_DATA_SET_FLAG_DEFAULT, 0, - sizeof(SimConnectLightAi), &simConnectLightAi); + const HRESULT res = ::SimConnect_SetDataOnSimObject( + d->simConnectHandle, Enum::underly(SimConnectType::DataDefinition::LightAi), + objectId, ::SIMCONNECT_DATA_SET_FLAG_DEFAULT, 0, + sizeof(SimConnectLightAi), &simConnectLightAi + ); ok = res == S_OK; } } @@ -611,6 +637,11 @@ void MSFSSimConnectPlugin::recordData() noexcept // Processed dataStored = true; } + if (!d->currentAttitudeData.isNull()) { + userAircraft.getAttitude().upsertLast(d->currentAttitudeData); + // Processed + dataStored = true; + } if (!d->currentEngineData.isNull()) { userAircraft.getEngine().upsertLast(d->currentEngineData); // Processed @@ -658,6 +689,7 @@ void MSFSSimConnectPlugin::frenchConnection() noexcept void MSFSSimConnectPlugin::resetCurrentSampleData() noexcept { d->currentPositionData.reset(); + d->currentAttitudeData.reset(); d->currentEngineData.reset(); d->currentPrimaryFlightControlData.reset(); d->currentSecondaryFlightControlData.reset(); @@ -694,9 +726,10 @@ void MSFSSimConnectPlugin::setupRequestData() noexcept // Request data SimConnectFlightInfo::addToDataDefinition(d->simConnectHandle); SimConnectAircraftInfo::addToDataDefinition(d->simConnectHandle); - SimConnectPositionUser::addToDataDefinition(d->simConnectHandle); - SimConnectPositionAi::addToDataDefinition(d->simConnectHandle); + SimConnectPositionAndAttitudeUser::addToDataDefinition(d->simConnectHandle); + SimConnectPositionAndAttitudeAi::addToDataDefinition(d->simConnectHandle); SimConnectPositionAll::addToDataDefinition(d->simConnectHandle); + SimConnectAttitudeAll::addToDataDefinition(d->simConnectHandle); SimConnectEngineUser::addToDataDefinition(d->simConnectHandle); SimConnectEngineAi::addToDataDefinition(d->simConnectHandle); SimConnectEngineAll::addToDataDefinition(d->simConnectHandle); @@ -768,32 +801,54 @@ void MSFSSimConnectPlugin::updateRecordingFrequency(SampleRate::SampleRate sampl void MSFSSimConnectPlugin::updateRequestPeriod(::SIMCONNECT_PERIOD period) noexcept { if (d->currentRequestPeriod != period) { - ::SimConnect_RequestDataOnSimObject(d->simConnectHandle, Enum::underly(SimConnectType::DataRequest::AircraftPositionAll), - Enum::underly(SimConnectType::DataDefinition::PositionAll), - ::SIMCONNECT_OBJECT_ID_USER, period, ::SIMCONNECT_DATA_REQUEST_FLAG_CHANGED); - ::SimConnect_RequestDataOnSimObject(d->simConnectHandle, Enum::underly(SimConnectType::DataRequest::EngineAll), - Enum::underly(SimConnectType::DataDefinition::EngineAll), - ::SIMCONNECT_OBJECT_ID_USER, period, ::SIMCONNECT_DATA_REQUEST_FLAG_CHANGED); - ::SimConnect_RequestDataOnSimObject(d->simConnectHandle, Enum::underly(SimConnectType::DataRequest::PrimaryFlightControlAll), - Enum::underly(SimConnectType::DataDefinition::PrimaryFlightControlAll), - ::SIMCONNECT_OBJECT_ID_USER, period, ::SIMCONNECT_DATA_REQUEST_FLAG_CHANGED); - ::SimConnect_RequestDataOnSimObject(d->simConnectHandle, Enum::underly(SimConnectType::DataRequest::SecondaryFlightControlAll), - Enum::underly(SimConnectType::DataDefinition::SecondaryFlightControlAll), - ::SIMCONNECT_OBJECT_ID_USER, period, ::SIMCONNECT_DATA_REQUEST_FLAG_CHANGED); - ::SimConnect_RequestDataOnSimObject(d->simConnectHandle, Enum::underly(SimConnectType::DataRequest::AircraftHandleAll), - Enum::underly(SimConnectType::DataDefinition::AircraftHandleAll), - ::SIMCONNECT_OBJECT_ID_USER, period, ::SIMCONNECT_DATA_REQUEST_FLAG_CHANGED); - ::SimConnect_RequestDataOnSimObject(d->simConnectHandle, Enum::underly(SimConnectType::DataRequest::LightAll), - Enum::underly(SimConnectType::DataDefinition::LightAll), - ::SIMCONNECT_OBJECT_ID_USER, period, ::SIMCONNECT_DATA_REQUEST_FLAG_CHANGED); - // Update the flight plan and simulation time only every second + // We sample the position data only at 1 Hz, in order to "smoothen" the curve + // The flight plan and simulation time is also updated only every second ::SIMCONNECT_PERIOD oneSecondPeriod = period != ::SIMCONNECT_PERIOD_NEVER ? ::SIMCONNECT_PERIOD_SECOND : ::SIMCONNECT_PERIOD_NEVER; - ::SimConnect_RequestDataOnSimObject(d->simConnectHandle, Enum::underly(SimConnectType::DataRequest::FlightPlan), - Enum::underly(SimConnectType::DataDefinition::FlightPlan), - ::SIMCONNECT_OBJECT_ID_USER, oneSecondPeriod, ::SIMCONNECT_DATA_REQUEST_FLAG_CHANGED); - ::SimConnect_RequestDataOnSimObject(d->simConnectHandle, Enum::underly(SimConnectType::DataRequest::SimulationTime), - Enum::underly(SimConnectType::DataDefinition::SimulationTime), - ::SIMCONNECT_OBJECT_ID_USER, oneSecondPeriod, ::SIMCONNECT_DATA_REQUEST_FLAG_CHANGED); + ::SimConnect_RequestDataOnSimObject( + d->simConnectHandle, Enum::underly(SimConnectType::DataRequest::PositionAll), + Enum::underly(SimConnectType::DataDefinition::PositionAll), + ::SIMCONNECT_OBJECT_ID_USER, oneSecondPeriod, ::SIMCONNECT_DATA_REQUEST_FLAG_CHANGED + ); + ::SimConnect_RequestDataOnSimObject( + d->simConnectHandle, Enum::underly(SimConnectType::DataRequest::AttitudeAll), + Enum::underly(SimConnectType::DataDefinition::AttitudeAll), + ::SIMCONNECT_OBJECT_ID_USER, period, ::SIMCONNECT_DATA_REQUEST_FLAG_CHANGED + ); + ::SimConnect_RequestDataOnSimObject( + d->simConnectHandle, Enum::underly(SimConnectType::DataRequest::EngineAll), + Enum::underly(SimConnectType::DataDefinition::EngineAll), + ::SIMCONNECT_OBJECT_ID_USER, period, ::SIMCONNECT_DATA_REQUEST_FLAG_CHANGED + ); + ::SimConnect_RequestDataOnSimObject( + d->simConnectHandle, Enum::underly(SimConnectType::DataRequest::PrimaryFlightControlAll), + Enum::underly(SimConnectType::DataDefinition::PrimaryFlightControlAll), + ::SIMCONNECT_OBJECT_ID_USER, period, ::SIMCONNECT_DATA_REQUEST_FLAG_CHANGED + ); + ::SimConnect_RequestDataOnSimObject( + d->simConnectHandle, Enum::underly(SimConnectType::DataRequest::SecondaryFlightControlAll), + Enum::underly(SimConnectType::DataDefinition::SecondaryFlightControlAll), + ::SIMCONNECT_OBJECT_ID_USER, period, ::SIMCONNECT_DATA_REQUEST_FLAG_CHANGED + ); + ::SimConnect_RequestDataOnSimObject( + d->simConnectHandle, Enum::underly(SimConnectType::DataRequest::AircraftHandleAll), + Enum::underly(SimConnectType::DataDefinition::AircraftHandleAll), + ::SIMCONNECT_OBJECT_ID_USER, period, ::SIMCONNECT_DATA_REQUEST_FLAG_CHANGED + ); + ::SimConnect_RequestDataOnSimObject( + d->simConnectHandle, Enum::underly(SimConnectType::DataRequest::LightAll), + Enum::underly(SimConnectType::DataDefinition::LightAll), + ::SIMCONNECT_OBJECT_ID_USER, period, ::SIMCONNECT_DATA_REQUEST_FLAG_CHANGED + ); + ::SimConnect_RequestDataOnSimObject( + d->simConnectHandle, Enum::underly(SimConnectType::DataRequest::FlightPlan), + Enum::underly(SimConnectType::DataDefinition::FlightPlan), + ::SIMCONNECT_OBJECT_ID_USER, oneSecondPeriod, ::SIMCONNECT_DATA_REQUEST_FLAG_CHANGED + ); + ::SimConnect_RequestDataOnSimObject( + d->simConnectHandle, Enum::underly(SimConnectType::DataRequest::SimulationTime), + Enum::underly(SimConnectType::DataDefinition::SimulationTime), + ::SIMCONNECT_OBJECT_ID_USER, oneSecondPeriod, ::SIMCONNECT_DATA_REQUEST_FLAG_CHANGED + ); d->currentRequestPeriod = period; } } @@ -982,7 +1037,7 @@ void CALLBACK MSFSSimConnectPlugin::dispatch(::SIMCONNECT_RECV *receivedData, [[ const bool storeDataImmediately = skyConnect->d->storeDataImmediately; switch (static_cast(objectData->dwRequestID)) { - case SimConnectType::DataRequest::AircraftPositionAll: + case SimConnectType::DataRequest::PositionAll: { if (skyConnect->getState() == Connect::State::Recording) { auto simConnectPositionAll = reinterpret_cast(&objectData->dwData); @@ -997,6 +1052,21 @@ void CALLBACK MSFSSimConnectPlugin::dispatch(::SIMCONNECT_RECV *receivedData, [[ } break; } + case SimConnectType::DataRequest::AttitudeAll: + { + if (skyConnect->getState() == Connect::State::Recording) { + auto simConnectAttitudeAll = reinterpret_cast(&objectData->dwData); + AttitudeData attitudeData = simConnectAttitudeAll->toAttitudeData(); + attitudeData.timestamp = skyConnect->getCurrentTimestamp(); + if (storeDataImmediately) { + userAircraft.getAttitude().upsertLast(attitudeData); + dataStored = true; + } else { + skyConnect->d->currentAttitudeData = std::move(attitudeData); + } + } + break; + } case SimConnectType::DataRequest::EngineAll: { if (skyConnect->getState() == Connect::State::Recording) { diff --git a/src/Plugins/Connect/MSFSSimConnectPlugin/src/MSFSSimConnectPlugin.h b/src/Plugins/Connect/MSFSSimConnectPlugin/src/MSFSSimConnectPlugin.h index 8207316f3..e81c39272 100644 --- a/src/Plugins/Connect/MSFSSimConnectPlugin/src/MSFSSimConnectPlugin.h +++ b/src/Plugins/Connect/MSFSSimConnectPlugin/src/MSFSSimConnectPlugin.h @@ -41,6 +41,7 @@ #include struct PositionData; +struct AttitudeData; struct EngineData; class Aircraft; class MSFSSimConnectSettings; @@ -60,7 +61,7 @@ class MSFSSimConnectPlugin : public AbstractSkyConnect MSFSSimConnectPlugin &operator=(MSFSSimConnectPlugin &&rhs) = delete; ~MSFSSimConnectPlugin() noexcept override; - bool setUserAircraftPositionAndAttitude(const PositionData &positionData) noexcept override; + bool setUserAircraftPositionAndAttitude(const PositionData &positionData, const AttitudeData &attitudeData) noexcept override; protected: ConnectPluginBaseSettings &getPluginSettings() const noexcept override; diff --git a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimConnectAi.cpp b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimConnectAi.cpp index 4148379b4..0d3e12d2b 100644 --- a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimConnectAi.cpp +++ b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimConnectAi.cpp @@ -37,8 +37,11 @@ #include #include #include +#include +#include #include "SimVar/SimConnectType.h" #include "SimVar/SimulationVariables.h" +#include "SimVar/PositionAndAttitude/SimConnectPositionAndAttitudeAll.h" #include "SimConnectAi.h" using RequestByAircraftId = std::unordered_map; @@ -73,9 +76,11 @@ void SimConnectAi::addObject(const Aircraft &aircraft, std::int64_t timestamp) n // (otherwise it is the new user aircraft being added for a new recording) if (aircraft.getId() != Const::InvalidId) { const AircraftInfo &aircraftInfo = aircraft.getAircraftInfo(); - Position &position = aircraft.getPosition(); + const auto &position = aircraft.getPosition(); + const auto &attitude = aircraft.getAttitude(); const auto &positionData = position.interpolate(timestamp, TimeVariableData::Access::DiscreteSeek); - const ::SIMCONNECT_DATA_INITPOSITION initialPosition = SimConnectPositionAll::toInitialPosition(positioNData, aircraftInfo.startOnGround, aircraftInfo.initialAirspeed); + const auto &attitudeData = attitude.interpolate(timestamp, TimeVariableData::Access::DiscreteSeek); + const ::SIMCONNECT_DATA_INITPOSITION initialPosition = SimConnectPositionAndAttitudeAll::toInitialPosition(positionData, attitudeData, aircraftInfo.initialAirspeed); const ::SIMCONNECT_DATA_REQUEST_ID requestId = Enum::underly(SimConnectType::DataRequest::AiObjectBase) + d->lastAiCreateRequestId; HRESULT result = ::SimConnect_AICreateNonATCAircraft(d->simConnectHandle, aircraftInfo.aircraftType.type.toLocal8Bit(), aircraftInfo.tailNumber.toLocal8Bit(), initialPosition, requestId); diff --git a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/AircraftHandle/SimConnectAircraftHandleInfo.h b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/AircraftHandle/SimConnectAircraftHandleInfo.h index a4dc2a739..d27de8d86 100644 --- a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/AircraftHandle/SimConnectAircraftHandleInfo.h +++ b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/AircraftHandle/SimConnectAircraftHandleInfo.h @@ -73,8 +73,8 @@ struct SimConnectAircraftHandleInfo static void addToDataDefinition(HANDLE simConnectHandle, ::SIMCONNECT_DATA_DEFINITION_ID dataDefinitionId) noexcept { - ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::TailhookHandle, "Boolean", ::SIMCONNECT_DATATYPE_INT32); - ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::FoldingWingHandlePosition, "Boolean", ::SIMCONNECT_DATATYPE_INT32); + ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::TailhookHandle, "Bool", ::SIMCONNECT_DATATYPE_INT32); + ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::FoldingWingHandlePosition, "Bool", ::SIMCONNECT_DATATYPE_INT32); } }; #pragma pack(pop) diff --git a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/Attitude/SimConnectAttitudeAll.h b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/Attitude/SimConnectAttitudeAll.h new file mode 100644 index 000000000..3ba66777d --- /dev/null +++ b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/Attitude/SimConnectAttitudeAll.h @@ -0,0 +1,76 @@ +/** + * Sky Dolly - The Black Sheep for your Flight Recordings + * + * Copyright (c) Oliver Knoll + * All rights reserved. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this + * software and associated documentation files (the "Software"), to deal in the Software + * without restriction, including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE + * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef SIMCONNECTATTITUDEALL_H +#define SIMCONNECTATTITUDEALL_H + +#include +#include + +#include +#include "SimConnectAttitudeCommon.h" +#include "SimConnectAttitudeInfo.h" + +/*! + * All aircraft position simulation variables (reply from the flight simulator). + * + * Implementation note: this struct needs to be packed. + */ +#pragma pack(push, 1) +struct SimConnectAttitudeAll +{ + SimConnectAttitudeCommon common; + SimConnectAttitudeInfo info; + + SimConnectAttitudeAll(const AttitudeData &positionData) noexcept + : SimConnectAttitudeAll() + { + fromAttitudeData(positionData); + } + + SimConnectAttitudeAll() = default; + + inline void fromAttitudeData(const AttitudeData &data) noexcept + { + common.fromAttitudeData(data); + info.fromAttitudeData(data); + } + + inline AttitudeData toAttitudeData() const noexcept + { + auto positionData = common.toAttitudeData(); + info.toAttitudeData(positionData); + return positionData; + } + + static void addToDataDefinition(HANDLE simConnectHandle) noexcept + { + SimConnectAttitudeCommon::addToDataDefinition(simConnectHandle, Enum::underly(SimConnectType::DataDefinition::AttitudeAll)); + SimConnectAttitudeInfo::addToDataDefinition(simConnectHandle, Enum::underly(SimConnectType::DataDefinition::AttitudeAll)); + } + +}; +#pragma pack(pop) + +#endif // SIMCONNECTATTITUDEALL_H diff --git a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/Attitude/SimConnectAttitudeCommon.h b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/Attitude/SimConnectAttitudeCommon.h new file mode 100644 index 000000000..216029f8a --- /dev/null +++ b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/Attitude/SimConnectAttitudeCommon.h @@ -0,0 +1,113 @@ +/** + * Sky Dolly - The Black Sheep for your Flight Recordings + * + * Copyright (c) Oliver Knoll + * All rights reserved. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this + * software and associated documentation files (the "Software"), to deal in the Software + * without restriction, including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE + * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef SIMCONNECTATTITUDECOMMON_H +#define SIMCONNECTATTITUDECOMMON_H + +#include +#include + +#include +#include +#include +#include +#include +#include "SimConnectType.h" + +/*! + * Common aircraft attitude simulation variables that are sent both to the user- and AI aircraft. + * + * Implementation note: this struct needs to be packed. + */ +#pragma pack(push, 1) +struct SimConnectAttitudeCommon +{ + // Attitude + double pitch {0.0}; + double bank {0.0}; + double trueHeading {0.0}; + + // Velocity + double velocityBodyX {0.0}; + double velocityBodyY {0.0}; + double velocityBodyZ {0.0}; + + // Implementation note: + // If we would store the "rotation velocity body" (which we currently do not anymore) then + // then the unit would be (wrongly) "FEET per second" (and not "RADIANS per second): + // https://docs.flightsimulator.com/html/Programming_Tools/SimVars/Aircraft_SimVars/Aircraft_Misc_Variables.htm#ROTATION_VELOCITY_BODY_X + + SimConnectAttitudeCommon(const AttitudeData &attitudeData) noexcept + : SimConnectAttitudeCommon() + { + fromAttitudeData(attitudeData); + } + + SimConnectAttitudeCommon() = default; + + inline void fromAttitudeData(const AttitudeData &data) noexcept + { + pitch = data.pitch; + bank = data.bank; + trueHeading = data.trueHeading; + + velocityBodyX = data.velocityBodyX; + velocityBodyY = data.velocityBodyY; + velocityBodyZ = data.velocityBodyZ; + } + + inline AttitudeData toAttitudeData() const noexcept + { + AttitudeData data; + toAttitudeData(data); + return data; + } + + inline void toAttitudeData(AttitudeData &data) const noexcept + { + data.pitch = pitch; + data.bank = bank; + data.trueHeading = trueHeading; + + data.velocityBodyX = velocityBodyX; + data.velocityBodyY = velocityBodyY; + data.velocityBodyZ = velocityBodyZ; + } + + static inline void addToDataDefinition(HANDLE simConnectHandle, ::SIMCONNECT_DATA_DEFINITION_ID dataDefinitionId) noexcept + { + // Aircraft attitude + ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::Pitch, "Degrees", ::SIMCONNECT_DATATYPE_FLOAT64); + ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::Bank, "Degrees", ::SIMCONNECT_DATATYPE_FLOAT64); + ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::TrueHeading, "Degrees", ::SIMCONNECT_DATATYPE_FLOAT64); + + // Velocity + ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::VelocityBodyX, "Feet per Second", ::SIMCONNECT_DATATYPE_FLOAT64); + ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::VelocityBodyY, "Feet per Second", ::SIMCONNECT_DATATYPE_FLOAT64); + ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::VelocityBodyZ, "Feet per Second", ::SIMCONNECT_DATATYPE_FLOAT64); + } +}; +#pragma pack(pop) + +#endif // SIMCONNECTATTITUDECOMMON_H diff --git a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/Attitude/SimConnectAttitudeInfo.h b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/Attitude/SimConnectAttitudeInfo.h new file mode 100644 index 000000000..9f0d23fcc --- /dev/null +++ b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/Attitude/SimConnectAttitudeInfo.h @@ -0,0 +1,80 @@ +/** + * Sky Dolly - The Black Sheep for your Flight Recordings + * + * Copyright (c) Oliver Knoll + * All rights reserved. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this + * software and associated documentation files (the "Software"), to deal in the Software + * without restriction, including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE + * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef SIMCONNECTATTITUDEINFOH +#define SIMCONNECTATTITUDEINFOH + +#include +#include + +#include +#include +#include +#include +#include +#include "SimConnectType.h" + +/*! + * Common aircraft position simulation variables that are sent both to the user- and AI aircraft. + * + * Implementation note: this struct needs to be packed. + */ +#pragma pack(push, 1) +struct SimConnectAttitudeInfo +{ + std::int32_t onGround {0}; + + SimConnectAttitudeInfo(const AttitudeData &attitudeData) noexcept + : SimConnectAttitudeInfo() + { + fromAttitudeData(attitudeData); + } + + SimConnectAttitudeInfo() = default; + + inline void fromAttitudeData(const AttitudeData &data) noexcept + { + onGround = data.onGround ? 1 : 0; + } + + inline AttitudeData toAttitudeData() const noexcept + { + AttitudeData data; + toAttitudeData(data); + return data; + } + + inline void toAttitudeData(AttitudeData &data) const noexcept + { + data.onGround = onGround != 0; + } + + static inline void addToDataDefinition(HANDLE simConnectHandle, ::SIMCONNECT_DATA_DEFINITION_ID dataDefinitionId) noexcept + { + ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::SimOnGround, "Bool", ::SIMCONNECT_DATATYPE_INT32); + } +}; +#pragma pack(pop) + +#endif // SIMCONNECTATTITUDEINFOH diff --git a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/Light/SimConnectLightEvent.h b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/Light/SimConnectLightEvent.h index 3d62f7ff3..901758e0f 100644 --- a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/Light/SimConnectLightEvent.h +++ b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/Light/SimConnectLightEvent.h @@ -94,18 +94,17 @@ struct SimConnectLightEvent static void addToDataDefinition(HANDLE simConnectHandle, ::SIMCONNECT_DATA_DEFINITION_ID dataDefinitionId) noexcept { - ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::LightNav, "Boolean", ::SIMCONNECT_DATATYPE_INT32); - ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::LightBeacon, "Boolean", ::SIMCONNECT_DATATYPE_INT32); - ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::LightLanding, "Boolean", ::SIMCONNECT_DATATYPE_INT32); - ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::LightTaxi, "Boolean", ::SIMCONNECT_DATATYPE_INT32); - ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::LightStrobe, "Boolean", ::SIMCONNECT_DATATYPE_INT32); - ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::LightPanel, "Boolean", ::SIMCONNECT_DATATYPE_INT32); - ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::LightRecognition, "Boolean", ::SIMCONNECT_DATATYPE_INT32); - ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::LightWing, "Boolean", ::SIMCONNECT_DATATYPE_INT32); - ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::LightLogo, "Boolean", ::SIMCONNECT_DATATYPE_INT32); - ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::LightCabin, "Boolean", ::SIMCONNECT_DATATYPE_INT32); + ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::LightNav, "Bool", ::SIMCONNECT_DATATYPE_INT32); + ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::LightBeacon, "Bool", ::SIMCONNECT_DATATYPE_INT32); + ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::LightLanding, "Bool", ::SIMCONNECT_DATATYPE_INT32); + ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::LightTaxi, "Bool", ::SIMCONNECT_DATATYPE_INT32); + ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::LightStrobe, "Bool", ::SIMCONNECT_DATATYPE_INT32); + ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::LightPanel, "Bool", ::SIMCONNECT_DATATYPE_INT32); + ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::LightRecognition, "Bool", ::SIMCONNECT_DATATYPE_INT32); + ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::LightWing, "Bool", ::SIMCONNECT_DATATYPE_INT32); + ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::LightLogo, "Bool", ::SIMCONNECT_DATATYPE_INT32); + ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::LightCabin, "Bool", ::SIMCONNECT_DATATYPE_INT32); } - }; #pragma pack(pop) diff --git a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/Position/SimConnectPositionAll.h b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/Position/SimConnectPositionAll.h index bf6f1a789..a2cabe62b 100644 --- a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/Position/SimConnectPositionAll.h +++ b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/Position/SimConnectPositionAll.h @@ -31,8 +31,6 @@ #include #include "SimConnectPositionCommon.h" #include "SimConnectPositionInfo.h" -#include "SimConnectPositionUser.h" -#include "SimConnectPositionAi.h" /*! * All aircraft position simulation variables (reply from the flight simulator). @@ -61,55 +59,9 @@ struct SimConnectPositionAll inline PositionData toPositionData() const noexcept { - PositionData positionData = common.toPositionData(); + auto positionData = common.toPositionData(); info.toPositionData(positionData); return positionData; - } - - inline SimConnectPositionUser user() const noexcept - { - SimConnectPositionUser user; - user.common = common; - return user; - } - - inline SimConnectPositionAi ai() const noexcept - { - SimConnectPositionAi ai; - ai.common = common; - return ai; - } - - static inline SIMCONNECT_DATA_INITPOSITION toInitialPosition(const PositionData &positionData, bool onGround, int initialAirspeed) - { - SIMCONNECT_DATA_INITPOSITION initialPosition {}; - - initialPosition.Latitude = positionData.latitude; - initialPosition.Longitude = positionData.longitude; - initialPosition.Altitude = positionData.altitude; - initialPosition.Pitch = positionData.pitch; - initialPosition.Bank = positionData.bank; - initialPosition.Heading = positionData.trueHeading; - initialPosition.OnGround = onGround ? 1 : 0; - initialPosition.Airspeed = initialAirspeed; - - return initialPosition; - } - - static inline SIMCONNECT_DATA_INITPOSITION toInitialPosition(const InitialPosition &initialPosition) - { - SIMCONNECT_DATA_INITPOSITION initialSimConnnectPosition {}; - - initialSimConnnectPosition.Latitude = initialPosition.latitude; - initialSimConnnectPosition.Longitude = initialPosition.longitude; - initialSimConnnectPosition.Altitude = initialPosition.altitude; - initialSimConnnectPosition.Pitch = initialPosition.pitch; - initialSimConnnectPosition.Bank = initialPosition.bank; - initialSimConnnectPosition.Heading = initialPosition.trueHeading; - initialSimConnnectPosition.OnGround = initialPosition.onGround ? 1 : 0; - initialSimConnnectPosition.Airspeed = initialPosition.indicatedAirspeed; - - return initialSimConnnectPosition; } static void addToDataDefinition(HANDLE simConnectHandle) noexcept @@ -117,7 +69,6 @@ struct SimConnectPositionAll SimConnectPositionCommon::addToDataDefinition(simConnectHandle, Enum::underly(SimConnectType::DataDefinition::PositionAll)); SimConnectPositionInfo::addToDataDefinition(simConnectHandle, Enum::underly(SimConnectType::DataDefinition::PositionAll)); } - }; #pragma pack(pop) diff --git a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/Position/SimConnectPositionCommon.h b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/Position/SimConnectPositionCommon.h index 3146d2204..111e48df6 100644 --- a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/Position/SimConnectPositionCommon.h +++ b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/Position/SimConnectPositionCommon.h @@ -25,8 +25,6 @@ #ifndef SIMCONNECTPOSITIONCOMMON_H #define SIMCONNECTPOSITIONCOMMON_H -#include - #include #include @@ -49,14 +47,6 @@ struct SimConnectPositionCommon double latitude {0.0}; double longitude {0.0}; double altitude {0.0}; - double pitch {0.0}; - double bank {0.0}; - double trueHeading {0.0}; - - // Velocity - double velocityBodyX {0.0}; - double velocityBodyY {0.0}; - double velocityBodyZ {0.0}; // Implementation note: // If we would store the "rotation velocity body" (which we currently do not anymore) then @@ -76,13 +66,6 @@ struct SimConnectPositionCommon latitude = positionData.latitude; longitude = positionData.longitude; altitude = positionData.altitude; - pitch = positionData.pitch; - bank = positionData.bank; - trueHeading = positionData.trueHeading; - - velocityBodyX = positionData.velocityBodyX; - velocityBodyY = positionData.velocityBodyY; - velocityBodyZ = positionData.velocityBodyZ; } inline PositionData toPositionData() const noexcept @@ -94,17 +77,9 @@ struct SimConnectPositionCommon inline void toPositionData(PositionData &positionData) const noexcept { - positionData.latitude = latitude; positionData.longitude = longitude; positionData.altitude = altitude; - positionData.pitch = pitch; - positionData.bank = bank; - positionData.trueHeading = trueHeading; - - positionData.velocityBodyX = velocityBodyX; - positionData.velocityBodyY = velocityBodyY; - positionData.velocityBodyZ = velocityBodyZ; } static inline void addToDataDefinition(HANDLE simConnectHandle, ::SIMCONNECT_DATA_DEFINITION_ID dataDefinitionId) noexcept @@ -113,14 +88,6 @@ struct SimConnectPositionCommon ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::Latitude, "Degrees", ::SIMCONNECT_DATATYPE_FLOAT64); ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::Longitude, "Degrees", ::SIMCONNECT_DATATYPE_FLOAT64); ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::Altitude, "Feet", ::SIMCONNECT_DATATYPE_FLOAT64); - ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::Pitch, "Degrees", ::SIMCONNECT_DATATYPE_FLOAT64); - ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::Bank, "Degrees", ::SIMCONNECT_DATATYPE_FLOAT64); - ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::TrueHeading, "Degrees", ::SIMCONNECT_DATATYPE_FLOAT64); - - // Velocity - ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::VelocityBodyX, "Feet per Second", ::SIMCONNECT_DATATYPE_FLOAT64); - ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::VelocityBodyY, "Feet per Second",::SIMCONNECT_DATATYPE_FLOAT64); - ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::VelocityBodyZ, "Feet per Second",::SIMCONNECT_DATATYPE_FLOAT64); } }; #pragma pack(pop) diff --git a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/Position/SimConnectPositionAi.h b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/PositionAndAttitude/SimConnectPositionAndAttitudeAi.h similarity index 59% rename from src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/Position/SimConnectPositionAi.h rename to src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/PositionAndAttitude/SimConnectPositionAndAttitudeAi.h index c02be3425..1f4fcf826 100644 --- a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/Position/SimConnectPositionAi.h +++ b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/PositionAndAttitude/SimConnectPositionAndAttitudeAi.h @@ -22,13 +22,15 @@ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ -#ifndef SIMCONNECTPOSITIONAI_H -#define SIMCONNECTPOSITIONAI_H +#ifndef SIMCONNECTPOSITIONANDATTITUDEAI_H +#define SIMCONNECTPOSITIONANDATTITUDEAI_H #include #include +#include #include "SimConnectType.h" -#include "SimConnectPositionCommon.h" +#include "../Position/SimConnectPositionCommon.h" +#include "../Attitude/SimConnectAttitudeCommon.h" /*! * Position simulation variables that are sent to AI aircraft. @@ -36,34 +38,48 @@ * Implementation note: this struct needs to be packed. */ #pragma pack(push, 1) -struct SimConnectPositionAi +struct SimConnectPositionAndAttitudeAi { - SimConnectPositionCommon common; + SimConnectPositionCommon positionCommon; + SimConnectAttitudeCommon attitudeCommon; - SimConnectPositionAi(const PositionData &positionData) noexcept - : SimConnectPositionAi() + SimConnectPositionAndAttitudeAi(const PositionData &positionData, const AttitudeData &attitudeData) noexcept + : SimConnectPositionAndAttitudeAi() { fromPositionData(positionData); + fromAttitudeData(attitudeData); } - SimConnectPositionAi() = default; + SimConnectPositionAndAttitudeAi() = default; inline void fromPositionData(const PositionData &positionData) { - common.fromPositionData(positionData); + positionCommon.fromPositionData(positionData); } inline PositionData toPositionData() const noexcept { - PositionData positionData = common.toPositionData(); + const auto positionData = positionCommon.toPositionData(); return positionData; } + inline void fromAttitudeData(const AttitudeData &attitudeData) + { + attitudeCommon.fromAttitudeData(attitudeData); + } + + inline AttitudeData toAttitudeData() const noexcept + { + const auto attitudeData = attitudeCommon.toAttitudeData(); + return attitudeData; + } + static void addToDataDefinition(HANDLE simConnectHandle) noexcept { - SimConnectPositionCommon::addToDataDefinition(simConnectHandle, Enum::underly(SimConnectType::DataDefinition::PositionAi)); + SimConnectPositionCommon::addToDataDefinition(simConnectHandle, Enum::underly(SimConnectType::DataDefinition::PositionAndAttitudeAi)); + SimConnectAttitudeCommon::addToDataDefinition(simConnectHandle, Enum::underly(SimConnectType::DataDefinition::PositionAndAttitudeAi)); } }; #pragma pack(pop) -#endif // SIMCONNECTPOSITIONAI_H +#endif // SIMCONNECTPOSITIONANDATTITUDEAI_H diff --git a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/PositionAndAttitude/SimConnectPositionAndAttitudeAll.h b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/PositionAndAttitude/SimConnectPositionAndAttitudeAll.h new file mode 100644 index 000000000..e93cc321d --- /dev/null +++ b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/PositionAndAttitude/SimConnectPositionAndAttitudeAll.h @@ -0,0 +1,124 @@ +/** + * Sky Dolly - The Black Sheep for your Flight Recordings + * + * Copyright (c) Oliver Knoll + * All rights reserved. + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this + * software and associated documentation files (the "Software"), to deal in the Software + * without restriction, including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE + * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef SIMCONNECTPOSITIONANDATTITUDEALL_H +#define SIMCONNECTPOSITIONANDATTITUDEALL_H + +#include +#include + +#include +#include +#include "../Position/SimConnectPositionCommon.h" +#include "../Attitude/SimConnectAttitudeCommon.h" +#include "SimConnectPositionAndAttitudeUser.h" +#include "SimConnectPositionAndAttitudeAi.h" + +/*! + * All aircraft position simulation variables (reply from the flight simulator). + * + * Implementation note: this struct needs to be packed. + */ +#pragma pack(push, 1) +struct SimConnectPositionAndAttitudeAll +{ + SimConnectPositionCommon positionCommon; + SimConnectAttitudeCommon attitudeCommon; + + SimConnectPositionAndAttitudeAll(const PositionData &positionData, const AttitudeData &attitudeData) noexcept + : SimConnectPositionAndAttitudeAll() + { + fromPositionData(positionData); + fromAttitudeData(attitudeData); + } + + SimConnectPositionAndAttitudeAll() = default; + + inline void fromPositionData(const PositionData &positionData) noexcept + { + positionCommon.fromPositionData(positionData); + } + + inline void fromAttitudeData(const AttitudeData &attitudeData) noexcept + { + attitudeCommon.fromAttitudeData(attitudeData); + } + + inline SimConnectPositionAndAttitudeUser user() const noexcept + { + SimConnectPositionAndAttitudeUser user; + user.positionCommon = positionCommon; + user.attitudeCommon = attitudeCommon; + return user; + } + + inline SimConnectPositionAndAttitudeAi ai() const noexcept + { + SimConnectPositionAndAttitudeAi ai; + ai.positionCommon = positionCommon; + ai.attitudeCommon = attitudeCommon; + return ai; + } + + static inline SIMCONNECT_DATA_INITPOSITION toInitialPosition(const PositionData &positionData, const AttitudeData &attitudeData, int initialAirspeed) + { + SIMCONNECT_DATA_INITPOSITION initialPosition {}; + + initialPosition.Latitude = positionData.latitude; + initialPosition.Longitude = positionData.longitude; + initialPosition.Altitude = positionData.altitude; + initialPosition.Pitch = attitudeData.pitch; + initialPosition.Bank = attitudeData.bank; + initialPosition.Heading = attitudeData.trueHeading; + initialPosition.OnGround = attitudeData.onGround ? 1 : 0; + initialPosition.Airspeed = initialAirspeed; + + return initialPosition; + } + + static inline SIMCONNECT_DATA_INITPOSITION toInitialPosition(const InitialPosition &initialPosition) + { + SIMCONNECT_DATA_INITPOSITION initialSimConnnectPosition {}; + + initialSimConnnectPosition.Latitude = initialPosition.latitude; + initialSimConnnectPosition.Longitude = initialPosition.longitude; + initialSimConnnectPosition.Altitude = initialPosition.altitude; + initialSimConnnectPosition.Pitch = initialPosition.pitch; + initialSimConnnectPosition.Bank = initialPosition.bank; + initialSimConnnectPosition.Heading = initialPosition.trueHeading; + initialSimConnnectPosition.OnGround = initialPosition.onGround ? 1 : 0; + initialSimConnnectPosition.Airspeed = initialPosition.indicatedAirspeed; + + return initialSimConnnectPosition; + } + + static void addToDataDefinition(HANDLE simConnectHandle) noexcept + { + SimConnectPositionCommon::addToDataDefinition(simConnectHandle, Enum::underly(SimConnectType::DataDefinition::PositionAndAttitudeAll)); + SimConnectAttitudeCommon::addToDataDefinition(simConnectHandle, Enum::underly(SimConnectType::DataDefinition::PositionAndAttitudeAll)); + } +}; +#pragma pack(pop) + +#endif // SIMCONNECTPOSITIONANDATTITUDEALL_H diff --git a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/Position/SimConnectPositionUser.h b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/PositionAndAttitude/SimConnectPositionAndAttitudeUser.h similarity index 56% rename from src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/Position/SimConnectPositionUser.h rename to src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/PositionAndAttitude/SimConnectPositionAndAttitudeUser.h index e87c95e49..b22f2a988 100644 --- a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/Position/SimConnectPositionUser.h +++ b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/PositionAndAttitude/SimConnectPositionAndAttitudeUser.h @@ -22,48 +22,64 @@ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ -#ifndef SIMCONNECTPOSITIONUSER_H -#define SIMCONNECTPOSITIONUSER_H +#ifndef SIMCONNECTPOSITIONANDATTITUDEUSER_H +#define SIMCONNECTPOSITIONANDATTITUDEUSER_H #include #include +#include #include "SimConnectType.h" -#include "SimConnectPositionCommon.h" +#include "../Position/SimConnectPositionCommon.h" +#include "../Attitude/SimConnectAttitudeCommon.h" /*! - * Aircraft position simulation variables that are sent to the user aircraft. + * Position simulation variables that are sent to AI aircraft. * * Implementation note: this struct needs to be packed. */ #pragma pack(push, 1) -struct SimConnectPositionUser +struct SimConnectPositionAndAttitudeUser { - SimConnectPositionCommon common; + SimConnectPositionCommon positionCommon; + SimConnectAttitudeCommon attitudeCommon; - SimConnectPositionUser(const PositionData &positionData) noexcept - : SimConnectPositionUser() + SimConnectPositionAndAttitudeUser(const PositionData &positionData, const AttitudeData &attitudeData) noexcept + : SimConnectPositionAndAttitudeUser() { fromPositionData(positionData); + fromAttitudeData(attitudeData); } - SimConnectPositionUser() = default; + SimConnectPositionAndAttitudeUser() = default; + + inline void fromPositionData(const PositionData &positionData) + { + positionCommon.fromPositionData(positionData); + } inline PositionData toPositionData() const noexcept { - PositionData positionData = common.toPositionData(); + const auto positionData = positionCommon.toPositionData(); return positionData; } - inline void fromPositionData(const PositionData &positionData) + inline void fromAttitudeData(const AttitudeData &attitudeData) + { + attitudeCommon.fromAttitudeData(attitudeData); + } + + inline AttitudeData toAttitudeData() const noexcept { - common.fromPositionData(positionData); + const auto attitudeData = attitudeCommon.toAttitudeData(); + return attitudeData; } static void addToDataDefinition(HANDLE simConnectHandle) noexcept { - SimConnectPositionCommon::addToDataDefinition(simConnectHandle, Enum::underly(SimConnectType::DataDefinition::PositionUser)); + SimConnectPositionCommon::addToDataDefinition(simConnectHandle, Enum::underly(SimConnectType::DataDefinition::PositionAndAttitudeUser)); + SimConnectAttitudeCommon::addToDataDefinition(simConnectHandle, Enum::underly(SimConnectType::DataDefinition::PositionAndAttitudeUser)); } }; #pragma pack(pop) -#endif // SIMCONNECTPOSITIONUSER_H +#endif // SIMCONNECTPOSITIONANDATTITUDEUSER_H diff --git a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/SecondaryFlightControl/SimConnectSecondaryFlightControlAnimation.h b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/SecondaryFlightControl/SimConnectSecondaryFlightControlAnimation.h index 0f2e2cba1..3647067a4 100644 --- a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/SecondaryFlightControl/SimConnectSecondaryFlightControlAnimation.h +++ b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/SecondaryFlightControl/SimConnectSecondaryFlightControlAnimation.h @@ -90,7 +90,7 @@ struct SimConnectSecondaryFlightControlAnimation static inline void addToDataDefinition(HANDLE simConnectHandle, ::SIMCONNECT_DATA_DEFINITION_ID dataDefinitionId) noexcept { ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::LeadingEdgeFlapsLeftPercent, "Position", ::SIMCONNECT_DATATYPE_FLOAT32); - ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::LeadingEdgeFlapsRightPercent, "Position",::SIMCONNECT_DATATYPE_FLOAT32); + ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::LeadingEdgeFlapsRightPercent, "Position", ::SIMCONNECT_DATATYPE_FLOAT32); ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::TrailingEdgeFlapsLeftPercent, "Position", ::SIMCONNECT_DATATYPE_FLOAT32); ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::TrailingEdgeFlapsRightPercent, "Position", ::SIMCONNECT_DATATYPE_FLOAT32); ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::SpoilersLeftPosition, "Position", ::SIMCONNECT_DATATYPE_FLOAT32); diff --git a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/SecondaryFlightControl/SimConnectSecondaryFlightControlEvent.h b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/SecondaryFlightControl/SimConnectSecondaryFlightControlEvent.h index 4834cbd47..344333f79 100644 --- a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/SecondaryFlightControl/SimConnectSecondaryFlightControlEvent.h +++ b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/SecondaryFlightControl/SimConnectSecondaryFlightControlEvent.h @@ -81,7 +81,7 @@ struct SimConnectSecondaryFlightControlEvent { // Spoilers, also known as "speed brakes" ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::SpoilersHandlePosition, "Percent", ::SIMCONNECT_DATATYPE_FLOAT32); - ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::SpoilersArmed, "Boolean", ::SIMCONNECT_DATATYPE_INT32); + ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::SpoilersArmed, "Bool", ::SIMCONNECT_DATATYPE_INT32); ::SimConnect_AddToDataDefinition(simConnectHandle, dataDefinitionId, SimVar::FlapsHandleIndex, "Number", ::SIMCONNECT_DATATYPE_INT32); } }; diff --git a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/SimConnectType.h b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/SimConnectType.h index 718fe0e71..d9197d9e4 100644 --- a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/SimConnectType.h +++ b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/SimConnectType.h @@ -38,9 +38,11 @@ namespace SimConnectType SimulationTime, InitialPosition, Location, - PositionUser, - PositionAi, PositionAll, + AttitudeAll, + PositionAndAttitudeAi, + PositionAndAttitudeUser, + PositionAndAttitudeAll, EngineUser, EngineAi, EngineAll, @@ -75,7 +77,8 @@ namespace SimConnectType InitialPosition, Location, SimulationTime, - AircraftPositionAll, + PositionAll, + AttitudeAll, EngineAll, PrimaryFlightControlAll, SecondaryFlightControlAll, diff --git a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/SimConnectVariables.cpp b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/SimConnectVariables.cpp index 8886dcae2..e6a74648b 100644 --- a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/SimConnectVariables.cpp +++ b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/SimConnectVariables.cpp @@ -37,16 +37,16 @@ void SimConnectVariables::addToDataDefinition(HANDLE simConnectHandle) noexcept ::SimConnect_AddToDataDefinition(simConnectHandle, Enum::underly(SimConnectType::DataDefinition::FlapsHandleIndex), SimVar::FlapsHandleIndex, "Number", ::SIMCONNECT_DATATYPE_INT32); // Lights - ::SimConnect_AddToDataDefinition(simConnectHandle, Enum::underly(SimConnectType::DataDefinition::NavigationLight), SimVar::LightNav, "Boolean", ::SIMCONNECT_DATATYPE_INT32); - ::SimConnect_AddToDataDefinition(simConnectHandle, Enum::underly(SimConnectType::DataDefinition::BeaconLight), SimVar::LightBeacon, "Boolean", ::SIMCONNECT_DATATYPE_INT32); - ::SimConnect_AddToDataDefinition(simConnectHandle, Enum::underly(SimConnectType::DataDefinition::LandingLight), SimVar::LightLanding, "Boolean", ::SIMCONNECT_DATATYPE_INT32); - ::SimConnect_AddToDataDefinition(simConnectHandle, Enum::underly(SimConnectType::DataDefinition::TaxiLight), SimVar::LightTaxi, "Boolean", ::SIMCONNECT_DATATYPE_INT32); - ::SimConnect_AddToDataDefinition(simConnectHandle, Enum::underly(SimConnectType::DataDefinition::StrobeLight), SimVar::LightStrobe, "Boolean", ::SIMCONNECT_DATATYPE_INT32); - ::SimConnect_AddToDataDefinition(simConnectHandle, Enum::underly(SimConnectType::DataDefinition::PanelLight), SimVar::LightPanel, "Boolean", ::SIMCONNECT_DATATYPE_INT32); - ::SimConnect_AddToDataDefinition(simConnectHandle, Enum::underly(SimConnectType::DataDefinition::RecognitionLight), SimVar::LightRecognition, "Boolean", ::SIMCONNECT_DATATYPE_INT32); - ::SimConnect_AddToDataDefinition(simConnectHandle, Enum::underly(SimConnectType::DataDefinition::WingLight), SimVar::LightWing, "Boolean", ::SIMCONNECT_DATATYPE_INT32); - ::SimConnect_AddToDataDefinition(simConnectHandle, Enum::underly(SimConnectType::DataDefinition::LogoLight), SimVar::LightLogo, "Boolean", ::SIMCONNECT_DATATYPE_INT32); - ::SimConnect_AddToDataDefinition(simConnectHandle, Enum::underly(SimConnectType::DataDefinition::CabinLight), SimVar::LightCabin, "Boolean", ::SIMCONNECT_DATATYPE_INT32); + ::SimConnect_AddToDataDefinition(simConnectHandle, Enum::underly(SimConnectType::DataDefinition::NavigationLight), SimVar::LightNav, "Bool", ::SIMCONNECT_DATATYPE_INT32); + ::SimConnect_AddToDataDefinition(simConnectHandle, Enum::underly(SimConnectType::DataDefinition::BeaconLight), SimVar::LightBeacon, "Bool", ::SIMCONNECT_DATATYPE_INT32); + ::SimConnect_AddToDataDefinition(simConnectHandle, Enum::underly(SimConnectType::DataDefinition::LandingLight), SimVar::LightLanding, "Bool", ::SIMCONNECT_DATATYPE_INT32); + ::SimConnect_AddToDataDefinition(simConnectHandle, Enum::underly(SimConnectType::DataDefinition::TaxiLight), SimVar::LightTaxi, "Bool", ::SIMCONNECT_DATATYPE_INT32); + ::SimConnect_AddToDataDefinition(simConnectHandle, Enum::underly(SimConnectType::DataDefinition::StrobeLight), SimVar::LightStrobe, "Bool", ::SIMCONNECT_DATATYPE_INT32); + ::SimConnect_AddToDataDefinition(simConnectHandle, Enum::underly(SimConnectType::DataDefinition::PanelLight), SimVar::LightPanel, "Bool", ::SIMCONNECT_DATATYPE_INT32); + ::SimConnect_AddToDataDefinition(simConnectHandle, Enum::underly(SimConnectType::DataDefinition::RecognitionLight), SimVar::LightRecognition, "Bool", ::SIMCONNECT_DATATYPE_INT32); + ::SimConnect_AddToDataDefinition(simConnectHandle, Enum::underly(SimConnectType::DataDefinition::WingLight), SimVar::LightWing, "Bool", ::SIMCONNECT_DATATYPE_INT32); + ::SimConnect_AddToDataDefinition(simConnectHandle, Enum::underly(SimConnectType::DataDefinition::LogoLight), SimVar::LightLogo, "Bool", ::SIMCONNECT_DATATYPE_INT32); + ::SimConnect_AddToDataDefinition(simConnectHandle, Enum::underly(SimConnectType::DataDefinition::CabinLight), SimVar::LightCabin, "Bool", ::SIMCONNECT_DATATYPE_INT32); // Simulation ::SimConnect_AddToDataDefinition(simConnectHandle, Enum::underly(SimConnectType::DataDefinition::SimulationRate), SimVar::SimulationRate, "Number", ::SIMCONNECT_DATATYPE_FLOAT32); diff --git a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/SimConnectVariables.h b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/SimConnectVariables.h index 7a02029fc..9fe497422 100644 --- a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/SimConnectVariables.h +++ b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/SimConnectVariables.h @@ -114,7 +114,6 @@ namespace SimConnectVariables struct LogoLight { std::int32_t value; - }; #pragma pack(pop) diff --git a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/SimulationVariables.h b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/SimulationVariables.h index 0e7941bd1..801de56ec 100644 --- a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/SimulationVariables.h +++ b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/SimulationVariables.h @@ -27,8 +27,11 @@ #include "SimConnectFlightInfo.h" // IWYU pragma: export #include "SimConnectAircraftInfo.h" // IWYU pragma: export -#include "Position/SimConnectPositionUser.h" // IWYU pragma: export #include "Position/SimConnectPositionAll.h" // IWYU pragma: export +#include "Attitude/SimConnectAttitudeAll.h" // IWYU pragma: export +#include "PositionAndAttitude/SimConnectPositionAndAttitudeAi.h" // IWYU pragma: export +#include "PositionAndAttitude/SimConnectPositionAndAttitudeUser.h" // IWYU pragma: export +#include "PositionAndAttitude/SimConnectPositionAndAttitudeAll.h" // IWYU pragma: export #include "Engine/SimConnectEngineUser.h" // IWYU pragma: export #include "Engine/SimConnectEngineAi.h" // IWYU pragma: export #include "Engine/SimConnectEngineAll.h" // IWYU pragma: export From 520f7bcf8466dec0e31acea6cea79c78bb7f00f6 Mon Sep 17 00:00:00 2001 From: Oliver Knoll Date: Sat, 11 May 2024 17:57:07 +0200 Subject: [PATCH 3/9] Update API docu --- .../SimVar/Attitude/SimConnectAttitudeAll.h | 2 +- .../SimVar/Attitude/SimConnectAttitudeInfo.h | 8 ++---- .../Position/SimConnectPositionCommon.h | 4 --- .../SimVar/Position/SimConnectPositionInfo.h | 3 -- .../SimConnectPositionAndAttitudeAi.h | 2 +- .../SimConnectPositionAndAttitudeAll.h | 2 +- .../SimConnectPositionAndAttitudeUser.h | 4 +-- .../SimConnectPrimaryFlightControlUser.h | 28 ------------------- 8 files changed, 8 insertions(+), 45 deletions(-) delete mode 100644 src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/PrimaryFlightControl/SimConnectPrimaryFlightControlUser.h diff --git a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/Attitude/SimConnectAttitudeAll.h b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/Attitude/SimConnectAttitudeAll.h index 3ba66777d..d834d549e 100644 --- a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/Attitude/SimConnectAttitudeAll.h +++ b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/Attitude/SimConnectAttitudeAll.h @@ -33,7 +33,7 @@ #include "SimConnectAttitudeInfo.h" /*! - * All aircraft position simulation variables (reply from the flight simulator). + * All aircraft attitude simulation variables (reply from the flight simulator). * * Implementation note: this struct needs to be packed. */ diff --git a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/Attitude/SimConnectAttitudeInfo.h b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/Attitude/SimConnectAttitudeInfo.h index 9f0d23fcc..c262000d6 100644 --- a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/Attitude/SimConnectAttitudeInfo.h +++ b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/Attitude/SimConnectAttitudeInfo.h @@ -28,15 +28,13 @@ #include #include -#include -#include #include -#include -#include +#include #include "SimConnectType.h" /*! - * Common aircraft position simulation variables that are sent both to the user- and AI aircraft. + * Aircraft attitude simulation variables that are either stored for information purposes only + * or that are sent exclusively to the user aircraft as events. * * Implementation note: this struct needs to be packed. */ diff --git a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/Position/SimConnectPositionCommon.h b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/Position/SimConnectPositionCommon.h index 111e48df6..51573f02f 100644 --- a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/Position/SimConnectPositionCommon.h +++ b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/Position/SimConnectPositionCommon.h @@ -28,12 +28,8 @@ #include #include -#include -#include #include #include -#include -#include "SimConnectType.h" /*! * Common aircraft position simulation variables that are sent both to the user- and AI aircraft. diff --git a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/Position/SimConnectPositionInfo.h b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/Position/SimConnectPositionInfo.h index 9891a9072..a4f819c7f 100644 --- a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/Position/SimConnectPositionInfo.h +++ b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/Position/SimConnectPositionInfo.h @@ -30,11 +30,8 @@ #include #include -#include -#include #include #include -#include #include "SimConnectType.h" /*! diff --git a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/PositionAndAttitude/SimConnectPositionAndAttitudeAi.h b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/PositionAndAttitude/SimConnectPositionAndAttitudeAi.h index 1f4fcf826..0ab33357b 100644 --- a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/PositionAndAttitude/SimConnectPositionAndAttitudeAi.h +++ b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/PositionAndAttitude/SimConnectPositionAndAttitudeAi.h @@ -33,7 +33,7 @@ #include "../Attitude/SimConnectAttitudeCommon.h" /*! - * Position simulation variables that are sent to AI aircraft. + * Position and attitude simulation variables that are sent to AI aircraft. * * Implementation note: this struct needs to be packed. */ diff --git a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/PositionAndAttitude/SimConnectPositionAndAttitudeAll.h b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/PositionAndAttitude/SimConnectPositionAndAttitudeAll.h index e93cc321d..ce57cd007 100644 --- a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/PositionAndAttitude/SimConnectPositionAndAttitudeAll.h +++ b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/PositionAndAttitude/SimConnectPositionAndAttitudeAll.h @@ -36,7 +36,7 @@ #include "SimConnectPositionAndAttitudeAi.h" /*! - * All aircraft position simulation variables (reply from the flight simulator). + * All aircraft position and attitude simulation variables (request to the flight simulator). * * Implementation note: this struct needs to be packed. */ diff --git a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/PositionAndAttitude/SimConnectPositionAndAttitudeUser.h b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/PositionAndAttitude/SimConnectPositionAndAttitudeUser.h index b22f2a988..2117f441e 100644 --- a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/PositionAndAttitude/SimConnectPositionAndAttitudeUser.h +++ b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/PositionAndAttitude/SimConnectPositionAndAttitudeUser.h @@ -28,12 +28,12 @@ #include #include #include -#include "SimConnectType.h" #include "../Position/SimConnectPositionCommon.h" #include "../Attitude/SimConnectAttitudeCommon.h" +#include "SimConnectType.h" /*! - * Position simulation variables that are sent to AI aircraft. + * Position and attitude simulation variables that are sent to the user aircraft. * * Implementation note: this struct needs to be packed. */ diff --git a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/PrimaryFlightControl/SimConnectPrimaryFlightControlUser.h b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/PrimaryFlightControl/SimConnectPrimaryFlightControlUser.h deleted file mode 100644 index 06dff8a7d..000000000 --- a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/PrimaryFlightControl/SimConnectPrimaryFlightControlUser.h +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Sky Dolly - The Black Sheep for your Flight Recordings - * - * Copyright (c) Oliver Knoll - * All rights reserved. - * - * MIT License - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this - * software and associated documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons - * to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR - * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE - * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ -#ifndef SIMCONNECTPRIMARYFLIGHTCONTROLUSER_H -#define SIMCONNECTPRIMARYFLIGHTCONTROLUSER_H - -#endif // SIMCONNECTPRIMARYFLIGHTCONTROLUSER_H From e3a9f6fc48dd7b682ecc2bdc386a09d8c583ad99 Mon Sep 17 00:00:00 2001 From: Oliver Knoll Date: Sat, 11 May 2024 18:07:16 +0200 Subject: [PATCH 4/9] Persist the attitude with the attitude DAO - TODO: database tables and migration --- src/Persistence/src/Dao/DaoFactory.cpp | 13 +++++++++++ src/Persistence/src/Dao/DaoFactory.h | 2 ++ .../src/Dao/SQLite/SQLiteAircraftDao.cpp | 22 +++++++++++++++++++ .../src/Dao/SQLite/SQLiteAttitudeDao.cpp | 1 + ...imConnectSecondaryFlightControlAnimation.h | 2 -- 5 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/Persistence/src/Dao/DaoFactory.cpp b/src/Persistence/src/Dao/DaoFactory.cpp index 3946ebe8f..eca781b23 100644 --- a/src/Persistence/src/Dao/DaoFactory.cpp +++ b/src/Persistence/src/Dao/DaoFactory.cpp @@ -31,6 +31,7 @@ #include "SQLite/SQLiteAircraftDao.h" #include "SQLite/SQLiteAircraftTypeDao.h" #include "SQLite/SQLitePositionDao.h" +#include "SQLite/SQLiteAttitudeDao.h" #include "SQLite/SQLiteEngineDao.h" #include "SQLite/SQLitePrimaryFlightControlDao.h" #include "SQLite/SQLiteSecondaryFlightControlDao.h" @@ -43,6 +44,7 @@ #include "AircraftDaoIntf.h" #include "AircraftTypeDaoIntf.h" #include "PositionDaoIntf.h" +#include "AttitudeDaoIntf.h" #include "EngineDaoIntf.h" #include "PrimaryFlightControlDaoIntf.h" #include "SecondaryFlightControlDaoIntf.h" @@ -140,6 +142,17 @@ std::unique_ptr DaoFactory::createPositionDao() noexcept return dao; } +std::unique_ptr DaoFactory::createAttitudeDao() noexcept +{ + std::unique_ptr dao {nullptr}; + switch (d->dbType) { + case DbType::SQLite: + dao = std::make_unique(d->connectionName); + break; + } + return dao; +} + std::unique_ptr DaoFactory::createEngineDao() noexcept { std::unique_ptr dao {nullptr}; diff --git a/src/Persistence/src/Dao/DaoFactory.h b/src/Persistence/src/Dao/DaoFactory.h index 751adbab3..07bd72815 100644 --- a/src/Persistence/src/Dao/DaoFactory.h +++ b/src/Persistence/src/Dao/DaoFactory.h @@ -36,6 +36,7 @@ class FlightDaoIntf; class AircraftDaoIntf; class AircraftTypeDaoIntf; class PositionDaoIntf; +class AttitudeDaoIntf; class EngineDaoIntf; class PrimaryFlightControlDaoIntf; class SecondaryFlightControlDaoIntf; @@ -70,6 +71,7 @@ class DaoFactory std::unique_ptr createAircraftDao() noexcept; std::unique_ptr createAircraftTypeDao() noexcept; std::unique_ptr createPositionDao() noexcept; + std::unique_ptr createAttitudeDao() noexcept; std::unique_ptr createEngineDao() noexcept; std::unique_ptr createPrimaryFlightControlDao() noexcept; std::unique_ptr createSecondaryFlightControlDao() noexcept; diff --git a/src/Persistence/src/Dao/SQLite/SQLiteAircraftDao.cpp b/src/Persistence/src/Dao/SQLite/SQLiteAircraftDao.cpp index ec73b0cd6..f0b1d5e04 100644 --- a/src/Persistence/src/Dao/SQLite/SQLiteAircraftDao.cpp +++ b/src/Persistence/src/Dao/SQLite/SQLiteAircraftDao.cpp @@ -45,6 +45,8 @@ #include #include #include +#include +#include #include #include #include @@ -59,6 +61,7 @@ #include #include "../../Dao/AircraftTypeDaoIntf.h" #include "../../Dao/PositionDaoIntf.h" +#include "../../Dao/AttitudeDaoIntf.h" #include "../../Dao/EngineDaoIntf.h" #include "../../Dao/PrimaryFlightControlDaoIntf.h" #include "../../Dao/SecondaryFlightControlDaoIntf.h" @@ -75,6 +78,7 @@ struct SQLiteAircraftDaoPrivate daoFactory(std::make_unique(DaoFactory::DbType::SQLite, std::move(connectionName))), aircraftTypeDao(daoFactory->createAircraftTypeDao()), positionDao(daoFactory->createPositionDao()), + attitudeDao(daoFactory->createAttitudeDao())), engineDao(daoFactory->createEngineDao()), primaryFlightControlDao(daoFactory->createPrimaryFlightControlDao()), secondaryFlightControlDao(daoFactory->createSecondaryFlightControlDao()), @@ -87,6 +91,7 @@ struct SQLiteAircraftDaoPrivate std::unique_ptr daoFactory; std::unique_ptr aircraftTypeDao; std::unique_ptr positionDao; + std::unique_ptr attitudeDao; std::unique_ptr engineDao; std::unique_ptr primaryFlightControlDao; std::unique_ptr secondaryFlightControlDao; @@ -146,6 +151,9 @@ std::vector SQLiteAircraftDao::getByFlightId(std::int64_t flightId, bo aircraft.setId(info.aircraftId); aircraft.setAircraftInfo(info); aircraft.getPosition().setData(d->positionDao->getByAircraftId(aircraft.getId(), &success)); + if (success) { + aircraft.getAttitude().setData(d->attitudeDao->getByAircraftId(aircraft.getId(), &success)); + } if (success) { aircraft.getEngine().setData(d->engineDao->getByAircraftId(aircraft.getId(), &success)); } @@ -202,6 +210,9 @@ bool SQLiteAircraftDao::deleteAllByFlightId(std::int64_t flightId) const noexcep { // Delete "bottom-up" in order not to violate foreign key constraints bool ok = d->positionDao->deleteByFlightId(flightId); + if (ok) { + ok = d->attitudeDao->deleteByFlightId(flightId); + } if (ok) { ok = d->engineDao->deleteByFlightId(flightId); } @@ -245,6 +256,9 @@ bool SQLiteAircraftDao::deleteById(std::int64_t id) const noexcept // Delete "bottom-up" in order not to violate foreign key constraints // Note: aircraft types (table aircraft_type) are not deleted bool ok = d->positionDao->deleteByAircraftId(id); + if (ok) { + ok = d->attitudeDao->deleteByAircraftId(id); + } if (ok) { ok = d->engineDao->deleteByAircraftId(id); } @@ -452,6 +466,14 @@ inline bool SQLiteAircraftDao::insertAircraftData(std::int64_t aircraftId, const break; } } + if (ok) { + for (const auto &data : aircraft.getAttitude()) { + ok = d->attitudeDao->add(aircraftId, data); + if (!ok) { + break; + } + } + } if (ok) { for (const auto &data : aircraft.getEngine()) { ok = d->engineDao->add(aircraftId, data); diff --git a/src/Persistence/src/Dao/SQLite/SQLiteAttitudeDao.cpp b/src/Persistence/src/Dao/SQLite/SQLiteAttitudeDao.cpp index c85da3728..e10752365 100644 --- a/src/Persistence/src/Dao/SQLite/SQLiteAttitudeDao.cpp +++ b/src/Persistence/src/Dao/SQLite/SQLiteAttitudeDao.cpp @@ -110,6 +110,7 @@ bool SQLiteAttitudeDao::add(std::int64_t aircraftId, const AttitudeData &attitud query.bindValue(":velocity_x", attitude.velocityBodyX); query.bindValue(":velocity_y", attitude.velocityBodyY); query.bindValue(":velocity_z", attitude.velocityBodyZ); + query.bindValue(":on_ground", attitude.onGround); const bool ok = query.exec(); diff --git a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/SecondaryFlightControl/SimConnectSecondaryFlightControlAnimation.h b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/SecondaryFlightControl/SimConnectSecondaryFlightControlAnimation.h index 3647067a4..77525536c 100644 --- a/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/SecondaryFlightControl/SimConnectSecondaryFlightControlAnimation.h +++ b/src/Plugins/Connect/MSFSSimConnectPlugin/src/SimVar/SecondaryFlightControl/SimConnectSecondaryFlightControlAnimation.h @@ -25,8 +25,6 @@ #ifndef SIMCONNECTSECONDARYFLIGHTCONTROLANIMATION_H #define SIMCONNECTSECONDARYFLIGHTCONTROLANIMATION_H -#include - #include #include From a0099759624c6d0bfb42326ef7b0c672bae6f090 Mon Sep 17 00:00:00 2001 From: Oliver Knoll Date: Sat, 11 May 2024 18:10:40 +0200 Subject: [PATCH 5/9] Fix compilation --- src/Persistence/src/Dao/SQLite/SQLiteAircraftDao.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Persistence/src/Dao/SQLite/SQLiteAircraftDao.cpp b/src/Persistence/src/Dao/SQLite/SQLiteAircraftDao.cpp index f0b1d5e04..d1719ca48 100644 --- a/src/Persistence/src/Dao/SQLite/SQLiteAircraftDao.cpp +++ b/src/Persistence/src/Dao/SQLite/SQLiteAircraftDao.cpp @@ -78,7 +78,7 @@ struct SQLiteAircraftDaoPrivate daoFactory(std::make_unique(DaoFactory::DbType::SQLite, std::move(connectionName))), aircraftTypeDao(daoFactory->createAircraftTypeDao()), positionDao(daoFactory->createPositionDao()), - attitudeDao(daoFactory->createAttitudeDao())), + attitudeDao(daoFactory->createAttitudeDao()), engineDao(daoFactory->createEngineDao()), primaryFlightControlDao(daoFactory->createPrimaryFlightControlDao()), secondaryFlightControlDao(daoFactory->createSecondaryFlightControlDao()), From d0f9b9dcd6c46b2d14ac83ff99f22d292c077550 Mon Sep 17 00:00:00 2001 From: Oliver Knoll Date: Sat, 11 May 2024 18:46:57 +0200 Subject: [PATCH 6/9] Create new attitude table - TODO: * fix Formation module set relative position * migrate data --- .../src/Dao/SQLite/SQLiteAttitudeDao.cpp | 4 ---- .../src/Dao/SQLite/migr/LogbookMigration.sql | 15 +++++++++++++++ .../Connect/PathCreator/src/PathCreatorPlugin.cpp | 4 +--- src/UserInterface/src/Widget/AircraftWidget.cpp | 1 - 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/Persistence/src/Dao/SQLite/SQLiteAttitudeDao.cpp b/src/Persistence/src/Dao/SQLite/SQLiteAttitudeDao.cpp index e10752365..16b81aedc 100644 --- a/src/Persistence/src/Dao/SQLite/SQLiteAttitudeDao.cpp +++ b/src/Persistence/src/Dao/SQLite/SQLiteAttitudeDao.cpp @@ -89,10 +89,6 @@ bool SQLiteAttitudeDao::add(std::int64_t aircraftId, const AttitudeData &attitud ") values (" " :aircraft_id," " :timestamp," - " :latitude," - " :longitude," - " :altitude," - " :indicated_altitude," " :pitch," " :bank," " :true_heading," diff --git a/src/Persistence/src/Dao/SQLite/migr/LogbookMigration.sql b/src/Persistence/src/Dao/SQLite/migr/LogbookMigration.sql index 67e82a213..62e0c50ba 100644 --- a/src/Persistence/src/Dao/SQLite/migr/LogbookMigration.sql +++ b/src/Persistence/src/Dao/SQLite/migr/LogbookMigration.sql @@ -1008,6 +1008,21 @@ insert into aircraft_type values update metadata set app_version = '0.17.0'; +@migr(id = "4fda1c12-4c05-4152-af6b-1e495b12492e", descn = "Create attitude table", step = 1) +create table attitude ( + aircraft_id integer not null, + timestamp integer not null, + pitch real, + bank real, + true_heading real, + velocity_x real, + velocity_y real, + velocity_z real, + on_ground int, + primary key(aircraft_id, timestamp), + foreign key(aircraft_id) references aircraft(id) +); + @migr(id = "80bcc81a-6554-4e05-8631-d17358d9d1dd", descn = "Update application version to 0.18", step = 1) update metadata set app_version = '0.18.0'; diff --git a/src/Plugins/Connect/PathCreator/src/PathCreatorPlugin.cpp b/src/Plugins/Connect/PathCreator/src/PathCreatorPlugin.cpp index f3fc59994..6adf59cb2 100644 --- a/src/Plugins/Connect/PathCreator/src/PathCreatorPlugin.cpp +++ b/src/Plugins/Connect/PathCreator/src/PathCreatorPlugin.cpp @@ -352,7 +352,6 @@ void PathCreatorPlugin::recordPositionData(std::int64_t timestamp) noexcept positionData.longitude = -180.0 + d->randomGenerator->bounded(360.0); positionData.altitude = d->randomGenerator->bounded(60000.0); positionData.indicatedAltitude = d->randomGenerator->bounded(20000.0); - positionData.timestamp = timestamp; aircraft.getPosition().upsertLast(positionData); @@ -365,8 +364,7 @@ void PathCreatorPlugin::recordPositionData(std::int64_t timestamp) noexcept attitudeData.velocityBodyY = d->randomGenerator->bounded(1.0); attitudeData.velocityBodyZ = d->randomGenerator->bounded(1.0); attitudeData.onGround = false; - - positionData.timestamp = timestamp; + attitudeData.timestamp = timestamp; aircraft.getAttitude().upsertLast(attitudeData); } diff --git a/src/UserInterface/src/Widget/AircraftWidget.cpp b/src/UserInterface/src/Widget/AircraftWidget.cpp index bd4dcda1d..e8ba20b21 100644 --- a/src/UserInterface/src/Widget/AircraftWidget.cpp +++ b/src/UserInterface/src/Widget/AircraftWidget.cpp @@ -23,7 +23,6 @@ * DEALINGS IN THE SOFTWARE. */ #include -#include #include #include From 0b80c7d5b225bea9a94b8d51e289ecf3524403f2 Mon Sep 17 00:00:00 2001 From: Oliver Knoll Date: Sat, 11 May 2024 19:23:18 +0200 Subject: [PATCH 7/9] Migrate attitude related data from table position to attitude --- .../src/Dao/SQLite/migr/LogbookMigration.sql | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/Persistence/src/Dao/SQLite/migr/LogbookMigration.sql b/src/Persistence/src/Dao/SQLite/migr/LogbookMigration.sql index 62e0c50ba..768f8f58d 100644 --- a/src/Persistence/src/Dao/SQLite/migr/LogbookMigration.sql +++ b/src/Persistence/src/Dao/SQLite/migr/LogbookMigration.sql @@ -1008,7 +1008,7 @@ insert into aircraft_type values update metadata set app_version = '0.17.0'; -@migr(id = "4fda1c12-4c05-4152-af6b-1e495b12492e", descn = "Create attitude table", step = 1) +@migr(id = "4fda1c12-4c05-4152-af6b-1e495b12492e", descn = "Create attitude table", step_cnt = 4) create table attitude ( aircraft_id integer not null, timestamp integer not null, @@ -1023,6 +1023,26 @@ create table attitude ( foreign key(aircraft_id) references aircraft(id) ); +@migr(id = "4fda1c12-4c05-4152-af6b-1e495b12492e", descn = "Migrate data from position to attitute table", step = 2) +insert into attitude (aircraft_id, timestamp, pitch, bank, true_heading, velocity_x, velocity_y, velocity_z, on_ground) +select aircraft_id, timestamp, pitch, bank, true_heading, velocity_x, velocity_y, velocity_z, 0 +from position; + +@migr(id = "4fda1c12-4c05-4152-af6b-1e495b12492e", descn = "Update the on_ground for the first n attitude with timestamp less than one second, based on the aircraft start_on_ground", step = 3) +update attitude +set on_ground = (select start_on_ground + from aircraft a + where a.id = aircraft_id) +where timestamp < 1000; + +@migr(id = "4fda1c12-4c05-4152-af6b-1e495b12492e", descn = "Drop attitude related columns from table position", step = 4) +alter table position drop column pitch; +alter table position drop column bank; +alter table position drop column true_heading; +alter table position drop column velocity_x; +alter table position drop column velocity_y; +alter table position drop column velocity_z; + @migr(id = "80bcc81a-6554-4e05-8631-d17358d9d1dd", descn = "Update application version to 0.18", step = 1) update metadata set app_version = '0.18.0'; From 244aa339ccdcbf038e9daba4307faf21ae1092f2 Mon Sep 17 00:00:00 2001 From: Oliver Knoll Date: Sat, 11 May 2024 19:50:54 +0200 Subject: [PATCH 8/9] Set timestamp when calculating relative position when recording formation flights --- src/Plugins/Module/Formation/src/Formation.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Plugins/Module/Formation/src/Formation.cpp b/src/Plugins/Module/Formation/src/Formation.cpp index fa1a453ed..19d1a0917 100644 --- a/src/Plugins/Module/Formation/src/Formation.cpp +++ b/src/Plugins/Module/Formation/src/Formation.cpp @@ -49,7 +49,7 @@ InitialPosition Formation::calculateInitialRelativePositionToUserAircraft(Horizo std::pair Formation::calculateRelativePositionToUserAircraft(HorizontalDistance horizontalDistance, VerticalDistance verticalDistance, Bearing bearing, std::int64_t timestamp) noexcept { - PositionData initialPosition; + PositionData initialPositionData; AttitudeData attitudeData; const auto &flight = Logbook::getInstance().getCurrentFlight(); @@ -161,13 +161,15 @@ std::pair Formation::calculateRelativePositionToUser } attitudeData = aircraft.getAttitude().interpolate(timestamp, TimeVariableData::Access::DiscreteSeek); bearingDegrees += attitudeData.trueHeading; - SkyMath::Coordinate initial = SkyMath::relativePosition(sourcePosition, bearingDegrees, Convert::feetToMeters(distance)); + SkyMath::Coordinate coordinate = SkyMath::relativePosition(sourcePosition, bearingDegrees, Convert::feetToMeters(distance)); - initialPosition.latitude = initial.first; - initialPosition.longitude = initial.second; - initialPosition.altitude = altitude; + initialPositionData.timestamp = positionData.timestamp; + initialPositionData.latitude = coordinate.first; + initialPositionData.longitude = coordinate.second; + initialPositionData.altitude = altitude; + initialPositionData.indicatedAltitude = altitude; } // position count > 0 - return std::make_pair(initialPosition, attitudeData); + return std::make_pair(initialPositionData, attitudeData); } From da14edcdd42175d0ac60ed03af94ad9de424f0d7 Mon Sep 17 00:00:00 2001 From: Oliver Knoll Date: Sat, 11 May 2024 20:49:05 +0200 Subject: [PATCH 9/9] Show on ground checkbox in aircraft position widget --- CHANGELOG.md | 3 ++ src/Kernel/include/Kernel/SkyMath.h | 18 ++++++++++- src/Model/src/AircraftHandle.cpp | 8 ++--- src/Model/src/Attitude.cpp | 3 ++ src/Model/src/Engine.cpp | 26 +++++++-------- src/Model/src/Light.cpp | 10 ++++-- src/Model/src/SecondaryFlightControl.cpp | 6 ++-- .../src/Widget/AircraftWidget.cpp | 8 +++++ .../src/Widget/AircraftWidget.ui | 20 ++++++++++++ test/KernelTest/src/SkyMathTest.cpp | 32 +++++++++++++++++++ test/KernelTest/src/SkyMathTest.h | 3 ++ 11 files changed, 114 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b7a9ccba..bed132a24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,9 @@ * Existing locations within the given distance are either *updated* * Or the location to be imported is *skipped* (not imported) * Loctations may also be unconditionally imported (*insert*) +- Position- and attitude simulation variables are now sampled separately + * Position data (latitude, longitude, altitude) is only sampled at 1Hz: the expectation is that "stutters" during recording should be automatically smoothened out + * Boolean simulation variables are now interpolated with nearest neighbour interpolation ## Bug Fixes diff --git a/src/Kernel/include/Kernel/SkyMath.h b/src/Kernel/include/Kernel/SkyMath.h index 3127a2079..8a322aa1c 100644 --- a/src/Kernel/include/Kernel/SkyMath.h +++ b/src/Kernel/include/Kernel/SkyMath.h @@ -265,7 +265,7 @@ namespace SkyMath } /*! - * Interpolates between \c p1 and \c p2 and using linear interpolation. + * Interpolates between \c p1 and \c p2 using linear interpolation. * * \param p1 * the first interpolation point @@ -284,6 +284,22 @@ namespace SkyMath } } + /*! + * Interpolates between \c p1 and \c p2 using nearest neighbour interpolation. + * + * \param p1 + * the first interpolation point + * \param p2 + * the second interpolation point + * \param mu + * the interpolation factor in [0.0, 1.0] + */ + template + inline constexpr T interpolateNearestNeighbour(T p1, T p2, U mu) noexcept + { + return mu < 0.5 ? p1 : p2; + } + /*! * Maps the \c position value to a discrete, signed 16bit value. * diff --git a/src/Model/src/AircraftHandle.cpp b/src/Model/src/AircraftHandle.cpp index 13ec90d44..779516e41 100644 --- a/src/Model/src/AircraftHandle.cpp +++ b/src/Model/src/AircraftHandle.cpp @@ -81,10 +81,10 @@ const AircraftHandleData &AircraftHandle::interpolate(std::int64_t timestamp, Ti m_currentData.canopyOpen = SkyMath::interpolateLinear(p1->canopyOpen, p2->canopyOpen, tn); m_currentData.leftWingFolding = SkyMath::interpolateLinear(p1->leftWingFolding, p2->leftWingFolding, tn); m_currentData.rightWingFolding = SkyMath::interpolateLinear(p1->rightWingFolding, p2->rightWingFolding, tn); - m_currentData.gearHandlePosition = p1->gearHandlePosition; - m_currentData.tailhookHandlePosition = p1->tailhookHandlePosition; - m_currentData.foldingWingHandlePosition = p1->foldingWingHandlePosition; - m_currentData.smokeEnabled = p1->smokeEnabled; + m_currentData.gearHandlePosition = SkyMath::interpolateNearestNeighbour(p1->gearHandlePosition, p2->gearHandlePosition, tn); + m_currentData.tailhookHandlePosition = SkyMath::interpolateNearestNeighbour(p1->tailhookHandlePosition, p2->tailhookHandlePosition, tn); + m_currentData.foldingWingHandlePosition = SkyMath::interpolateNearestNeighbour(p1->foldingWingHandlePosition, p2->foldingWingHandlePosition, tn); + m_currentData.smokeEnabled = SkyMath::interpolateNearestNeighbour(p1->smokeEnabled, p2->smokeEnabled, tn); m_currentData.timestamp = adjustedTimestamp; } else { // Certain aircraft override the CANOPY OPEN, so values need to be repeatedly set diff --git a/src/Model/src/Attitude.cpp b/src/Model/src/Attitude.cpp index 89a6dc8ae..3a3889d35 100644 --- a/src/Model/src/Attitude.cpp +++ b/src/Model/src/Attitude.cpp @@ -76,6 +76,9 @@ const AttitudeData &Attitude::interpolate(std::int64_t timestamp, TimeVariableDa m_currentData.velocityBodyY = SkyMath::interpolateLinear(p1->velocityBodyY, p2->velocityBodyY, tn); m_currentData.velocityBodyZ = SkyMath::interpolateLinear(p1->velocityBodyZ, p2->velocityBodyZ, tn); + // On ground + m_currentData.onGround = SkyMath::interpolateNearestNeighbour(p1->onGround, p2->onGround, tn); + m_currentData.timestamp = adjustedTimestamp; } else { // No recorded data, or the timestamp exceeds the timestamp of the last recorded data diff --git a/src/Model/src/Engine.cpp b/src/Model/src/Engine.cpp index 1a6ffb88c..b030d2407 100644 --- a/src/Model/src/Engine.cpp +++ b/src/Model/src/Engine.cpp @@ -89,19 +89,19 @@ const EngineData &Engine::interpolate(std::int64_t timestamp, TimeVariableData:: m_currentData.cowlFlapPosition3 = SkyMath::interpolateLinear(p1->cowlFlapPosition3, p2->cowlFlapPosition3, tn); m_currentData.cowlFlapPosition4 = SkyMath::interpolateLinear(p1->cowlFlapPosition4, p2->cowlFlapPosition4, tn); - // No interpolation for battery and starter/combustion states (boolean) - m_currentData.electricalMasterBattery1 = p1->electricalMasterBattery1; - m_currentData.electricalMasterBattery2 = p1->electricalMasterBattery2; - m_currentData.electricalMasterBattery3 = p1->electricalMasterBattery3; - m_currentData.electricalMasterBattery4 = p1->electricalMasterBattery4; - m_currentData.generalEngineStarter1 = p1->generalEngineStarter1; - m_currentData.generalEngineStarter2 = p1->generalEngineStarter2; - m_currentData.generalEngineStarter3 = p1->generalEngineStarter3; - m_currentData.generalEngineStarter4 = p1->generalEngineStarter4; - m_currentData.generalEngineCombustion1 = p1->generalEngineCombustion1; - m_currentData.generalEngineCombustion2 = p1->generalEngineCombustion2; - m_currentData.generalEngineCombustion3 = p1->generalEngineCombustion3; - m_currentData.generalEngineCombustion4 = p1->generalEngineCombustion4; + // Nearest neighbour interpolation for battery and starter/combustion states (boolean) + m_currentData.electricalMasterBattery1 = SkyMath::interpolateNearestNeighbour(p1->electricalMasterBattery1, p2->electricalMasterBattery1, tn); + m_currentData.electricalMasterBattery2 = SkyMath::interpolateNearestNeighbour(p1->electricalMasterBattery2, p2->electricalMasterBattery2, tn); + m_currentData.electricalMasterBattery3 = SkyMath::interpolateNearestNeighbour(p1->electricalMasterBattery3, p2->electricalMasterBattery3, tn); + m_currentData.electricalMasterBattery4 = SkyMath::interpolateNearestNeighbour(p1->electricalMasterBattery4, p2->electricalMasterBattery4, tn); + m_currentData.generalEngineStarter1 = SkyMath::interpolateNearestNeighbour(p1->generalEngineStarter1, p2->generalEngineStarter1, tn); + m_currentData.generalEngineStarter2 = SkyMath::interpolateNearestNeighbour(p1->generalEngineStarter2, p2->generalEngineStarter2, tn); + m_currentData.generalEngineStarter3 = SkyMath::interpolateNearestNeighbour(p1->generalEngineStarter3, p2->generalEngineStarter3, tn); + m_currentData.generalEngineStarter4 = SkyMath::interpolateNearestNeighbour(p1->generalEngineStarter4, p2->generalEngineStarter4, tn); + m_currentData.generalEngineCombustion1 = SkyMath::interpolateNearestNeighbour(p1->generalEngineCombustion1, p2->generalEngineCombustion1, tn); + m_currentData.generalEngineCombustion2 = SkyMath::interpolateNearestNeighbour(p1->generalEngineCombustion2, p2->generalEngineCombustion2, tn); + m_currentData.generalEngineCombustion3 = SkyMath::interpolateNearestNeighbour(p1->generalEngineCombustion3, p2->generalEngineCombustion3, tn); + m_currentData.generalEngineCombustion4 = SkyMath::interpolateNearestNeighbour(p1->generalEngineCombustion4, p2->generalEngineCombustion4, tn); m_currentData.timestamp = adjustedTimestamp; } else { // No recorded data, or the timestamp exceeds the timestamp of the last recorded data diff --git a/src/Model/src/Light.cpp b/src/Model/src/Light.cpp index 1667b0096..ba525c211 100644 --- a/src/Model/src/Light.cpp +++ b/src/Model/src/Light.cpp @@ -47,11 +47,14 @@ const LightData &Light::interpolate(std::int64_t timestamp, TimeVariableData::Ac if (getCurrentTimestamp() != adjustedTimestamp || getCurrentAccess() != access) { int currentIndex = getCurrentIndex(); + double tn {0.0}; switch (access) { case TimeVariableData::Access::Linear: [[fallthrough]]; case TimeVariableData::Access::NoTimeOffset: - SkySearch::getLinearInterpolationSupportData(getData(), adjustedTimestamp, SkySearch::DefaultInterpolationWindow, currentIndex, &p1, &p2); + if (SkySearch::getLinearInterpolationSupportData(getData(), adjustedTimestamp, SkySearch::DefaultInterpolationWindow, currentIndex, &p1, &p2)) { + tn = SkySearch::normaliseTimestamp(*p1, *p2, adjustedTimestamp); + } break; case TimeVariableData::Access::DiscreteSeek: [[fallthrough]]; @@ -62,6 +65,7 @@ const LightData &Light::interpolate(std::int64_t timestamp, TimeVariableData::Ac if (currentIndex != SkySearch::InvalidIndex) { p1 = &getData().at(currentIndex); p2 = p1; + tn = 0.0; } else { p1 = p2 = nullptr; } @@ -69,8 +73,8 @@ const LightData &Light::interpolate(std::int64_t timestamp, TimeVariableData::Ac } if (p1 != nullptr) { - // No interpolation for light states - m_currentData.lightStates = p1->lightStates; + // Nearest neighbour interpolation for light states (boolean) + m_currentData.lightStates = SkyMath::interpolateNearestNeighbour(p1->lightStates, p2->lightStates, tn); m_currentData.timestamp = adjustedTimestamp; } else { // No recorded data, or the timestamp exceeds the timestamp of the last recorded data diff --git a/src/Model/src/SecondaryFlightControl.cpp b/src/Model/src/SecondaryFlightControl.cpp index 4d17c9ee7..f3487100a 100644 --- a/src/Model/src/SecondaryFlightControl.cpp +++ b/src/Model/src/SecondaryFlightControl.cpp @@ -80,8 +80,10 @@ const SecondaryFlightControlData &SecondaryFlightControl::interpolate(std::int64 m_currentData.leftSpoilersPosition = SkyMath::interpolateLinear(p1->leftSpoilersPosition, p2->leftSpoilersPosition, tn); m_currentData.rightSpoilersPosition = SkyMath::interpolateLinear(p1->rightSpoilersPosition, p2->rightSpoilersPosition, tn); m_currentData.spoilersHandlePercent = SkyMath::interpolateLinear(p1->spoilersHandlePercent, p2->spoilersHandlePercent, tn); - m_currentData.flapsHandleIndex = p1->flapsHandleIndex; - m_currentData.spoilersArmed = p1->spoilersArmed; + + // Nearest neighbour interpolation (boolean) + m_currentData.flapsHandleIndex = SkyMath::interpolateNearestNeighbour(p1->flapsHandleIndex, p2->flapsHandleIndex, tn); + m_currentData.spoilersArmed = SkyMath::interpolateNearestNeighbour(p1->spoilersArmed, p2->spoilersArmed, tn); m_currentData.timestamp = adjustedTimestamp; } else { // No recorded data (and no repeat), or the timestamp exceeds the timestamp of the last recorded data diff --git a/src/UserInterface/src/Widget/AircraftWidget.cpp b/src/UserInterface/src/Widget/AircraftWidget.cpp index e8ba20b21..d84ce1e23 100644 --- a/src/UserInterface/src/Widget/AircraftWidget.cpp +++ b/src/UserInterface/src/Widget/AircraftWidget.cpp @@ -94,6 +94,7 @@ void AircraftWidget::updateUi(std::int64_t timestamp, TimeVariableData::Access a } if (!attitude.isNull()) { + // Attitude ui->pitchLineEdit->setText(d->unit.formatDegrees(attitude.pitch)); ui->bankLineEdit->setText(d->unit.formatDegrees(attitude.bank)); ui->headingLineEdit->setText(d->unit.formatDegrees(attitude.trueHeading)); @@ -109,6 +110,8 @@ void AircraftWidget::updateUi(std::int64_t timestamp, TimeVariableData::Access a speedKnots = Convert::feetPerSecondToKnots(speedFeetPerSec); ui->velocityZLineEdit->setText(d->unit.formatKnots(speedKnots) % " (" % d->unit.formatSpeedInFeetPerSecond(speedFeetPerSec) % ")"); + ui->onGroundCheckBox->setChecked(attitude.onGround); + attitudeColorName = d->ActiveTextColor.name(); } else { attitudeColorName = d->DisabledTextColor.name(); @@ -128,6 +131,7 @@ void AircraftWidget::updateUi(std::int64_t timestamp, TimeVariableData::Access a ui->velocityXLineEdit->setStyleSheet(attitudeCss); ui->velocityYLineEdit->setStyleSheet(attitudeCss); ui->velocityZLineEdit->setStyleSheet(attitudeCss); + ui->onGroundCheckBox->setStyleSheet(attitudeCss); } // PRIVATE @@ -147,6 +151,10 @@ void AircraftWidget::initUi() noexcept ui->velocityXLineEdit->setToolTip(QString::fromLatin1(SimVar::VelocityBodyX)); ui->velocityYLineEdit->setToolTip(QString::fromLatin1(SimVar::VelocityBodyY)); ui->velocityZLineEdit->setToolTip(QString::fromLatin1(SimVar::VelocityBodyZ)); + + ui->onGroundCheckBox->setToolTip(QString::fromLatin1(SimVar::SimOnGround)); + ui->onGroundCheckBox->setAttribute(Qt::WA_TransparentForMouseEvents, true); + ui->onGroundCheckBox->setFocusPolicy(Qt::NoFocus); } std::pair AircraftWidget::getCurrentPositionData(std::int64_t timestamp, TimeVariableData::Access access) const noexcept diff --git a/src/UserInterface/src/Widget/AircraftWidget.ui b/src/UserInterface/src/Widget/AircraftWidget.ui index 3d3f882f9..b7a536383 100644 --- a/src/UserInterface/src/Widget/AircraftWidget.ui +++ b/src/UserInterface/src/Widget/AircraftWidget.ui @@ -92,6 +92,26 @@ + + + + On ground + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + diff --git a/test/KernelTest/src/SkyMathTest.cpp b/test/KernelTest/src/SkyMathTest.cpp index 8c9a8afc1..ce61cf27e 100644 --- a/test/KernelTest/src/SkyMathTest.cpp +++ b/test/KernelTest/src/SkyMathTest.cpp @@ -315,6 +315,38 @@ void SkyMathTest::interpolateHermite360() QCOMPARE(result, expected); } +void SkyMathTest::interpolateNearestNeighbour_data() +{ + QTest::addColumn("p0"); + QTest::addColumn("p1"); + QTest::addColumn("mu"); + QTest::addColumn("expected"); + + QTest::newRow("First") << 1.0 << 2.0 << 0.1 << 1.0; + QTest::newRow("Second") << 1.0 << 2.0 << 0.5 << 2.0; + QTest::newRow("Negative time") << 1.0 << 2.0 << -1.0 << 1.0; + QTest::newRow("Exceeding time") << 1.0 << 2.0 << 2.0 << 2.0; + QTest::newRow("First negative") << -1.0 << 2.0 << 0.1 << -1.0; + QTest::newRow("Second negative") << 1.0 << -2.0 << 0.5 << -2.0; + QTest::newRow("First boundary") << 1.0 << 2.0 << 0.0 << 1.0; + QTest::newRow("Second boundary") << 1.0 << 2.0 << 1.0 << 2.0; +} + +void SkyMathTest::interpolateNearestNeighbour() +{ + // Setup + QFETCH(double, p0); + QFETCH(double, p1); + QFETCH(double, mu); + QFETCH(double, expected); + + // Exercise + double result = SkyMath::interpolateNearestNeighbour(p0, p1, mu); + + // Verify + QCOMPARE(result, expected); +} + void SkyMathTest::fromPosition_data() { QTest::addColumn("p"); diff --git a/test/KernelTest/src/SkyMathTest.h b/test/KernelTest/src/SkyMathTest.h index e5e93600a..ab6e75306 100644 --- a/test/KernelTest/src/SkyMathTest.h +++ b/test/KernelTest/src/SkyMathTest.h @@ -43,6 +43,9 @@ private slots: void interpolateHermite360_data(); void interpolateHermite360(); + void interpolateNearestNeighbour_data(); + void interpolateNearestNeighbour(); + void fromPosition_data(); void fromPosition();