Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/separate aircraft attitude #147

Merged
merged 9 commits into from
May 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
126 changes: 73 additions & 53 deletions src/Flight/src/FlightAugmentation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
#include <Model/Aircraft.h>
#include <Model/Position.h>
#include <Model/PositionData.h>
#include <Model/Attitude.h>
#include <Model/AttitudeData.h>
#include <Model/AircraftHandle.h>
#include <Model/AircraftHandleData.h>
#include <Model/Engine.h>
Expand All @@ -39,6 +41,7 @@
#include <Model/SecondaryFlightControlData.h>
#include <Model/Light.h>
#include <Model/LightData.h>
#include <Model/TimeVariableData.h>
#include "Analytics.h"
#include "FlightAugmentation.h"

Expand Down Expand Up @@ -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 &currentPositionData = 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
Expand Down Expand Up @@ -249,7 +268,7 @@ void FlightAugmentation::augmentStartProcedure(Aircraft &aircraft) noexcept

// Engine

Engine &engine = aircraft.getEngine();
auto &engine = aircraft.getEngine();
EngineData engineData;

// 0 seconds
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -366,7 +385,7 @@ void FlightAugmentation::augmentStartProcedure(Aircraft &aircraft) noexcept

// Handles & gear

AircraftHandle &aircraftHandle = aircraft.getAircraftHandle();
auto &aircraftHandle = aircraft.getAircraftHandle();
AircraftHandleData handleData;

// 0 seconds
Expand All @@ -383,7 +402,7 @@ void FlightAugmentation::augmentStartProcedure(Aircraft &aircraft) noexcept

// Lights

Light &light = aircraft.getLight();
auto &light = aircraft.getLight();
LightData lightData;

// 0 seconds
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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;
}
}
Expand Down
18 changes: 17 additions & 1 deletion src/Kernel/include/Kernel/SkyMath.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 <typename T, typename U>
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.
*
Expand Down
3 changes: 2 additions & 1 deletion src/Model/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -52,7 +54,6 @@ target_include_directories(${LIBRARY_NAME}
target_link_libraries(${LIBRARY_NAME}
PUBLIC
Qt6::Core
Qt6::Sql
PRIVATE
Sky::Kernel
)
Expand Down
5 changes: 5 additions & 0 deletions src/Model/include/Model/AbstractComponent.h
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
2 changes: 2 additions & 0 deletions src/Model/include/Model/Aircraft.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class QDateTime;
#include "Engine.h"

class Position;
class Attitude;
class PrimaryFlightControl;
class SecondaryFlightControl;
class AircraftHandle;
Expand All @@ -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;
Expand Down
Loading
Loading