WIP. Added templated RK4 solver and some more framework code

This commit is contained in:
Travis Hunter 2023-04-02 09:42:32 -06:00
parent 6e00f22ce4
commit 39920e2777
12 changed files with 238 additions and 64 deletions

View File

@ -82,15 +82,27 @@ void MainWindow::on_testButton2_clicked()
double ts = 0.01; double ts = 0.01;
sim::RK4Solver velXSolver([=](double x, double t) -> double { return 0.0; }); // X position/velocity. x[0] is X position, x[1] is X velocity
velXSolver.setTimeStep(ts); double x[2] = {0.0, initialVelocityX};
sim::RK4Solver velYSolver([=](double y, double t) -> double { return -9.8; });
velYSolver.setTimeStep(ts);
sim::RK4Solver posXSolver([=](double x, double t) -> double { return velXSolver.step(x, t); }); // Y position/velocity. y[0] is Y position, y[1] is Y velocity
posXSolver.setTimeStep(ts); double y[2] = {0.0, initialVelocityY};
sim::RK4Solver posYSolver([=](double y, double t) -> double { return velYSolver.step(x, t); });
posYSolver.setTimeStep(ts); auto xvelODE = [mass, dragCoeff](double, double* x) -> double
{
return -dragCoeff * 1.225 * 0.00774192 / (2.0 * mass) * x[1]*x[1]; };
auto xposODE = [](double, double* x) -> double { return x[1]; };
sim::RK4Solver xSolver(xposODE, xvelODE);
xSolver.setTimeStep(0.01);
auto yvelODE = [mass, dragCoeff](double, double* y) -> double
{
return -dragCoeff * 1.225 * 0.00774192 / (2.0 * mass) * y[1]*y[1] - 9.8; };
auto yposODE = [](double, double* y) -> double { return y[1]; };
sim::RK4Solver ySolver(yposODE, yvelODE);
ySolver.setTimeStep(0.01);
// These can be solved independently for now. Maybe figure out how to merge them later // These can be solved independently for now. Maybe figure out how to merge them later
@ -98,33 +110,42 @@ void MainWindow::on_testButton2_clicked()
QTextStream cout(stdout); QTextStream cout(stdout);
cout << "Initial X velocity: " << initialVelocityX << "\n"; cout << "Initial X velocity: " << initialVelocityX << "\n";
cout << "Initial Y velocity: " << initialVelocityY << "\n"; cout << "Initial Y velocity: " << initialVelocityY << "\n";
double resX[2];
double resY[2];
for(size_t i = 0; i < maxTs; ++i) for(size_t i = 0; i < maxTs; ++i)
{ {
position.emplace_back(posXSolver.step(position[i].getX1(), i * ts), xSolver.step(i * ts, x, resX);
posYSolver.step(position[i].getX2(), i * ts), ySolver.step(i * ts, y, resY);
0.0); position.emplace_back(resX[0], resY[0], 0.0);
x[0] = resX[0];
x[1] = resX[1];
y[0] = resY[0];
y[1] = resY[1];
cout << "(" << position[i].getX1() << ", " << position[i].getX2() << ")\n"; cout << "(" << position[i].getX1() << ", " << position[i].getX2() << ")\n";
if(y[0] < 0.0)
break;
} }
auto& plot = ui->plotWindow; auto& plot = ui->plotWindow;
// generate some data: // generate some data:
QVector<double> x(position.size()), y(position.size()); QVector<double> xData(position.size()), yData(position.size());
for (int i = 0; i < x.size(); ++i) for (int i = 0; i < xData.size(); ++i)
{ {
x[i] = position[i].getX1(); xData[i] = position[i].getX1();
y[i] = position[i].getX2(); yData[i] = position[i].getX2();
} }
// create graph and assign data to it: // create graph and assign data to it:
plot->addGraph(); plot->addGraph();
plot->graph(0)->setData(x, y); plot->graph(0)->setData(xData, yData);
// give the axes some labels: // give the axes some labels:
plot->xAxis->setLabel("x"); plot->xAxis->setLabel("x");
plot->yAxis->setLabel("y"); plot->yAxis->setLabel("y");
// set axes ranges, so we see all data: // set axes ranges, so we see all data:
plot->xAxis->setRange(*std::min_element(std::begin(x), std::end(x)), *std::max_element(std::begin(x), std::end(x))); plot->xAxis->setRange(*std::min_element(std::begin(xData), std::end(xData)), *std::max_element(std::begin(xData), std::end(xData)));
plot->yAxis->setRange(*std::min_element(std::begin(y), std::end(y)), *std::max_element(std::begin(y), std::end(y))); plot->yAxis->setRange(*std::min_element(std::begin(yData), std::end(yData)), *std::max_element(std::begin(yData), std::end(yData)));
plot->replot(); plot->replot();
} }

