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

added damping sine wave to model nose wheel bump on touchdown #32

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/cameracommands/cameracommand.cpp
Expand Up @@ -56,6 +56,11 @@ void CameraCommand::on_disable()

}

void CameraCommand::on_receiving_message(XPLMPluginID, int, void*)
{

}

float CameraCommand::get_blend_ratio() const
{
return mBlendTime / 2;
Expand Down
3 changes: 3 additions & 0 deletions src/cameracommands/cameracommand.h
@@ -1,6 +1,8 @@
#ifndef CAMERACOMMAND_H
#define CAMERACOMMAND_H

#include <CHeaders/XPLM/XPLMDefs.h>

#include "interfaces/ivisitable.h"
#include "cameraposition.h"

Expand All @@ -19,6 +21,7 @@ class CameraCommand : public IVisitable
virtual void on_view_changed(int);
virtual void on_enable();
virtual void on_disable();
virtual void on_receiving_message(XPLMPluginID, int, void*);
void reset_blend();
protected:
float get_blend_ratio() const;
Expand Down
231 changes: 227 additions & 4 deletions src/cameracommands/touchdowncameracommand.cpp
Expand Up @@ -3,17 +3,24 @@
#include <cmath>
#endif

#include <string>
#include <cstdint>
#include <climits>

#include <CHeaders/XPLM/XPLMDataAccess.h>
#include <CHeaders/XPLM/XPLMUtilities.h>
#include <CHeaders/XPLM/XPLMProcessing.h>
#include <CHeaders/XPLM/XPLMPlugin.h>

#include "cameracommands/touchdowncameracommand.h"
#include "helpers.h"
#include "cameraposition.h"
#include "interfaces/ivisitor.h"

#define MSG_ADD_DATAREF 0x01000000

TouchdownCameraCommand::TouchdownCameraCommand()
{
{
mVerticalSpeedDataRef = XPLMFindDataRef("sim/flightmodel/position/vh_ind_fpm");
mOnGroundDataRef = XPLMFindDataRef("sim/flightmodel/failures/onground_any");
mRadioAltDataRef = XPLMFindDataRef("sim/flightmodel/position/y_agl");
Expand All @@ -24,30 +31,141 @@ TouchdownCameraCommand::TouchdownCameraCommand()
mResponse = 25;
mLastPitch = 0;
mLastRoll = 0;
mLastX = 0;
mLastX = 0;

// Nose bump
mNoseWheelPrevOnGround = true;
noseWheelPosition = 0;
mGearsOnGroundDataRef = XPLMFindDataRef("sim/flightmodel2/gear/on_ground");
mWheelZPositionsDataRef = XPLMFindDataRef("sim/aircraft/parts/acf_gear_znodef");
mTrueThetaDataRef = XPLMFindDataRef("sim/flightmodel2/position/true_theta");
mLastY = 0;
noseWheelTouchdownTime = 0;
bumpInitialAmplitude = 0.02f;
bumpDecayRate = 1.5f;
bumpFrequency = 1.0f;
bumpRollInitialAmplitude = 0.01f;
bumpRollDecayRate = 1.5f;
bumpRollFrequency = 1.0f;
lastSavedPitchAngle = 0;
pitchAngleCounter = 0;
lastSavedPitchAngleTime = 0;
}

TouchdownCameraCommand::~TouchdownCameraCommand()
{
//dtor
}

void TouchdownCameraCommand::on_enable()
{
int datarefEditorId = XPLMFindPluginBySignature("xplanesdk.examples.DataRefEditor");

mBumpInitialAmplitude = XPLMRegisterDataAccessor(
"simcoders/headshake/touchdowncamera/bump_initial_amplitude", xplmType_Float, 1, NULL, NULL,
[](void* refCon) -> float {
if (refCon) return reinterpret_cast<TouchdownCameraCommand*>(refCon)-> bumpInitialAmplitude;
return 0.01f;
}, [](void* refCon, float value) -> void {
if (refCon) {
reinterpret_cast<TouchdownCameraCommand*>(refCon)-> bumpInitialAmplitude = value;
}
}, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, this, this);
XPLMSendMessageToPlugin(datarefEditorId, MSG_ADD_DATAREF, (void*)"simcoders/headshake/touchdowncamera/bump_initial_amplitude");

mBumpDecayRate = XPLMRegisterDataAccessor(
"simcoders/headshake/touchdowncamera/bump_decay_rate", xplmType_Float, 1, NULL, NULL,
[](void* refCon) -> float {
if (refCon) return reinterpret_cast<TouchdownCameraCommand*>(refCon)-> bumpDecayRate;
return 0.01f;
}, [](void* refCon, float value) -> void {
if (refCon) {
reinterpret_cast<TouchdownCameraCommand*>(refCon)-> bumpDecayRate = value;
}
}, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, this, this);
XPLMSendMessageToPlugin(datarefEditorId, MSG_ADD_DATAREF, (void*)"simcoders/headshake/touchdowncamera/bump_decay_rate");

mBumpFrequency = XPLMRegisterDataAccessor(
"simcoders/headshake/touchdowncamera/bump_frequency", xplmType_Float, 1, NULL, NULL,
[](void* refCon) -> float {
if (refCon) return reinterpret_cast<TouchdownCameraCommand*>(refCon)-> bumpFrequency;
return 0.01f;
}, [](void* refCon, float value) -> void {
if (refCon) {
reinterpret_cast<TouchdownCameraCommand*>(refCon)-> bumpFrequency = value;
}
}, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, this, this);
XPLMSendMessageToPlugin(datarefEditorId, MSG_ADD_DATAREF, (void*)"simcoders/headshake/touchdowncamera/bump_frequency");

// bump roll

mBumpRollInitialAmplitude = XPLMRegisterDataAccessor(
"simcoders/headshake/touchdowncamera/bump_roll_initial_amplitude", xplmType_Float, 1, NULL, NULL,
[](void* refCon) -> float {
if (refCon) return reinterpret_cast<TouchdownCameraCommand*>(refCon)-> bumpRollInitialAmplitude;
return 0.01f;
}, [](void* refCon, float value) -> void {
if (refCon) {
reinterpret_cast<TouchdownCameraCommand*>(refCon)-> bumpRollInitialAmplitude = value;
}
}, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, this, this);
XPLMSendMessageToPlugin(datarefEditorId, MSG_ADD_DATAREF, (void*)"simcoders/headshake/touchdowncamera/bump_roll_initial_amplitude");

mBumpDecayRate = XPLMRegisterDataAccessor(
"simcoders/headshake/touchdowncamera/bump_roll_decay_rate", xplmType_Float, 1, NULL, NULL,
[](void* refCon) -> float {
if (refCon) return reinterpret_cast<TouchdownCameraCommand*>(refCon)-> bumpRollDecayRate;
return 0.01f;
}, [](void* refCon, float value) -> void {
if (refCon) {
reinterpret_cast<TouchdownCameraCommand*>(refCon)-> bumpRollDecayRate = value;
}
}, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, this, this);
XPLMSendMessageToPlugin(datarefEditorId, MSG_ADD_DATAREF, (void*)"simcoders/headshake/touchdowncamera/bump_roll_decay_rate");

mBumpFrequency = XPLMRegisterDataAccessor(
"simcoders/headshake/touchdowncamera/bump_roll_frequency", xplmType_Float, 1, NULL, NULL,
[](void* refCon) -> float {
if (refCon) return reinterpret_cast<TouchdownCameraCommand*>(refCon)-> bumpRollFrequency;
return 0.01f;
}, [](void* refCon, float value) -> void {
if (refCon) {
reinterpret_cast<TouchdownCameraCommand*>(refCon)-> bumpRollFrequency = value;
}
}, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, this, this);
XPLMSendMessageToPlugin(datarefEditorId, MSG_ADD_DATAREF, (void*)"simcoders/headshake/touchdowncamera/bump_roll_frequency");
}

void TouchdownCameraCommand::on_disable()
{
XPLMUnregisterDataAccessor(mBumpInitialAmplitude);
XPLMUnregisterDataAccessor(mBumpDecayRate);
XPLMUnregisterDataAccessor(mBumpFrequency);

XPLMUnregisterDataAccessor(mBumpRollInitialAmplitude);
XPLMUnregisterDataAccessor(mBumpRollDecayRate);
XPLMUnregisterDataAccessor(mBumpRollFrequency);
}

void TouchdownCameraCommand::execute(CameraPosition &position, float elapsedTime)
{
CameraCommand::execute(position, elapsedTime);

float currentTime, touchdownshake, fastshake;
const double PI = 3.141592653589793238463;

// Restore the initial position
position.pitch -= mLastPitch;
position.roll -= mLastRoll;
position.x -= mLastX;
position.y -= mLastY;

// Reset the state vars
mLastPitch = 0;
mLastRoll = 0;
mLastX = 0;

mLastY = 0;

// Exit if disabled
if (!pEnabled)
Expand All @@ -56,8 +174,10 @@ void TouchdownCameraCommand::execute(CameraPosition &position, float elapsedTime
// Reset if not on ground
if (!XPLMGetDatai(mOnGroundDataRef)) {
// Reset only after climbing 1m agl
if (XPLMGetDataf(mRadioAltDataRef) > 1)
if (XPLMGetDataf(mRadioAltDataRef) > 1) {
mPrevOnGround = false;
mNoseWheelPrevOnGround = false;
}
}

// Touchdown effect
Expand All @@ -82,9 +202,60 @@ void TouchdownCameraCommand::execute(CameraPosition &position, float elapsedTime
mLastPitch = touchdownshake * 20.0f;
}
}

// Front wheel bump effect

if(noseWheelPosition >= 0) // if it's not a taildragger
{
XPLMGetDatavi(mGearsOnGroundDataRef, wheelsOnGround, 0, noseWheelPosition + 1);
bool noseWheelOnGround = wheelsOnGround[noseWheelPosition] == 1;

if(!mNoseWheelPrevOnGround && noseWheelOnGround)
{
XPLMDebugString("Bump Effect - Nose wheel touched down.\n");
mNoseWheelPrevOnGround = true;
noseWheelTouchdownTime = XPLMGetElapsedTime();
}
else
{
float timeSinceNoseWheelTouchdown = XPLMGetElapsedTime() - noseWheelTouchdownTime;
// saving plane pitch every 100 (kinda arbitrary) flight loops
if(!noseWheelOnGround)
{
if(pitchAngleCounter == 100)
{
pitchAngleCounter = 0;
lastSavedPitchAngle = XPLMGetDataf(mTrueThetaDataRef);
lastSavedPitchAngleTime = XPLMGetElapsedTime();
}
else
{
pitchAngleCounter++;

}
}

if (timeSinceNoseWheelTouchdown > 0 && timeSinceNoseWheelTouchdown < 1.5f)
{
float verticalSpeedCoeff = std::abs(lastSavedPitchAngle / (noseWheelTouchdownTime - lastSavedPitchAngleTime));
mLastY = - verticalSpeedCoeff * bumpInitialAmplitude * exp(-bumpDecayRate * timeSinceNoseWheelTouchdown) * std::sin(2 * PI * bumpFrequency * timeSinceNoseWheelTouchdown);
mLastRoll = - verticalSpeedCoeff * bumpRollInitialAmplitude * exp(-bumpRollDecayRate * timeSinceNoseWheelTouchdown) * std::sin(2 * PI * bumpRollFrequency * timeSinceNoseWheelTouchdown);
std::string s = "Bump Effect - Time since touch down: " + std::to_string(timeSinceNoseWheelTouchdown) + " Y offset = " + std::to_string(mLastY) + " Roll = " + std::to_string(mLastRoll) + " Vertical Speed Coefficient = " + std::to_string(verticalSpeedCoeff) + "\n";
XPLMDebugString(s.c_str());
}
else
{
timeSinceNoseWheelTouchdown = 0;
noseWheelTouchdownTime = 0;
}
}
}

position.pitch += mLastPitch;
position.roll += mLastRoll;
position.x += mLastX;

position.y += mLastY;
}

void TouchdownCameraCommand::accept(IVisitor &visitor)
Expand All @@ -101,3 +272,55 @@ float TouchdownCameraCommand::get_response()
{
return mResponse;
}

void TouchdownCameraCommand::on_receiving_message(XPLMPluginID pluginId, int message, void* param)
{
std::string ms = "Bump Effect - Received message from plugin " + std::to_string(pluginId) + " with message: " + std::to_string(message) + "\n";
XPLMDebugString(ms.c_str());

if(pluginId == XPLM_PLUGIN_XPLANE && message == XPLM_MSG_PLANE_LOADED)
{
int isUserPlane = (intptr_t) param;

if(isUserPlane == 0)
{
float wheelZPositions[10];
XPLMGetDatavf(mWheelZPositionsDataRef, wheelZPositions, 0, 10);
noseWheelPosition = locateNoseWheelPosition(wheelZPositions);
std::string s = "Bump Effect - Nose wheel found at position " + std::to_string(noseWheelPosition) + "\n";
XPLMDebugString(s.c_str());
}
}
}

short TouchdownCameraCommand::locateNoseWheelPosition(float (&wheelZPositions)[10])
{
// the nose gear should be the one with the smallest z coordinate (in relation to the CG)
// but we still need to exclude taildraggers and we do it by checking that there's no other gear at the same Z coordinate
// because this would imply that there are two gears at the same (smallest) Z coordinate, which in turn means there isn't a single
// nose gear, but a couple -> taildraggers
// a return value of -1 means that the aircraft is a taildragger

short noseWheelPositionInArray = 0;
float noseWheelZPosition = INT_MAX;
bool moreThanOneGearAtSameZ = false;

for(short i = 0; i < 10; i++)
{
std::string s = "Bump Effect - Cycling through the wheel array for the Z axis. Position: " + std::to_string(i) + " Value: " + std::to_string(wheelZPositions[i]) + "\n";
XPLMDebugString(s.c_str());

if(std::fabs(noseWheelZPosition - wheelZPositions[i]) <= 1.0e-05)
{
moreThanOneGearAtSameZ = true;
}
else if(noseWheelZPosition > wheelZPositions[i])
{
noseWheelZPosition = wheelZPositions[i];
noseWheelPositionInArray = i;
moreThanOneGearAtSameZ = false;
}
}

return moreThanOneGearAtSameZ ? -1 : noseWheelPositionInArray;
}
32 changes: 32 additions & 0 deletions src/cameracommands/touchdowncameracommand.h
Expand Up @@ -17,6 +17,18 @@ class TouchdownCameraCommand : public CameraCommand
/** Implementation methods */
void set_response(float);
float get_response();

// Nose bump
XPLMDataRef mBumpInitialAmplitude;
XPLMDataRef mBumpDecayRate;
XPLMDataRef mBumpFrequency;
XPLMDataRef mBumpRollInitialAmplitude;
XPLMDataRef mBumpRollDecayRate;
XPLMDataRef mBumpRollFrequency;
void on_enable() override;
void on_disable() override;
void on_receiving_message(XPLMPluginID, int, void*) override;

protected:
private:
bool mPrevOnGround;
Expand All @@ -27,9 +39,29 @@ class TouchdownCameraCommand : public CameraCommand
float mLastPitch;
float mLastRoll;
float mLastX;
float mLastY;
XPLMDataRef mVerticalSpeedDataRef;
XPLMDataRef mOnGroundDataRef;
XPLMDataRef mRadioAltDataRef;

// Nose bump
float bumpInitialAmplitude;
float bumpDecayRate;
float bumpFrequency;
float bumpRollInitialAmplitude;
float bumpRollDecayRate;
float bumpRollFrequency;
float noseWheelTouchdownTime;
bool mNoseWheelPrevOnGround;
int wheelsOnGround[10];
XPLMDataRef mGearsOnGroundDataRef;
XPLMDataRef mWheelZPositionsDataRef;
XPLMDataRef mTrueThetaDataRef;
short locateNoseWheelPosition(float (&wheelZPositions)[10]);
short noseWheelPosition;
float lastSavedPitchAngle;
float lastSavedPitchAngleTime;
short pitchAngleCounter;
};

#endif // TOUCHDOWNCAMERACOMMAND_H
8 changes: 8 additions & 0 deletions src/cameracontrol.cpp
Expand Up @@ -407,6 +407,14 @@ void CameraControl::reset_view()
}
}

void CameraControl::on_receiving_message(XPLMPluginID pluginId, int message, void* param)
{
// Send the on_receiving_message to the commands
for (auto command : mCommands) {
command->on_receiving_message(pluginId, message, param);
}
}

void CameraControl::freeze()
{
mFreezed1 = true;
Expand Down