View File

@ -99,7 +99,7 @@
<string>Calculate Ballistic Trajectory</string> <string>Calculate Ballistic Trajectory</string>
</property> </property>
</widget> </widget>
<widget class="QWidget" name=""> <widget class="QWidget" name="layoutWidget">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>260</x> <x>260</x>
@ -117,7 +117,11 @@
</widget> </widget>
</item> </item>
<item row="0" column="1"> <item row="0" column="1">
<widget class="QLineEdit" name="initialVelocity"/> <widget class="QLineEdit" name="initialVelocity">
<property name="text">
<string>500.0</string>
</property>
</widget>
</item> </item>
<item row="1" column="0"> <item row="1" column="0">
<widget class="QLabel" name="label_2"> <widget class="QLabel" name="label_2">
@ -127,7 +131,11 @@
</widget> </widget>
</item> </item>
<item row="1" column="1"> <item row="1" column="1">
<widget class="QLineEdit" name="initialAngle"/> <widget class="QLineEdit" name="initialAngle">
<property name="text">
<string>45.0</string>
</property>
</widget>
</item> </item>
<item row="2" column="0"> <item row="2" column="0">
<widget class="QLabel" name="label_3"> <widget class="QLabel" name="label_3">
@ -137,7 +145,11 @@
</widget> </widget>
</item> </item>
<item row="2" column="1"> <item row="2" column="1">
<widget class="QLineEdit" name="mass"/> <widget class="QLineEdit" name="mass">
<property name="text">
<string>1.0</string>
</property>
</widget>
</item> </item>
<item row="3" column="0"> <item row="3" column="0">
<widget class="QLabel" name="label_4"> <widget class="QLabel" name="label_4">
@ -147,7 +159,11 @@
</widget> </widget>
</item> </item>
<item row="3" column="1"> <item row="3" column="1">
<widget class="QLineEdit" name="dragCoeff"/> <widget class="QLineEdit" name="dragCoeff">
<property name="text">
<string>0.5</string>
</property>
</widget>
</item> </item>
</layout> </layout>
</widget> </widget>

6
model/Rocket.cpp Normal file
View File

@ -0,0 +1,6 @@
#include "Rocket.h"
Rocket::Rocket()
{
}

43
model/Rocket.h Normal file
View File

@ -0,0 +1,43 @@
#ifndef ROCKET_H
#define ROCKET_H
#include "utils/math/Vector3.h"
#include "utils/math/Quaternion.h"
#include "sim/Propagator.h"
#include <utility> // std::move
#include <memory>
class Rocket
{
public:
Rocket();
void setPosition(const math::Vector3& pos) { *position = pos; }
void setPosition(math::Vector3&& pos) { *position = std::move(pos); }
void setVelocity(const math::Vector3& vel) { *velocity = vel; }
void setVelocity(math::Vector3&& vel) { *velocity = std::move(vel); }
void setOrientation(const math::Quaternion& ori) { *orientation = ori; }
void setOrientation(math::Quaternion&& ori) { *orientation = std::move(ori); }
void setOrientationRate(const math::Quaternion& ori) { *orientationRate = ori; }
void setOrientationRate(math::Quaternion&& ori) { *orientationRate = std::move(ori); }
const math::Vector3& getPosition() const { return *position; }
const math::Vector3& getVelocity() const { return *velocity; }
const math::Quaternion& getOrientation() const { return *orientation; }
const math::Quaternion& getOrientationRate() const { return *orientation; }
private:
std::shared_ptr<math::Vector3> position;
std::shared_ptr<math::Vector3> velocity;
std::shared_ptr<math::Quaternion> orientation;
std::shared_ptr<math::Quaternion> orientationRate;
sim::Propagator propagator;
};
#endif // ROCKET_H

View File

@ -17,11 +17,11 @@ SOURCES += \
gui/MainWindow.cpp \ gui/MainWindow.cpp \
model/Motor.cpp \ model/Motor.cpp \
model/MotorCase.cpp \ model/MotorCase.cpp \
model/Rocket.cpp \
model/Thrustcurve.cpp \ model/Thrustcurve.cpp \
sim/AtmosphericModel.cpp \ sim/AtmosphericModel.cpp \
sim/GravityModel.cpp \ sim/GravityModel.cpp \
sim/Propagator.cpp \ sim/Propagator.cpp \
sim/RK4Solver.cpp \
sim/SphericalGeoidModel.cpp \ sim/SphericalGeoidModel.cpp \
sim/SphericalGravityModel.cpp \ sim/SphericalGravityModel.cpp \
sim/StateData.cpp \ sim/StateData.cpp \
@ -43,6 +43,7 @@ HEADERS += \
gui/qcustomplot.h \ gui/qcustomplot.h \
model/Motor.h \ model/Motor.h \
model/MotorCase.h \ model/MotorCase.h \
model/Rocket.h \
model/Thrustcurve.h \ model/Thrustcurve.h \
sim/AtmosphericModel.h \ sim/AtmosphericModel.h \
sim/DESolver.h \ sim/DESolver.h \

View File

@ -11,7 +11,7 @@ public:
virtual ~DESolver() {} virtual ~DESolver() {}
virtual void setTimeStep(double ts) = 0; virtual void setTimeStep(double ts) = 0;
virtual double step(double curVal, double t) const = 0; virtual void step(double t, double* curVal, double* res ) = 0;
}; };
} // namespace sim } // namespace sim

View File

@ -1,6 +1,9 @@
#ifndef SIM_PROPAGATOR_H #ifndef SIM_PROPAGATOR_H
#define SIM_PROPAGATOR_H #define SIM_PROPAGATOR_H
#include "sim/DESolver.h"
#include <memory>
namespace sim namespace sim
{ {
@ -11,6 +14,8 @@ public:
Propagator(); Propagator();
private: private:
std::unique_ptr<sim::DESolver> solver;
}; };
} // namespace sim } // namespace sim

View File

@ -1,19 +0,0 @@
#include "RK4Solver.h"
namespace sim {
double RK4Solver::step(double curVal, double t) const
{
double retVal(0.0);
double k1 = de(curVal, t);
double k2 = de(curVal + (dt * k1 / 2.0), t + dt / 2.0);
double k3 = de(curVal + (dt * k2 / 2.0), t + dt / 2.0);
double k4 = de(curVal + dt * k3, t + dt);
retVal = curVal + (1.0 / 6.0)*dt*(k1 + 2.0*k2 + 2.0*k3 + k4);
return retVal;
}
} // namespace sim

View File

@ -1,27 +1,93 @@
#ifndef SIM_RK4SOLVER_H #ifndef SIM_RK4SOLVER_H
#define SIM_RK4SOLVER_H #define SIM_RK4SOLVER_H
#include "DESolver.h"
#include <functional> #include <functional>
#include <vector>
#include <limits>
#include <cmath>
#include "utils/Logger.h"
#include "sim/DESolver.h"
namespace sim { namespace sim {
template<typename... Ts>
class RK4Solver : public DESolver class RK4Solver : public DESolver
{ {
public: public:
RK4Solver(std::function<double(double, double)> d) : de(d) {}
RK4Solver(Ts... funcs)
{
(odes.push_back(funcs), ...);
}
virtual ~RK4Solver() {} virtual ~RK4Solver() {}
void setTimeStep(double inTs) override { dt = inTs; } void setTimeStep(double inTs) override { dt = inTs; halfDT = dt / 2.0; }
double step(double curVal, double t) const override;
void step(double t, double* curVal, double* res) override
{
if(dt == std::numeric_limits<double>::quiet_NaN())
{
utils::Logger::getInstance()->error("Calling RK4Solver without setting dt first is an error");
res[0] = std::numeric_limits<double>::quiet_NaN();
}
for(size_t i = 0; i < len; ++i)
{
k1[i] = odes[i](t, curVal);
}
// compute k2 values. This involves stepping the current values forward a half-step
// based on k1, so we do the stepping first
for(size_t i = 0; i < len; ++i)
{
temp[i] = curVal[i] + k1[i]*dt / 2.0;
}
for(size_t i = 0; i < len; ++i)
{
k2[i] = odes[i](t + halfDT, temp);
}
// repeat for k3
for(size_t i = 0; i < len; ++i)
{
temp[i] = curVal[i] + k2[i]*dt / 2.0;
}
for(size_t i = 0; i < len; ++i)
{
k3[i] = odes[i](t + halfDT, temp);
}
// now k4
for(size_t i = 0; i < len; ++i)
{
temp[i] = curVal[i] + k3[i]*dt;
}
for(size_t i = 0; i < len; ++i)
{
k4[i] = odes[i](t + dt, temp);
}
// now compute the result
for(size_t i = 0; i < len; ++i)
{
res[i] = curVal[i] + (dt / 6.0)*(k1[i] + 2.0*k2[i] + 2.0*k3[i] + k4[i]);
}
}
private: private:
std::function<double(double, double)> de; std::vector<std::function<double(double, double*)>> odes;
//double k1, k2, k3, k4; static constexpr size_t len = sizeof...(Ts);
double k1[len];
double k2[len];
double k3[len];
double k4[len];
double dt; double temp[len];
double dt = std::numeric_limits<double>::quiet_NaN();
double halfDT = 0.0;
}; };

View File

@ -2,6 +2,7 @@
#define STATEDATA_H #define STATEDATA_H
#include "utils/math/Vector3.h" #include "utils/math/Vector3.h"
#include "utils/math/Quaternion.h"
/** /**
* @brief The StateData class holds physical state data. Things such as position, velocity, * @brief The StateData class holds physical state data. Things such as position, velocity,
@ -14,12 +15,11 @@ public:
StateData(); StateData();
private: private:
math::Vector3 position; math::Vector3 position{0.0, 0.0, 0.0};
math::Vector3 velocity; math::Vector3 velocity{0.0, 0.0, 0.0};
math::Vector3 acceleration;
math::Vector3 orientation; // roll, pitch, yaw math::Quaternion orientation{0.0, 0.0, 0.0, 0.0}; // roll, pitch, yaw
math::Vector3 orientationVelocity; // roll-rate, pitch-rate, yaw-rate math::Quaternion orientationRate{0.0, 0.0, 0.0, 0.0}; // roll-rate, pitch-rate, yaw-rate
// Necessary? // Necessary?
//math::Vector3 orientationAccel; //math::Vector3 orientationAccel;

View File

@ -8,4 +8,16 @@ Quaternion::Quaternion()
} }
Quaternion::Quaternion(double r, const Vector3& i)
: real(r),
imag(i)
{
}
Quaternion::Quaternion(double r, double i1, double i2, double i3)
: real(r),
imag(i1, i2, i3)
{
}
} // namespace math } // namespace math

View File

@ -3,6 +3,8 @@
#include "Vector3.h" #include "Vector3.h"
#include <utility>
namespace math namespace math
{ {
@ -10,17 +12,38 @@ class Quaternion
{ {
public: public:
Quaternion(); Quaternion();
~Quaternion() {}
Quaternion(double r, const Vector3& i); Quaternion(double r, const Vector3& i);
Quaternion(double r, double i1, double i2, double i3); Quaternion(double r, double i1, double i2, double i3);
~Quaternion(); Quaternion(const Quaternion&) = default;
Quaternion(Quaternion&&) = default;
Quaternion operator-(); Quaternion& operator=(const Quaternion& rhs)
Quaternion operator-(const Quaternion& rhs); {
Quaternion operator+(const Quaternion& rhs); if(this == &rhs)
Quaternion operator*(double s); return *this;
Quaternion operator*(const Quaternion& rhs); real = rhs.real;
imag = rhs.imag;
return *this;
}
Quaternion& operator=(Quaternion&& rhs)
{
if(this == &rhs)
return *this;
real = std::move(rhs.real);
imag = std::move(rhs.imag);
return *this;
}
/*
Quaternion operator-() {}
Quaternion operator-(const Quaternion& ) {}
Quaternion operator+(const Quaternion& ) {}
Quaternion operator*(double ) {}
Quaternion operator*(const Quaternion& ) {}
*/
private: private:
double real; double real;