From 42298ca80104e6d0c747fbd8bc3224decf2d2d38 Mon Sep 17 00:00:00 2001 From: Travis Hunter Date: Thu, 4 May 2023 16:40:20 -0600 Subject: [PATCH 01/32] merge with upstream --- utils/MotorModelDatabase.cpp | 2 ++ utils/MotorModelDatabase.h | 2 +- utils/ThrustCurveAPI.cpp | 58 ++++++++++++++++++++++++++++++++++++ utils/ThrustCurveAPI.h | 3 ++ 4 files changed, 64 insertions(+), 1 deletion(-) diff --git a/utils/MotorModelDatabase.cpp b/utils/MotorModelDatabase.cpp index 23ffa0d..c36baa8 100644 --- a/utils/MotorModelDatabase.cpp +++ b/utils/MotorModelDatabase.cpp @@ -4,6 +4,8 @@ /// \cond // C headers // C++ headers +#include +#include // 3rd party headers #include #include diff --git a/utils/MotorModelDatabase.h b/utils/MotorModelDatabase.h index ea22557..3b734cb 100644 --- a/utils/MotorModelDatabase.h +++ b/utils/MotorModelDatabase.h @@ -74,4 +74,4 @@ private: } // namespace utils -#endif // UTILS_MOTORMODELDATABASE_H \ No newline at end of file +#endif // UTILS_MOTORMODELDATABASE_H diff --git a/utils/ThrustCurveAPI.cpp b/utils/ThrustCurveAPI.cpp index de3ff83..bd4c8c5 100644 --- a/utils/ThrustCurveAPI.cpp +++ b/utils/ThrustCurveAPI.cpp @@ -4,6 +4,7 @@ // C++ headers // 3rd party headers #include +#include /// \endcond // qtrocket headers @@ -25,6 +26,58 @@ ThrustCurveAPI::~ThrustCurveAPI() } +std::optional ThrustCurveAPI::getThrustCurve(const std::string& id) +{ + std::stringstream endpoint; + endpoint << hostname << "api/v1/download.json?motorId=" << id << "&data=samples"; + std::vector extraHeaders = {}; + + std::string res = curlConnection.get(endpoint.str(), extraHeaders); + model::MotorModel mm; + + if(!res.empty()) + { + try + { + Json::Reader reader; + Json::Value jsonResult; + reader.parse(res, jsonResult); + + std::vector> samples; + for(Json::ValueConstIterator iter = jsonResult["results"].begin(); + iter != jsonResult["results"].end(); + ++iter) + { + // if there are more than 1 items in the results list, we only want the RASP data + // Otherwise just take whatever is there + if(std::next(iter) != jsonResult["results"].end()) + { + if( (*iter)["format"].asString() != "RASP") + continue; + } + for(Json::ValueConstIterator samplesIter = (*iter)["samples"].begin(); + samplesIter != (*iter)["samples"].end(); + ++samplesIter) + { + samples.push_back(std::make_pair((*samplesIter)["time"].asDouble(), + (*samplesIter)["thrust"].asDouble())); + + } + } + return ThrustCurve(samples); + } + catch(const std::exception& e) + { + std::string err("Unable to parse JSON from Thrustcurve motor data request. Error: "); + err += e.what(); + + Logger::getInstance()->error(err); + } + } + + return std::nullopt; + +} model::MotorModel ThrustCurveAPI::getMotorData(const std::string& motorId) { @@ -227,6 +280,11 @@ std::vector ThrustCurveAPI::searchMotors(const SearchCriteria mm.type = model::MotorModel::MotorType(model::MotorModel::MOTORTYPE::HYBRID); motorModel.moveMetaData(std::move(mm)); + auto tc = getThrustCurve(mm.motorIdTC); + if(tc) + { + motorModel.addThrustCurve(*tc); + } retVal.push_back(motorModel); } } diff --git a/utils/ThrustCurveAPI.h b/utils/ThrustCurveAPI.h index eb63946..af09ecb 100644 --- a/utils/ThrustCurveAPI.h +++ b/utils/ThrustCurveAPI.h @@ -7,6 +7,7 @@ // C++ headers #include #include +#include // 3rd party headers /// \endcond @@ -91,6 +92,8 @@ private: const std::string hostname; CurlConnection curlConnection; + std::optional getThrustCurve(const std::string& id); + // no extra headers, but CurlConnection library wants them const std::vector extraHeaders{}; }; From 821df8905a1136bb1743672849b31f09262472b3 Mon Sep 17 00:00:00 2001 From: Travis Hunter Date: Fri, 5 May 2023 08:01:59 -0600 Subject: [PATCH 02/32] Now reads motor delays --- QtRocket.cpp | 6 +----- QtRocket.h | 5 ----- model/Rocket.h | 2 ++ utils/MotorModelDatabase.cpp | 24 ++++++++---------------- utils/RSEDatabaseLoader.cpp | 11 +++++++++++ 5 files changed, 22 insertions(+), 26 deletions(-) diff --git a/QtRocket.cpp b/QtRocket.cpp index eca81cb..88703e6 100644 --- a/QtRocket.cpp +++ b/QtRocket.cpp @@ -103,10 +103,6 @@ int QtRocket::run(int argc, char* argv[]) void QtRocket::addMotorModels(std::vector& m) { - for(const auto& i : m) - { - motorModels.push_back(i); - } - motorDatabase->addMotorModels(motorModels); + motorDatabase->addMotorModels(m); // TODO: Now clear any duplicates? } diff --git a/QtRocket.h b/QtRocket.h index ef21c17..88a719a 100644 --- a/QtRocket.h +++ b/QtRocket.h @@ -50,8 +50,6 @@ public: void addMotorModels(std::vector& m); - const std::vector& getMotorModels() const { return motorModels; } - void addRocket(std::shared_ptr r) { rocket.first = r; } void setEnvironment(std::shared_ptr e) { environment = e; } @@ -66,9 +64,6 @@ private: static std::mutex mtx; static QtRocket* instance; - // Motor "database(s)" - std::vector motorModels; - utils::Logger* logger; std::pair, std::shared_ptr> rocket; diff --git a/model/Rocket.h b/model/Rocket.h index 72345a4..f802d70 100644 --- a/model/Rocket.h +++ b/model/Rocket.h @@ -107,6 +107,8 @@ public: void setName(const std::string& n) { name = n; } private: + + std::vector bodyFrameState{ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; std::string name; /// Rocket name sim::Propagator propagator; /// propagator double dragCoeff; /// @todo get rid of this, should be dynamically calculated diff --git a/utils/MotorModelDatabase.cpp b/utils/MotorModelDatabase.cpp index c36baa8..43958c7 100644 --- a/utils/MotorModelDatabase.cpp +++ b/utils/MotorModelDatabase.cpp @@ -4,8 +4,6 @@ /// \cond // C headers // C++ headers -#include -#include // 3rd party headers #include #include @@ -70,20 +68,6 @@ std::optional MotorModelDatabase::getMotorModel(const std::st void MotorModelDatabase::saveMotorDatabase(const std::string& filename) { -/* - - - 1.0 - - - 10.0 - 123.4 - - - - -*/ - namespace pt = boost::property_tree; // top-level tree @@ -114,6 +98,14 @@ void MotorModelDatabase::saveMotorDatabase(const std::string& filename) motor.put("type", m.data.type.str()); motor.put("lastUpdated", m.data.lastUpdated); + std::stringstream delays; + for (std::size_t i = 0; i < m.data.delays.size() - 1; ++i) + { + delays << std::to_string(m.data.delays[i]) << ","; + } + delays << std::to_string(m.data.delays[m.data.delays.size() - 1]); + motor.put("delays", delays.str()); + // thrust data { pt::ptree tc; diff --git a/utils/RSEDatabaseLoader.cpp b/utils/RSEDatabaseLoader.cpp index 111aa21..83b52d5 100644 --- a/utils/RSEDatabaseLoader.cpp +++ b/utils/RSEDatabaseLoader.cpp @@ -5,6 +5,7 @@ #include #include #include +#include // 3rd party headers #include @@ -58,6 +59,16 @@ void RSEDatabaseLoader::buildAndAppendMotorModel(boost::property_tree::ptree& v) mm.commonName = v.get(".code", ""); // mm.delays = extract vector from csv list + std::string delays = v.get(".delays", "1000"); + std::size_t pos{0}; + std::string tok; + while ((pos = delays.find(",")) != std::string::npos) + { + tok = delays.substr(0, pos); + mm.delays.push_back(std::atoi(tok.c_str())); + delays.erase(0, pos + 1); + } + mm.delays.push_back(std::atoi(delays.c_str())); // mm.designation = What is this? From 8e620cf0c138ee08907d5b3a7107e93f6eb35a4c Mon Sep 17 00:00:00 2001 From: Travis Hunter Date: Sat, 6 May 2023 08:53:56 -0600 Subject: [PATCH 03/32] Added Eigen dependency, so now QtRocket Vectors (and matrices and quaternions) are backed by Eigen. Cleaned up unused files also --- CMakeLists.txt | 19 +++----- gui/MainWindow.cpp | 9 +++- model/MotorCase.cpp | 1 - model/MotorCase.h | 17 -------- model/MotorModel.h | 2 +- sim/ConstantGravityModel.cpp | 10 ----- sim/ConstantGravityModel.h | 11 +++-- sim/DESolver.h | 12 ++++- sim/GravityModel.cpp | 14 ------ sim/GravityModel.h | 10 ++--- sim/Propagator.cpp | 39 +++++++++-------- sim/Propagator.h | 7 +-- sim/RK4Solver.h | 21 ++++++--- sim/SphericalGravityModel.cpp | 4 +- sim/SphericalGravityModel.h | 4 +- sim/StateData.cpp | 6 --- sim/StateData.h | 20 ++++----- sim/WindModel.cpp | 4 +- sim/WindModel.h | 4 +- utils/MotorModelDatabase.cpp | 1 + utils/Triplet.h | 21 --------- utils/math/MathTypes.h | 15 +++++++ utils/math/Quaternion.cpp | 25 ----------- utils/math/Quaternion.h | 63 -------------------------- utils/math/UtilityMathFunctions.cpp | 15 ------- utils/math/Vector3.cpp | 68 ----------------------------- utils/math/Vector3.h | 40 ----------------- 27 files changed, 113 insertions(+), 349 deletions(-) delete mode 100644 model/MotorCase.cpp delete mode 100644 model/MotorCase.h delete mode 100644 sim/ConstantGravityModel.cpp delete mode 100644 sim/GravityModel.cpp delete mode 100644 sim/StateData.cpp delete mode 100644 utils/Triplet.h create mode 100644 utils/math/MathTypes.h delete mode 100644 utils/math/Quaternion.cpp delete mode 100644 utils/math/Quaternion.h delete mode 100644 utils/math/UtilityMathFunctions.cpp delete mode 100644 utils/math/Vector3.cpp delete mode 100644 utils/math/Vector3.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ece6ee2..8c3c574 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,6 +36,11 @@ if(WIN32) endif() FetchContent_MakeAvailable(CURL) +# eigen dependency +FetchContent_Declare(eigen + GIT_REPOSITORY https://gitlab.com/libeigen/eigen + GIT_TAG 3.4.0) +FetchContent_MakeAvailable(eigen) set(CMAKE_AUTOUIC ON) @@ -84,8 +89,6 @@ set(PROJECT_SOURCES gui/ThrustCurveMotorSelector.ui gui/qcustomplot.cpp gui/qcustomplot.h - model/MotorCase.cpp - model/MotorCase.h model/MotorModel.cpp model/MotorModel.h model/MotorModelDatabase.cpp @@ -96,12 +99,10 @@ set(PROJECT_SOURCES model/ThrustCurve.h sim/AtmosphericModel.h sim/ConstantAtmosphere.h - sim/ConstantGravityModel.cpp sim/ConstantGravityModel.h sim/DESolver.h sim/Environment.h sim/GeoidModel.h - sim/GravityModel.cpp sim/GravityModel.h sim/Propagator.cpp sim/Propagator.h @@ -110,7 +111,6 @@ set(PROJECT_SOURCES sim/SphericalGeoidModel.h sim/SphericalGravityModel.cpp sim/SphericalGravityModel.h - sim/StateData.cpp sim/StateData.h sim/USStandardAtmosphere.cpp sim/USStandardAtmosphere.h @@ -130,15 +130,10 @@ set(PROJECT_SOURCES utils/ThreadPool.h utils/ThrustCurveAPI.cpp utils/ThrustCurveAPI.h - utils/Triplet.h utils/TSQueue.h utils/math/Constants.h - utils/math/Quaternion.cpp - utils/math/Quaternion.h - utils/math/UtilityMathFunctions.cpp + utils/math/MathTypes.h utils/math/UtilityMathFunctions.h - utils/math/Vector3.cpp - utils/math/Vector3.h ${TS_FILES} ) @@ -171,7 +166,7 @@ else() endif() #target_link_libraries(qtrocket PRIVATE Qt${QT_VERSION_MAJOR}::Widgets Qt${Qt_VERSION_MAJOR}::PrintSupport libcurl jsoncpp_static fmt::fmt-header-only) -target_link_libraries(qtrocket PRIVATE Qt6::Widgets Qt6::PrintSupport libcurl jsoncpp_static fmt::fmt-header-only) +target_link_libraries(qtrocket PRIVATE Qt6::Widgets Qt6::PrintSupport libcurl jsoncpp_static fmt::fmt-header-only eigen) set_target_properties(qtrocket PROPERTIES MACOSX_BUNDLE_GUI_IDENTIFIER my.example.com diff --git a/gui/MainWindow.cpp b/gui/MainWindow.cpp index 0184fd0..8937398 100644 --- a/gui/MainWindow.cpp +++ b/gui/MainWindow.cpp @@ -83,6 +83,7 @@ MainWindow::MainWindow(QtRocket* _qtRocket, QWidget *parent) this, SLOT(onButton_getTCMotorData_clicked())); + ui->calculateTrajectory_btn->setDisabled(true); } MainWindow::~MainWindow() @@ -186,7 +187,13 @@ void MainWindow::onButton_setMotor_clicked() QString motorName = ui->engineSelectorComboBox->currentText(); model::MotorModel mm = rseDatabase->getMotorModelByName(motorName.toStdString()); QtRocket::getInstance()->getRocket()->setMotorModel(mm); - QtRocket::getInstance()->addMotorModels(rseDatabase->getMotors()); + + // Now that we have a motor selected, we can enable the calculateTrajectory button + ui->calculateTrajectory_btn->setDisabled(false); + + /// TODO: Figure out if this is the right place to populate the motor database + /// or from RSEDatabaseLoader where it currently is populated. + //QtRocket::getInstance()->addMotorModels(rseDatabase->getMotors()); } diff --git a/model/MotorCase.cpp b/model/MotorCase.cpp deleted file mode 100644 index bb394dd..0000000 --- a/model/MotorCase.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "MotorCase.h" \ No newline at end of file diff --git a/model/MotorCase.h b/model/MotorCase.h deleted file mode 100644 index 5e965fa..0000000 --- a/model/MotorCase.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef MODEL_MOTORCASE_H -#define MODEL_MOTORCASE_H - -namespace model -{ -class MotorCase -{ -public: - MotorCase(); - ~MotorCase(); - -private: - -}; - -} // namespace model -#endif // MODEL_MOTORCASE_H diff --git a/model/MotorModel.h b/model/MotorModel.h index 72b08af..b6ea62c 100644 --- a/model/MotorModel.h +++ b/model/MotorModel.h @@ -406,7 +406,7 @@ public: MetaData data; private: bool ignitionOccurred{false}; - bool burnOutOccurred{false}; + bool burnOutOccurred{false}; double emptyMass; double isp; double maxTime; diff --git a/sim/ConstantGravityModel.cpp b/sim/ConstantGravityModel.cpp deleted file mode 100644 index 9027ec7..0000000 --- a/sim/ConstantGravityModel.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include "ConstantGravityModel.h" - -namespace sim { - -ConstantGravityModel::ConstantGravityModel() -{ - -} - -} // namespace sim diff --git a/sim/ConstantGravityModel.h b/sim/ConstantGravityModel.h index 74a83af..7d3a0fd 100644 --- a/sim/ConstantGravityModel.h +++ b/sim/ConstantGravityModel.h @@ -3,18 +3,21 @@ // qtrocket headers #include "sim/GravityModel.h" -#include "utils/Triplet.h" +#include "utils/math/MathTypes.h" namespace sim { class ConstantGravityModel : public GravityModel { public: - ConstantGravityModel(); + ConstantGravityModel() {} - virtual ~ConstantGravityModel() {} + virtual ~ConstantGravityModel() {} - TripletD getAccel(double, double, double) override { return TripletD(0.0, 0.0, -9.8); } + Vector3 getAccel(double, double, double) override + { + return Vector3(0.0, 0.0, -9.8); + } }; } // namespace sim diff --git a/sim/DESolver.h b/sim/DESolver.h index a20c2ad..36d04fa 100644 --- a/sim/DESolver.h +++ b/sim/DESolver.h @@ -19,7 +19,17 @@ public: virtual ~DESolver() {} virtual void setTimeStep(double ts) = 0; - virtual void step(double t, const std::vector& curVal, std::vector& res ) = 0; + /** + * @brief + * + * @param curVal + * @param res + * @param t Optional parameter, but not used in QtRocket. Some generic solvers take time as + * a parameter to ODEs, but QtRocket's kinematic equations don't. Since I wrote + * the RK4 solver independently as a general tool, this interface is needed + * here unfortunately. + */ + virtual void step(const std::vector& curVal, std::vector& res, double t = 0.0) = 0; }; } // namespace sim diff --git a/sim/GravityModel.cpp b/sim/GravityModel.cpp deleted file mode 100644 index 36d4207..0000000 --- a/sim/GravityModel.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "GravityModel.h" - -namespace sim -{ - -GravityModel::GravityModel() -{ -} - -GravityModel::~GravityModel() -{ -} - -} // namespace sim \ No newline at end of file diff --git a/sim/GravityModel.h b/sim/GravityModel.h index da666ba..5f4085b 100644 --- a/sim/GravityModel.h +++ b/sim/GravityModel.h @@ -2,7 +2,7 @@ #define SIM_GRAVITYMODEL_H // qtrocket headers -#include "utils/Triplet.h" +#include "utils/math/MathTypes.h" namespace sim { @@ -10,11 +10,11 @@ namespace sim class GravityModel { public: - GravityModel(); - virtual ~GravityModel(); + GravityModel() {} + virtual ~GravityModel() {} - virtual TripletD getAccel(double x, double y, double z) = 0; - TripletD getAccel(const TripletD& t) { return this->getAccel(t.x1, t.x2, t.x3); } + virtual Vector3 getAccel(double x, double y, double z) = 0; + Vector3 getAccel(const Vector3& t) { return this->getAccel(t[0], t[1], t[2]); } }; } // namespace sim diff --git a/sim/Propagator.cpp b/sim/Propagator.cpp index 619377a..c17738b 100644 --- a/sim/Propagator.cpp +++ b/sim/Propagator.cpp @@ -23,7 +23,8 @@ namespace sim { Propagator::Propagator(Rocket* r) - : integrator(), + : linearIntegrator(), + //orientationIntegrator(), rocket(r) { @@ -37,23 +38,25 @@ Propagator::Propagator(Rocket* r) // and pass it a freshly allocated RK4Solver pointer // The state vector has components of the form: - // (x, y, z, xdot, ydot, zdot, pitch, yaw, roll, pitchRate, yawRate, rollRate) - integrator.reset(new RK4Solver( - /* dx/dt */ [](double, const std::vector& s) -> double {return s[3]; }, - /* dy/dt */ [](double, const std::vector& s) -> double {return s[4]; }, - /* dz/dt */ [](double, const std::vector& s) -> double {return s[5]; }, - /* dvx/dt */ [this](double, const std::vector& ) -> double { return getForceX() / getMass(); }, - /* dvy/dt */ [this](double, const std::vector& ) -> double { return getForceY() / getMass(); }, - /* dvz/dt */ [this](double, const std::vector& ) -> double { return getForceZ() / getMass(); }, - /* dpitch/dt */ [](double, const std::vector& s) -> double { return s[9]; }, - /* dyaw/dt */ [](double, const std::vector& s) -> double { return s[10]; }, - /* droll/dt */ [](double, const std::vector& s) -> double { return s[11]; }, - /* dpitchRate/dt */ [this](double, const std::vector& s) -> double { return (getTorqueP() - s[7] * s[8] * (getIroll() - getIyaw())) / getIpitch(); }, - /* dyawRate/dt */ [this](double, const std::vector& s) -> double { return (getTorqueQ() - s[6] * s[9] * (getIpitch() - getIroll())) / getIyaw(); }, - /* drollRate/dt */ [this](double, const std::vector& s) -> double { return (getTorqueR() - s[6] * s[7] * (getIyaw() - getIpitch())) / getIroll(); })); + linearIntegrator.reset(new RK4Solver( + /* dx/dt */ [](const std::vector& s, double) -> double {return s[3]; }, + /* dy/dt */ [](const std::vector& s, double) -> double {return s[4]; }, + /* dz/dt */ [](const std::vector& s, double) -> double {return s[5]; }, + /* dvx/dt */ [this](const std::vector&, double ) -> double { return getForceX() / getMass(); }, + /* dvy/dt */ [this](const std::vector&, double ) -> double { return getForceY() / getMass(); }, + /* dvz/dt */ [this](const std::vector&, double ) -> double { return getForceZ() / getMass(); })); + linearIntegrator->setTimeStep(timeStep); + +// orientationIntegrator.reset(new RK4Solver( +// /* dpitch/dt */ [](double, const std::vector& s) -> double { return s[3]; }, +// /* dyaw/dt */ [](double, const std::vector& s) -> double { return s[4]; }, +// /* droll/dt */ [](double, const std::vector& s) -> double { return s[5]; }, +// /* dpitchRate/dt */ [this](double, const std::vector& s) -> double { return (getTorqueP() - s[1] * s[2] * (getIroll() - getIyaw())) / getIpitch(); }, +// /* dyawRate/dt */ [this](double, const std::vector& s) -> double { return (getTorqueQ() - s[0] * s[2] * (getIpitch() - getIroll())) / getIyaw(); }, +// /* drollRate/dt */ [this](double, const std::vector& s) -> double { return (getTorqueR() - s[0] * s[1] * (getIyaw() - getIpitch())) / getIroll(); })); +// orientationIntegrator->setTimeStep(timeStep); - integrator->setTimeStep(timeStep); saveStates = true; } @@ -69,7 +72,7 @@ void Propagator::runUntilTerminate() while(true) { // tempRes gets overwritten - integrator->step(currentTime, currentState, tempRes); + linearIntegrator->step(currentState, tempRes); std::swap(currentState, tempRes); if(saveStates) @@ -110,7 +113,7 @@ double Propagator::getForceY() double Propagator::getForceZ() { QtRocket* qtrocket = QtRocket::getInstance(); - double gravity = (qtrocket->getEnvironment()->getGravityModel()->getAccel(currentState[0], currentState[1], currentState[2])).x3; + double gravity = (qtrocket->getEnvironment()->getGravityModel()->getAccel(currentState[0], currentState[1], currentState[2]))[2]; double airDrag = (currentState[5] >= 0 ? -1.0 : 1.0) * qtrocket->getEnvironment()->getAtmosphericModel()->getDensity(currentState[2]) / 2.0 * 0.008107 * rocket->getDragCoefficient() * currentState[5]* currentState[5]; double thrust = rocket->getThrust(currentTime); return gravity + airDrag + thrust; diff --git a/sim/Propagator.h b/sim/Propagator.h index 9e1151b..97a04ee 100644 --- a/sim/Propagator.h +++ b/sim/Propagator.h @@ -74,12 +74,13 @@ private: //private: - std::unique_ptr integrator; + std::unique_ptr linearIntegrator; + //std::unique_ptr orientationIntegrator; Rocket* rocket; - std::vector currentState{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; - std::vector tempRes{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; + std::vector currentState{0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; + std::vector tempRes{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; bool saveStates{true}; double currentTime{0.0}; double timeStep{0.01}; diff --git a/sim/RK4Solver.h b/sim/RK4Solver.h index 26a7e93..ec481da 100644 --- a/sim/RK4Solver.h +++ b/sim/RK4Solver.h @@ -19,6 +19,15 @@ namespace sim { +/** + * @brief Runge-Kutta 4th order coupled ODE solver. + * @note This was written outside of the context of QtRocket, and it is very generic. There are + * some features of this solver that are note used by QtRocket, for example, it can solve + * and arbitrarily large system of coupled ODEs, but QtRocket only makes use of a system + * of size 6 (x, y, z, xDot, yDot, zDot) at a time. + * + * @tparam Ts + */ template class RK4Solver : public DESolver { @@ -34,7 +43,7 @@ public: void setTimeStep(double inTs) override { dt = inTs; halfDT = dt / 2.0; } - void step(double t, const std::vector& curVal, std::vector& res) override + void step(const std::vector& curVal, std::vector& res, double t = 0.0) override { if(dt == std::numeric_limits::quiet_NaN()) { @@ -44,7 +53,7 @@ public: for(size_t i = 0; i < len; ++i) { - k1[i] = odes[i](t, curVal); + k1[i] = odes[i](curVal, t); } // compute k2 values. This involves stepping the current values forward a half-step // based on k1, so we do the stepping first @@ -54,7 +63,7 @@ public: } for(size_t i = 0; i < len; ++i) { - k2[i] = odes[i](t + halfDT, temp); + k2[i] = odes[i](temp, t + halfDT); } // repeat for k3 for(size_t i = 0; i < len; ++i) @@ -63,7 +72,7 @@ public: } for(size_t i = 0; i < len; ++i) { - k3[i] = odes[i](t + halfDT, temp); + k3[i] = odes[i](temp, t + halfDT); } // now k4 @@ -73,7 +82,7 @@ public: } for(size_t i = 0; i < len; ++i) { - k4[i] = odes[i](t + dt, temp); + k4[i] = odes[i](temp, t + dt); } // now compute the result @@ -85,7 +94,7 @@ public: } private: - std::vector&)>> odes; + std::vector&, double)>> odes; static constexpr size_t len = sizeof...(Ts); double k1[len]; diff --git a/sim/SphericalGravityModel.cpp b/sim/SphericalGravityModel.cpp index 24a9353..3ab355b 100644 --- a/sim/SphericalGravityModel.cpp +++ b/sim/SphericalGravityModel.cpp @@ -25,7 +25,7 @@ SphericalGravityModel::~SphericalGravityModel() } -TripletD SphericalGravityModel::getAccel(double x, double y, double z) +Vector3 SphericalGravityModel::getAccel(double x, double y, double z) { // Convert x, y, z from meters to km. This is to avoid potential precision losses // with using the earth's gravitation parameter in meters (14 digit number). @@ -43,7 +43,7 @@ TripletD SphericalGravityModel::getAccel(double x, double y, double z) double ay = accelFactor * y_km * 1000.0; double az = accelFactor * z_km * 1000.0; - return TripletD(ax, ay, az); + return Vector3(ax, ay, az); } diff --git a/sim/SphericalGravityModel.h b/sim/SphericalGravityModel.h index 8b28d31..90e7acb 100644 --- a/sim/SphericalGravityModel.h +++ b/sim/SphericalGravityModel.h @@ -2,7 +2,7 @@ #define SIM_SPHERICALGRAVITYMODEL_H // qtrocket headers -#include "GravityModel.h" +#include "sim/GravityModel.h" namespace sim { @@ -13,7 +13,7 @@ public: SphericalGravityModel(); virtual ~SphericalGravityModel(); - TripletD getAccel(double x, double y, double z) override; + Vector3 getAccel(double x, double y, double z) override; }; } // namespace sim diff --git a/sim/StateData.cpp b/sim/StateData.cpp deleted file mode 100644 index 2af9702..0000000 --- a/sim/StateData.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "StateData.h" - -StateData::StateData() -{ - -} diff --git a/sim/StateData.h b/sim/StateData.h index fc27e8a..e0b2567 100644 --- a/sim/StateData.h +++ b/sim/StateData.h @@ -2,8 +2,7 @@ #define STATEDATA_H // qtrocket headers -#include "utils/math/Vector3.h" -#include "utils/math/Quaternion.h" +#include "utils/math/MathTypes.h" /** * @brief The StateData class holds physical state data. Things such as position, velocity, @@ -13,17 +12,18 @@ class StateData { public: - StateData(); + StateData() {} + ~StateData() {} +/// TODO: Put these behind an interface +// private: + Vector3 position{0.0, 0.0, 0.0}; + Vector3 velocity{0.0, 0.0, 0.0}; -private: - math::Vector3 position{0.0, 0.0, 0.0}; - math::Vector3 velocity{0.0, 0.0, 0.0}; + Quaternion orientation{0.0, 0.0, 0.0, 0.0}; /// (scalar, vector) + Quaternion orientationRate{0.0, 0.0, 0.0, 0.0}; /// (scalar, vector) - math::Quaternion orientation{0.0, 0.0, 0.0, 0.0}; // roll, pitch, yaw - math::Quaternion orientationRate{0.0, 0.0, 0.0, 0.0}; // roll-rate, pitch-rate, yaw-rate - // Necessary? - //math::Vector3 orientationAccel; + Matrix3 dcm{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; // This is an array because the integrator expects it double data[6]; diff --git a/sim/WindModel.cpp b/sim/WindModel.cpp index b98811c..ca5b51e 100644 --- a/sim/WindModel.cpp +++ b/sim/WindModel.cpp @@ -13,9 +13,9 @@ WindModel::~WindModel() { } -TripletD WindModel::getWindSpeed(double /* x */, double /* y */ , double /* z */) +Vector3 WindModel::getWindSpeed(double /* x */, double /* y */ , double /* z */) { - return TripletD(0.0, 0.0, 0.0); + return Vector3(0.0, 0.0, 0.0); } } // namespace sim diff --git a/sim/WindModel.h b/sim/WindModel.h index d01046b..7c584d7 100644 --- a/sim/WindModel.h +++ b/sim/WindModel.h @@ -2,7 +2,7 @@ #define SIM_WINDMODEL_H // qtrocket headers -#include "utils/Triplet.h" +#include "utils/math/MathTypes.h" namespace sim { @@ -13,7 +13,7 @@ public: WindModel(); virtual ~WindModel(); - virtual TripletD getWindSpeed(double x, double y, double z); + virtual Vector3 getWindSpeed(double x, double y, double z); }; diff --git a/utils/MotorModelDatabase.cpp b/utils/MotorModelDatabase.cpp index 43958c7..cf958b7 100644 --- a/utils/MotorModelDatabase.cpp +++ b/utils/MotorModelDatabase.cpp @@ -98,6 +98,7 @@ void MotorModelDatabase::saveMotorDatabase(const std::string& filename) motor.put("type", m.data.type.str()); motor.put("lastUpdated", m.data.lastUpdated); + // delays tag is in the form of a csv string std::stringstream delays; for (std::size_t i = 0; i < m.data.delays.size() - 1; ++i) { diff --git a/utils/Triplet.h b/utils/Triplet.h deleted file mode 100644 index fd6a4cb..0000000 --- a/utils/Triplet.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef TRIPLET_H -#define TRIPLET_H - -/** - * The purpose of this class is to get rid of using std::tuple for coordinate triplets. - */ - -template -struct Triplet -{ - Triplet(const T& a, const T& b, const T& c) - : x1(a), x2(b), x3(c) - {} - T x1; - T x2; - T x3; -}; - -using TripletD = Triplet; - -#endif // TRIPLET_H diff --git a/utils/math/MathTypes.h b/utils/math/MathTypes.h new file mode 100644 index 0000000..41551bb --- /dev/null +++ b/utils/math/MathTypes.h @@ -0,0 +1,15 @@ +#ifndef UTILS_MATH_MATHTYPES_H +#define UTILS_MATH_MATHTYPES_H + +#include + +/// This is not in any namespace. These typedefs are intended to be used throughout QtRocket, +/// so keeping them in the global namespace seems to make sense. + +typedef Eigen::Matrix3d Matrix3; +typedef Eigen::Matrix4d Matrix4; +typedef Eigen::Vector3d Vector3; +typedef Eigen::Quaterniond Quaternion; + + +#endif // UTILS_MATH_MATHTYPES_H \ No newline at end of file diff --git a/utils/math/Quaternion.cpp b/utils/math/Quaternion.cpp deleted file mode 100644 index c575d0f..0000000 --- a/utils/math/Quaternion.cpp +++ /dev/null @@ -1,25 +0,0 @@ - -// qtrocket headers -#include "utils/math/Quaternion.h" - -namespace math -{ - -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 diff --git a/utils/math/Quaternion.h b/utils/math/Quaternion.h deleted file mode 100644 index 2d8a848..0000000 --- a/utils/math/Quaternion.h +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef MATH_QUATERNION_H -#define MATH_QUATERNION_H - -/// \cond -// C headers -// C++ headers -#include - -// 3rd party headers -/// \endcond - -// qtrocket headers -#include "Vector3.h" - - -namespace math -{ - -class Quaternion -{ -public: - Quaternion(); - ~Quaternion() {} - - Quaternion(double r, const Vector3& i); - Quaternion(double r, double i1, double i2, double i3); - - Quaternion(const Quaternion&) = default; - Quaternion(Quaternion&&) = default; - - Quaternion& operator=(const Quaternion& rhs) - { - if(this == &rhs) - return *this; - 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: - double real; - Vector3 imag; -}; - -} // namespace math - -#endif // MATH_QUATERNION_H diff --git a/utils/math/UtilityMathFunctions.cpp b/utils/math/UtilityMathFunctions.cpp deleted file mode 100644 index 21d5dfa..0000000 --- a/utils/math/UtilityMathFunctions.cpp +++ /dev/null @@ -1,15 +0,0 @@ - -/// \cond -// C headers -// C++ headers -// 3rd party headers -/// \endcond - - -namespace utils -{ -namespace math -{ - -} // namespace math -} // namespace utils diff --git a/utils/math/Vector3.cpp b/utils/math/Vector3.cpp deleted file mode 100644 index 90c7f1b..0000000 --- a/utils/math/Vector3.cpp +++ /dev/null @@ -1,68 +0,0 @@ - -// qtrocket headers -#include "utils/math/Vector3.h" - -namespace math -{ - -Vector3::Vector3() - : x1(0.0), - x2(0.0), - x3(0.0) -{ - -} - -Vector3::Vector3(const double& inX1, - const double& inX2, - const double& inX3) - : x1(inX1), - x2(inX2), - x3(inX3) -{ - -} - -Vector3::Vector3(const Vector3& o) - : x1(o.x1), - x2(o.x2), - x3(o.x3) -{ - -} - -Vector3::~Vector3() -{} - -Vector3 Vector3::operator=(const Vector3& rhs) -{ - return Vector3(rhs.x1, rhs.x2, rhs.x3); -} - -Vector3 Vector3::operator-() -{ - return Vector3(-x1, -x2, -x3); -} - -Vector3 Vector3::operator-(const Vector3& rhs) -{ - return Vector3(x1 - rhs.x1, - x2 - rhs.x2, - x3 - rhs.x3); -} - -Vector3 Vector3::operator+(const Vector3& rhs) -{ - return Vector3(x1 + rhs.x1, - x2 + rhs.x2, - x3 + rhs.x3); -} - -Vector3 Vector3::operator*(const double& s) -{ - return Vector3(x1 * s, - x2 * s, - x3 * s); -} - -} // namespace math diff --git a/utils/math/Vector3.h b/utils/math/Vector3.h deleted file mode 100644 index 6a42472..0000000 --- a/utils/math/Vector3.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef MATH_VECTOR3_H -#define MATH_VECTOR3_H - - -namespace math -{ - -class Vector3 -{ -public: - Vector3(); - Vector3(const double& inX1, - const double& inX2, - const double& inX3); - - Vector3(const Vector3& o); - - ~Vector3(); - - Vector3 operator=(const Vector3& rhs); - - Vector3 operator-(); - Vector3 operator-(const Vector3& rhs); - Vector3 operator+(const Vector3& rhs); - Vector3 operator*(const double& s); - - double getX1() { return x1; } - double getX2() { return x2; } - double getX3() { return x3; } - - -//private: - double x1; - double x2; - double x3; -}; - -} // namespace math - -#endif // MATH_VECTOR3_H From 558211e9feb3ec9752c2ca402b29c64d899bbdeb Mon Sep 17 00:00:00 2001 From: Travis Hunter Date: Sat, 6 May 2023 09:20:42 -0600 Subject: [PATCH 04/32] Removed the Propagator from the Rocket. It shouldn't really be there, now the Propagator belongs to QtRocket, and is paired with a particular Rocket model, but doesn't belong to that model --- QtRocket.cpp | 16 ++++++++++++++++ QtRocket.h | 13 +++++++++++++ gui/AnalysisWindow.cpp | 8 ++++---- gui/MainWindow.cpp | 5 +++-- model/Rocket.cpp | 5 +---- model/Rocket.h | 16 +++------------- sim/Propagator.cpp | 2 +- sim/Propagator.h | 5 +++-- sim/StateData.h | 3 +++ 9 files changed, 47 insertions(+), 26 deletions(-) diff --git a/QtRocket.cpp b/QtRocket.cpp index 88703e6..49a8bc5 100644 --- a/QtRocket.cpp +++ b/QtRocket.cpp @@ -82,6 +82,9 @@ QtRocket::QtRocket() rocket.first = std::make_shared(); + + rocket.second = + std::make_shared(rocket.first); motorDatabase = std::make_shared(); @@ -101,6 +104,19 @@ int QtRocket::run(int argc, char* argv[]) return 0; } +void QtRocket::launchRocket() +{ + // initialize the propagator + rocket.second->clearStates(); + rocket.second->setCurrentTime(0.0); + + // start the rocket motor + rocket.first->launch(); + + // run the propagator until it terminates + rocket.second->runUntilTerminate(); +} + void QtRocket::addMotorModels(std::vector& m) { motorDatabase->addMotorModels(m); diff --git a/QtRocket.h b/QtRocket.h index 88a719a..02e301b 100644 --- a/QtRocket.h +++ b/QtRocket.h @@ -54,6 +54,19 @@ public: void setEnvironment(std::shared_ptr e) { environment = e; } + void launchRocket(); + /** + * @brief getStates returns a vector of time/state pairs generated during launch() + * @return vector of pairs of doubles, where the first value is a time and the second a state vector + */ + const std::vector>>& getStates() const { return rocket.second->getStates(); } + + /** + * @brief setInitialState sets the initial state of the Rocket. + * @param initState initial state vector (x, y, z, xDot, yDot, zDot, pitch, yaw, roll, pitchDot, yawDot, rollDot) + */ + void setInitialState(const std::vector& initState) { rocket.second->setInitialState(initState); } + private: QtRocket(); diff --git a/gui/AnalysisWindow.cpp b/gui/AnalysisWindow.cpp index 3a47076..92c4e66 100644 --- a/gui/AnalysisWindow.cpp +++ b/gui/AnalysisWindow.cpp @@ -35,8 +35,8 @@ AnalysisWindow::~AnalysisWindow() void AnalysisWindow::onButton_plotAltitude_clicked() { - std::shared_ptr rocket = QtRocket::getInstance()->getRocket(); - const std::vector>>& res = rocket->getStates(); + QtRocket* qtRocket = QtRocket::getInstance(); + const std::vector>>& res = qtRocket->getStates(); auto& plot = ui->plotWidget; plot->clearGraphs(); plot->setInteraction(QCP::iRangeDrag, true); @@ -62,8 +62,8 @@ void AnalysisWindow::onButton_plotAltitude_clicked() void AnalysisWindow::onButton_plotVelocity_clicked() { - std::shared_ptr rocket = QtRocket::getInstance()->getRocket(); - const std::vector>>& res = rocket->getStates(); + QtRocket* qtRocket = QtRocket::getInstance(); + const std::vector>>& res = qtRocket->getStates(); auto& plot = ui->plotWidget; plot->clearGraphs(); plot->setInteraction(QCP::iRangeDrag, true); diff --git a/gui/MainWindow.cpp b/gui/MainWindow.cpp index 8937398..054c266 100644 --- a/gui/MainWindow.cpp +++ b/gui/MainWindow.cpp @@ -125,10 +125,11 @@ void MainWindow::onButton_calculateTrajectory_clicked() double initialVelocityZ = initialVelocity * std::sin(initialAngle / 57.2958); std::vector initialState = {0.0, 0.0, 0.0, initialVelocityX, 0.0, initialVelocityZ, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; auto rocket = QtRocket::getInstance()->getRocket(); - rocket->setInitialState(initialState); rocket->setMass(mass); rocket->setDragCoefficient(dragCoeff); - rocket->launch(); + + qtRocket->setInitialState(initialState); + qtRocket->launchRocket(); AnalysisWindow aWindow; aWindow.setModal(false); diff --git a/model/Rocket.cpp b/model/Rocket.cpp index 1ce0e14..144553f 100644 --- a/model/Rocket.cpp +++ b/model/Rocket.cpp @@ -1,17 +1,14 @@ #include "Rocket.h" #include "QtRocket.h" -Rocket::Rocket() : propagator(this) +Rocket::Rocket() { } void Rocket::launch() { - propagator.clearStates(); - propagator.setCurrentTime(0.0); mm.startMotor(0.0); - propagator.runUntilTerminate(); } void Rocket::setMotorModel(const model::MotorModel& motor) diff --git a/model/Rocket.h b/model/Rocket.h index f802d70..5473840 100644 --- a/model/Rocket.h +++ b/model/Rocket.h @@ -34,18 +34,6 @@ public: */ void launch(); - /** - * @brief getStates returns a vector of time/state pairs generated during launch() - * @return vector of pairs of doubles, where the first value is a time and the second a state vector - */ - const std::vector>>& getStates() const { return propagator.getStates(); } - - /** - * @brief setInitialState sets the initial state of the Rocket. - * @param initState initial state vector (x, y, z, xDot, yDot, zDot, pitch, yaw, roll, pitchDot, yawDot, rollDot) - */ - void setInitialState(const std::vector& initState) { propagator.setInitialState(initState); } - /** * @brief getMass returns the current mass of the rocket. This is the sum of all components' masses * @return total current mass of the Rocket @@ -105,12 +93,14 @@ public: * @param n name to set the Rocket */ void setName(const std::string& n) { name = n; } + + + private: std::vector bodyFrameState{ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; std::string name; /// Rocket name - sim::Propagator propagator; /// propagator double dragCoeff; /// @todo get rid of this, should be dynamically calculated double mass; /// @todo get rid of this, should be dynamically computed, but is the current rocket mass diff --git a/sim/Propagator.cpp b/sim/Propagator.cpp index c17738b..bb46605 100644 --- a/sim/Propagator.cpp +++ b/sim/Propagator.cpp @@ -22,7 +22,7 @@ namespace sim { -Propagator::Propagator(Rocket* r) +Propagator::Propagator(std::shared_ptr r) : linearIntegrator(), //orientationIntegrator(), rocket(r) diff --git a/sim/Propagator.h b/sim/Propagator.h index 97a04ee..916251d 100644 --- a/sim/Propagator.h +++ b/sim/Propagator.h @@ -24,7 +24,7 @@ namespace sim class Propagator { public: - Propagator(Rocket* r); + Propagator(std::shared_ptr r); ~Propagator(); void setInitialState(const std::vector& initialState) @@ -77,7 +77,8 @@ private: std::unique_ptr linearIntegrator; //std::unique_ptr orientationIntegrator; - Rocket* rocket; + std::shared_ptr rocket; + std::vector currentState{0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; std::vector tempRes{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; diff --git a/sim/StateData.h b/sim/StateData.h index e0b2567..f0a747b 100644 --- a/sim/StateData.h +++ b/sim/StateData.h @@ -25,6 +25,9 @@ public: Matrix3 dcm{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; + /// Euler angles are yaw-pitch-roll, and (3-2-1) order + Vector3 eulerAngles{0.0, 0.0, 0.0}; + // This is an array because the integrator expects it double data[6]; From d307bf47c68170c67b786c926d6e4ce5fa1b431b Mon Sep 17 00:00:00 2001 From: Travis Hunter Date: Wed, 10 May 2023 21:03:02 -0600 Subject: [PATCH 05/32] Split large CMakeLists into multiple subdirectory libraries. This will make unit testing easier on a per-component basis --- CMakeLists.txt | 66 ++++++++++++-------------------------------- model/CMakeLists.txt | 12 ++++++++ sim/CMakeLists.txt | 24 ++++++++++++++++ utils/CMakeLists.txt | 25 +++++++++++++++++ 4 files changed, 79 insertions(+), 48 deletions(-) create mode 100644 model/CMakeLists.txt create mode 100644 sim/CMakeLists.txt create mode 100644 utils/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 8c3c574..25d60ed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -89,51 +89,6 @@ set(PROJECT_SOURCES gui/ThrustCurveMotorSelector.ui gui/qcustomplot.cpp gui/qcustomplot.h - model/MotorModel.cpp - model/MotorModel.h - model/MotorModelDatabase.cpp - model/MotorModelDatabase.h - model/Rocket.cpp - model/Rocket.h - model/ThrustCurve.cpp - model/ThrustCurve.h - sim/AtmosphericModel.h - sim/ConstantAtmosphere.h - sim/ConstantGravityModel.h - sim/DESolver.h - sim/Environment.h - sim/GeoidModel.h - sim/GravityModel.h - sim/Propagator.cpp - sim/Propagator.h - sim/RK4Solver.h - sim/SphericalGeoidModel.cpp - sim/SphericalGeoidModel.h - sim/SphericalGravityModel.cpp - sim/SphericalGravityModel.h - sim/StateData.h - sim/USStandardAtmosphere.cpp - sim/USStandardAtmosphere.h - sim/WindModel.cpp - sim/WindModel.h - utils/BinMap.cpp - utils/BinMap.h - utils/CurlConnection.cpp - utils/CurlConnection.h - utils/Logger.cpp - utils/Logger.h - utils/MotorModelDatabase.cpp - utils/MotorModelDatabase.h - utils/RSEDatabaseLoader.cpp - utils/RSEDatabaseLoader.h - utils/ThreadPool.cpp - utils/ThreadPool.h - utils/ThrustCurveAPI.cpp - utils/ThrustCurveAPI.h - utils/TSQueue.h - utils/math/Constants.h - utils/math/MathTypes.h - utils/math/UtilityMathFunctions.h ${TS_FILES} ) @@ -162,11 +117,26 @@ else() ) endif() - #qt5_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES}) endif() -#target_link_libraries(qtrocket PRIVATE Qt${QT_VERSION_MAJOR}::Widgets Qt${Qt_VERSION_MAJOR}::PrintSupport libcurl jsoncpp_static fmt::fmt-header-only) -target_link_libraries(qtrocket PRIVATE Qt6::Widgets Qt6::PrintSupport libcurl jsoncpp_static fmt::fmt-header-only eigen) +add_subdirectory(utils) +add_subdirectory(sim) +add_subdirectory(model) + +#target_link_libraries(qtrocket PRIVATE +# Qt6::Widgets +# Qt6::PrintSupport +# libcurl +# jsoncpp_static +# fmt::fmt-header-only +# eigen) + +target_link_libraries(qtrocket PRIVATE + Qt6::Widgets + Qt6::PrintSupport + utils + sim + model) set_target_properties(qtrocket PROPERTIES MACOSX_BUNDLE_GUI_IDENTIFIER my.example.com diff --git a/model/CMakeLists.txt b/model/CMakeLists.txt new file mode 100644 index 0000000..bdac965 --- /dev/null +++ b/model/CMakeLists.txt @@ -0,0 +1,12 @@ +add_library(model + MotorModel.cpp + MotorModel.h + MotorModelDatabase.cpp + MotorModelDatabase.h + Rocket.cpp + Rocket.h + ThrustCurve.cpp + ThrustCurve.h) + +target_link_libraries(model PRIVATE + utils) diff --git a/sim/CMakeLists.txt b/sim/CMakeLists.txt new file mode 100644 index 0000000..9c113d0 --- /dev/null +++ b/sim/CMakeLists.txt @@ -0,0 +1,24 @@ +add_library(sim + AtmosphericModel.h + ConstantAtmosphere.h + ConstantGravityModel.h + DESolver.h + Environment.h + GeoidModel.h + GravityModel.h + Propagator.cpp + Propagator.h + RK4Solver.h + SphericalGeoidModel.cpp + SphericalGeoidModel.h + SphericalGravityModel.cpp + SphericalGravityModel.h + StateData.h + USStandardAtmosphere.cpp + USStandardAtmosphere.h + WindModel.cpp + WindModel.h) + +target_link_libraries(sim PRIVATE + utils) + diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt new file mode 100644 index 0000000..eebe01d --- /dev/null +++ b/utils/CMakeLists.txt @@ -0,0 +1,25 @@ +add_library(utils + BinMap.cpp + BinMap.h + CurlConnection.cpp + CurlConnection.h + Logger.cpp + Logger.h + MotorModelDatabase.cpp + MotorModelDatabase.h + RSEDatabaseLoader.cpp + RSEDatabaseLoader.h + ThreadPool.cpp + ThreadPool.h + ThrustCurveAPI.cpp + ThrustCurveAPI.h + TSQueue.h + math/Constants.h + math/MathTypes.h + math/UtilityMathFunctions.h) + +target_link_libraries(utils PUBLIC + libcurl + jsoncpp_static + fmt::fmt-header-only + eigen) From c7b453d25355361c86ae0cda331dd17ec1cb7ea2 Mon Sep 17 00:00:00 2001 From: Travis Hunter Date: Mon, 15 May 2023 18:36:47 -0600 Subject: [PATCH 06/32] added googletest and a USStandardAtmosphereTest --- CMakeLists.txt | 9 ++++++++ sim/CMakeLists.txt | 2 ++ sim/USStandardAtmosphere.cpp | 20 ++++++++--------- sim/tests/CMakeLists.txt | 14 ++++++++++++ sim/tests/USStandardAtmosphereTests.cpp | 29 +++++++++++++++++++++++++ 5 files changed, 64 insertions(+), 10 deletions(-) create mode 100644 sim/tests/CMakeLists.txt create mode 100644 sim/tests/USStandardAtmosphereTests.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 25d60ed..3381b5e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,6 +5,15 @@ project(qtrocket VERSION 0.1 LANGUAGES CXX) include(FetchContent) +# Google Test framework +FetchContent_Declare(googletest + GIT_REPOSITORY https://github.com/google/googletest + GIT_TAG v1.13.0) +if(WIN32) + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +endif() +FetchContent_MakeAvailable(googletest) + # fmtlib dependency FetchContent_Declare(fmt GIT_REPOSITORY https://github.com/fmtlib/fmt diff --git a/sim/CMakeLists.txt b/sim/CMakeLists.txt index 9c113d0..41e05e5 100644 --- a/sim/CMakeLists.txt +++ b/sim/CMakeLists.txt @@ -22,3 +22,5 @@ add_library(sim target_link_libraries(sim PRIVATE utils) +# Unit tests +add_subdirectory(tests) diff --git a/sim/USStandardAtmosphere.cpp b/sim/USStandardAtmosphere.cpp index c73d729..83e4753 100644 --- a/sim/USStandardAtmosphere.cpp +++ b/sim/USStandardAtmosphere.cpp @@ -10,6 +10,7 @@ // qtrocket headers #include "sim/USStandardAtmosphere.h" #include "utils/math/Constants.h" +#include "utils/math/UtilityMathFunctions.h" using namespace utils::math; @@ -35,13 +36,13 @@ utils::BinMap initTemps() utils::BinMap initLapseRates() { utils::BinMap map; - map.insert(std::make_pair(0.0, 0.0065)); + map.insert(std::make_pair(0.0, -0.0065)); map.insert(std::make_pair(11000.0, 0.0)); - map.insert(std::make_pair(20000.0, -0.001)); - map.insert(std::make_pair(32000.0, -0.0028)); + map.insert(std::make_pair(20000.0, 0.001)); + map.insert(std::make_pair(32000.0, 0.0028)); map.insert(std::make_pair(47000.0, 0.0)); - map.insert(std::make_pair(51000.0, 0.0028)); - map.insert(std::make_pair(71000.0, 0.002)); + map.insert(std::make_pair(51000.0, -0.0028)); + map.insert(std::make_pair(71000.0, -0.002)); return map; } @@ -78,7 +79,7 @@ USStandardAtmosphere::~USStandardAtmosphere() double USStandardAtmosphere::getDensity(double altitude) { - if(temperatureLapseRate[altitude] == 0.0) + if(utils::math::floatingPointEqual(temperatureLapseRate[altitude], 0.0)) { return standardDensity[altitude] * std::exp((-Constants::g0 * Constants::airMolarMass * (altitude - standardDensity.getBinBase(altitude))) / (Constants::Rstar * standardTemperature[altitude])); @@ -86,11 +87,10 @@ double USStandardAtmosphere::getDensity(double altitude) } else { - double base = standardTemperature[altitude] / - (standardTemperature[altitude] + temperatureLapseRate[altitude] * (altitude - standardDensity.getBinBase(altitude))); + double base = (standardTemperature[altitude] - temperatureLapseRate[altitude] * (altitude - standardDensity.getBinBase(altitude))) / standardTemperature[altitude]; - double exponent = 1 + (Constants::g0 * Constants::airMolarMass) / - (Constants::Rstar * temperatureLapseRate[altitude]); + double exponent = (Constants::g0 * Constants::airMolarMass) / + (Constants::Rstar * temperatureLapseRate[altitude]) - 1.0; return standardDensity[altitude] * std::pow(base, exponent); diff --git a/sim/tests/CMakeLists.txt b/sim/tests/CMakeLists.txt new file mode 100644 index 0000000..f928cd9 --- /dev/null +++ b/sim/tests/CMakeLists.txt @@ -0,0 +1,14 @@ +enable_testing() + +add_executable(sim_tests + USStandardAtmosphereTests.cpp +) + +target_link_libraries(sim_tests + sim + GTest::gtest_main +) + +include(GoogleTest) +gtest_discover_tests(sim_tests) + diff --git a/sim/tests/USStandardAtmosphereTests.cpp b/sim/tests/USStandardAtmosphereTests.cpp new file mode 100644 index 0000000..1424232 --- /dev/null +++ b/sim/tests/USStandardAtmosphereTests.cpp @@ -0,0 +1,29 @@ +#include + +#include "sim/USStandardAtmosphere.h" + +TEST(USStandardAtmosphereTests, DensityTests) +{ + sim::USStandardAtmosphere atmosphere; + + EXPECT_DOUBLE_EQ(atmosphere.getDensity(0.0), 1.225); + EXPECT_DOUBLE_EQ(atmosphere.getDensity(1000.0), 1.112); + EXPECT_DOUBLE_EQ(atmosphere.getDensity(2000.0), 1.007); + EXPECT_DOUBLE_EQ(atmosphere.getDensity(3000.0), 0.9093); + EXPECT_DOUBLE_EQ(atmosphere.getDensity(4000.0), 0.8194); + EXPECT_DOUBLE_EQ(atmosphere.getDensity(5000.0), 0.7364); + EXPECT_DOUBLE_EQ(atmosphere.getDensity(6000.0), 0.6601); + EXPECT_DOUBLE_EQ(atmosphere.getDensity(7000.0), 0.5900); + EXPECT_DOUBLE_EQ(atmosphere.getDensity(8000.0), 0.5258); + EXPECT_DOUBLE_EQ(atmosphere.getDensity(9000.0), 0.4647); + EXPECT_DOUBLE_EQ(atmosphere.getDensity(10000.0), 0.4135); + EXPECT_DOUBLE_EQ(atmosphere.getDensity(15000.0), 0.1948); + EXPECT_DOUBLE_EQ(atmosphere.getDensity(20000.0), 0.08891); + EXPECT_DOUBLE_EQ(atmosphere.getDensity(25000.0), 0.04008); + EXPECT_DOUBLE_EQ(atmosphere.getDensity(30000.0), 0.01841); + EXPECT_DOUBLE_EQ(atmosphere.getDensity(40000.0), 0.003996); + EXPECT_DOUBLE_EQ(atmosphere.getDensity(50000.0), 0.001027); + EXPECT_DOUBLE_EQ(atmosphere.getDensity(60000.0), 0.0003097); + EXPECT_DOUBLE_EQ(atmosphere.getDensity(70000.0), 0.00008283); + EXPECT_DOUBLE_EQ(atmosphere.getDensity(80000.0), 0.00001846); +} From 22c4e79f52e86e72d92f874f3ef5194954fc83b9 Mon Sep 17 00:00:00 2001 From: Travis Hunter Date: Thu, 18 May 2023 19:58:34 -0600 Subject: [PATCH 07/32] WIP --- CMakeLists.txt | 9 +-- QtRocket.h | 2 +- gui/AnalysisWindow.cpp | 4 +- main.cpp | 43 +------------ model/Rocket.cpp | 4 +- model/Rocket.h | 5 +- sim/DESolver.h | 6 +- sim/GravityModel.h | 2 +- sim/Propagator.cpp | 139 +++++++++++++++++++++++++++++++++-------- sim/Propagator.h | 62 +++++++++++------- sim/RK4Solver.h | 87 +++++++++++--------------- sim/StateData.h | 27 +++++++- utils/math/MathTypes.h | 14 ++++- 13 files changed, 244 insertions(+), 160 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8c3c574..b4932ee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,8 @@ cmake_minimum_required(VERSION 3.16) project(qtrocket VERSION 0.1 LANGUAGES CXX) +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) include(FetchContent) @@ -42,13 +44,13 @@ FetchContent_Declare(eigen GIT_TAG 3.4.0) FetchContent_MakeAvailable(eigen) +# Add qtrocket subdirectories. These are components that will be linked in + set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) -set(CMAKE_CXX_STANDARD 20) -set(CMAKE_CXX_STANDARD_REQUIRED ON) if(WIN32) set(CMAKE_PREFIX_PATH $ENV{QTDIR}) @@ -112,6 +114,7 @@ set(PROJECT_SOURCES sim/SphericalGravityModel.cpp sim/SphericalGravityModel.h sim/StateData.h + sim/TestRK4Solver.h sim/USStandardAtmosphere.cpp sim/USStandardAtmosphere.h sim/WindModel.cpp @@ -162,10 +165,8 @@ else() ) endif() - #qt5_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES}) endif() -#target_link_libraries(qtrocket PRIVATE Qt${QT_VERSION_MAJOR}::Widgets Qt${Qt_VERSION_MAJOR}::PrintSupport libcurl jsoncpp_static fmt::fmt-header-only) target_link_libraries(qtrocket PRIVATE Qt6::Widgets Qt6::PrintSupport libcurl jsoncpp_static fmt::fmt-header-only eigen) set_target_properties(qtrocket PROPERTIES diff --git a/QtRocket.h b/QtRocket.h index 02e301b..eaf2381 100644 --- a/QtRocket.h +++ b/QtRocket.h @@ -59,7 +59,7 @@ public: * @brief getStates returns a vector of time/state pairs generated during launch() * @return vector of pairs of doubles, where the first value is a time and the second a state vector */ - const std::vector>>& getStates() const { return rocket.second->getStates(); } + const std::vector>& getStates() const { return rocket.second->getStates(); } /** * @brief setInitialState sets the initial state of the Rocket. diff --git a/gui/AnalysisWindow.cpp b/gui/AnalysisWindow.cpp index 92c4e66..b8096f8 100644 --- a/gui/AnalysisWindow.cpp +++ b/gui/AnalysisWindow.cpp @@ -36,7 +36,7 @@ AnalysisWindow::~AnalysisWindow() void AnalysisWindow::onButton_plotAltitude_clicked() { QtRocket* qtRocket = QtRocket::getInstance(); - const std::vector>>& res = qtRocket->getStates(); + const std::vector>& res = qtRocket->getStates(); auto& plot = ui->plotWidget; plot->clearGraphs(); plot->setInteraction(QCP::iRangeDrag, true); @@ -63,7 +63,7 @@ void AnalysisWindow::onButton_plotAltitude_clicked() void AnalysisWindow::onButton_plotVelocity_clicked() { QtRocket* qtRocket = QtRocket::getInstance(); - const std::vector>>& res = qtRocket->getStates(); + const std::vector>& res = qtRocket->getStates(); auto& plot = ui->plotWidget; plot->clearGraphs(); plot->setInteraction(QCP::iRangeDrag, true); diff --git a/main.cpp b/main.cpp index e0d1885..02d1e70 100644 --- a/main.cpp +++ b/main.cpp @@ -23,45 +23,4 @@ int main(int argc, char *argv[]) int retVal = qtrocket->run(argc, argv); logger->debug("Returning"); return retVal; -} - - - -/* - * This was the old main when it directly started the QtGui. It worked. The new way of - * starting the gui through QtRocket->run() also seems to work, but I'm leaving this here for - * now in case there are unforeseen issues with starting the gui in a separate thread via - * QtRocket::run() - * - -#include "gui/MainWindow.h" - -#include -#include -#include - -int main(int argc, char* argv[]) -{ - QApplication a(argc, argv); - a.setWindowIcon(QIcon(":/qtrocket.png")); - - // Start translation component. - // TODO: Only support US English at the moment. Anyone want to help translate? - QTranslator translator; - const QStringList uiLanguages = QLocale::system().uiLanguages(); - for (const QString &locale : uiLanguages) - { - const QString baseName = "qtrocket_" + QLocale(locale).name(); - if (translator.load(":/i18n/" + baseName)) - { - a.installTranslator(&translator); - break; - } - } - - // Go! - MainWindow w; - w.show(); - return a.exec(); -} -*/ +} \ No newline at end of file diff --git a/model/Rocket.cpp b/model/Rocket.cpp index 144553f..7bf0fa5 100644 --- a/model/Rocket.cpp +++ b/model/Rocket.cpp @@ -1,3 +1,5 @@ + +// qtrocket headers #include "Rocket.h" #include "QtRocket.h" @@ -16,7 +18,7 @@ void Rocket::setMotorModel(const model::MotorModel& motor) mm = motor; } -bool Rocket::terminateCondition(const std::pair>& cond) +bool Rocket::terminateCondition(const std::pair& cond) { // Terminate propagation when the z coordinate drops below zero if(cond.second[2] < 0) diff --git a/model/Rocket.h b/model/Rocket.h index 5473840..2823843 100644 --- a/model/Rocket.h +++ b/model/Rocket.h @@ -15,6 +15,7 @@ #include "model/ThrustCurve.h" #include "model/MotorModel.h" #include "sim/Propagator.h" +#include "utils/math/MathTypes.h" /** * @brief The Rocket class holds all rocket components @@ -86,7 +87,7 @@ public: * @param cond time/state pair * @return true if the passed-in time/state satisfies the terminate condition */ - bool terminateCondition(const std::pair>& cond); + bool terminateCondition(const std::pair& cond); /** * @brief setName sets the rocket name @@ -98,8 +99,6 @@ public: private: - - std::vector bodyFrameState{ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; std::string name; /// Rocket name double dragCoeff; /// @todo get rid of this, should be dynamically calculated double mass; /// @todo get rid of this, should be dynamically computed, but is the current rocket mass diff --git a/sim/DESolver.h b/sim/DESolver.h index 36d04fa..54a4dee 100644 --- a/sim/DESolver.h +++ b/sim/DESolver.h @@ -9,9 +9,13 @@ // 3rd party headers /// \endcond +// qtrocket headers +#include "utils/math/MathTypes.h" + namespace sim { +template class DESolver { public: @@ -29,7 +33,7 @@ public: * the RK4 solver independently as a general tool, this interface is needed * here unfortunately. */ - virtual void step(const std::vector& curVal, std::vector& res, double t = 0.0) = 0; + virtual std::pair step(T& state, T& rate) = 0; }; } // namespace sim diff --git a/sim/GravityModel.h b/sim/GravityModel.h index 5f4085b..718dad1 100644 --- a/sim/GravityModel.h +++ b/sim/GravityModel.h @@ -14,7 +14,7 @@ public: virtual ~GravityModel() {} virtual Vector3 getAccel(double x, double y, double z) = 0; - Vector3 getAccel(const Vector3& t) { return this->getAccel(t[0], t[1], t[2]); } + Vector3 getAccel(const Vector3& t) { return this->getAccel(t.x(), t.y(), t.z()); } }; } // namespace sim diff --git a/sim/Propagator.cpp b/sim/Propagator.cpp index bb46605..c8344fb 100644 --- a/sim/Propagator.cpp +++ b/sim/Propagator.cpp @@ -24,9 +24,10 @@ namespace sim { Propagator::Propagator(std::shared_ptr r) : linearIntegrator(), - //orientationIntegrator(), - rocket(r) - + orientationIntegrator(), + rocket(r), + currentBodyState(), + worldFrameState() { @@ -38,23 +39,78 @@ Propagator::Propagator(std::shared_ptr r) // and pass it a freshly allocated RK4Solver pointer // The state vector has components of the form: - linearIntegrator.reset(new RK4Solver( - /* dx/dt */ [](const std::vector& s, double) -> double {return s[3]; }, - /* dy/dt */ [](const std::vector& s, double) -> double {return s[4]; }, - /* dz/dt */ [](const std::vector& s, double) -> double {return s[5]; }, - /* dvx/dt */ [this](const std::vector&, double ) -> double { return getForceX() / getMass(); }, - /* dvy/dt */ [this](const std::vector&, double ) -> double { return getForceY() / getMass(); }, - /* dvz/dt */ [this](const std::vector&, double ) -> double { return getForceZ() / getMass(); })); + + // Linear velocity and acceleration + std::function(Vector3&, Vector3&)> linearODE = [this](Vector3& state, Vector3& rate) -> std::pair + { + Vector3 dPosition; + Vector3 dVelocity; + // dx/dt + dPosition[0] = rate[0]; + + // dy/dt + dPosition[1] = rate[1]; + + // dz/dt + dPosition[2] = rate[2]; + + // dvx/dt + dVelocity[0] = getForceX() / getMass(); + + // dvy/dt + dVelocity[1] = getForceY() / getMass(); + + // dvz/dt + dVelocity[2] = getForceZ() / getMass(); + + return std::make_pair(dPosition, dVelocity); + + }; + + linearIntegrator.reset(new RK4Solver(linearODE)); linearIntegrator->setTimeStep(timeStep); + // This is pure quaternion + // This formula is taken from https://www.vectornav.com/resources/inertial-navigation-primer/math-fundamentals/math-attitudetran + // + // Convention for the quaternions is (vector, scalar). So q4 is scalar term + // + // q1Dot = 1/2 * [(q4*omega1) - (q3*omega2) + (q2*omega3)] + // q2Dot = 1/2 * [(q3*omega1) + (q4*omega2) - (q1*omega3)] + // q3Dot = 1/2 * [(-q2*omega1) + (q1*omega2) + (q4*omega3)] + // q4Dot = -1/2 *[(q1*omega1) + (q2*omega2) + (q3*omega3)] + // + // omega1 = yawRate + // omega2 = pitchRate + // omega3 = rollRate + // // orientationIntegrator.reset(new RK4Solver( -// /* dpitch/dt */ [](double, const std::vector& s) -> double { return s[3]; }, -// /* dyaw/dt */ [](double, const std::vector& s) -> double { return s[4]; }, -// /* droll/dt */ [](double, const std::vector& s) -> double { return s[5]; }, -// /* dpitchRate/dt */ [this](double, const std::vector& s) -> double { return (getTorqueP() - s[1] * s[2] * (getIroll() - getIyaw())) / getIpitch(); }, -// /* dyawRate/dt */ [this](double, const std::vector& s) -> double { return (getTorqueQ() - s[0] * s[2] * (getIpitch() - getIroll())) / getIyaw(); }, -// /* drollRate/dt */ [this](double, const std::vector& s) -> double { return (getTorqueR() - s[0] * s[1] * (getIyaw() - getIpitch())) / getIroll(); })); - +// /* dyawRate/dt */ [this](const std::vector& s, double) -> double +// { return getTorqueP() / getIyaw(); }, +// /* dpitchRate/dt */ [this](const std::vector& s, double) -> double +// { return getTorqueQ() / getIpitch(); }, +// /* drollRate/dt */ [this](const std::vector& s, double) -> double +// { return getTorqueR() / getIroll(); }, +// /* q1Dot */ [](const std::vector& s, double) -> double +// { +// double retVal = s[6]*s[0] - s[5]*s[1] + s[4]*s[2]; +// return 0.5 * retVal; +// }, +// /* q2Dot */ [](const std::vector& s, double) -> double +// { +// double retVal = s[5]*s[0] + s[6]*s[1] - s[3]*s[2]; +// return 0.5 * retVal; +// }, +// /* q3Dot */ [](const std::vector& s, double) -> double +// { +// double retVal = -s[4]*s[0] + s[3]*s[1] + s[6]*s[2]; +// return 0.5 * retVal; +// }, +// /* q4Dot */ [](const std::vector& s, double) -> double +// { +// double retVal = s[3]*s[0] + s[4]*s[1] + s[5]*s[2]; +// return -0.5 * retVal; +// })); // orientationIntegrator->setTimeStep(timeStep); saveStates = true; @@ -71,15 +127,22 @@ void Propagator::runUntilTerminate() while(true) { - // tempRes gets overwritten - linearIntegrator->step(currentState, tempRes); - std::swap(currentState, tempRes); + // Reset the body frame positions to zero since the origin of the body frame is the CM + currentBodyState[0] = 0.0; + currentBodyState[1] = 0.0; + currentBodyState[2] = 0.0; + + // tempRes gets overwritten + tempRes = linearIntegrator->step(currentBodyState); + + std::swap(currentBodyState, tempRes); + if(saveStates) { - states.push_back(std::make_pair(currentTime, currentState)); + states.push_back(std::make_pair(currentTime, currentBodyState)); } - if(rocket->terminateCondition(std::make_pair(currentTime, currentState))) + if(rocket->terminateCondition(std::make_pair(currentTime, currentBodyState))) break; currentTime += timeStep; @@ -101,20 +164,20 @@ double Propagator::getMass() double Propagator::getForceX() { QtRocket* qtrocket = QtRocket::getInstance(); - return (currentState[3] >= 0 ? -1.0 : 1.0) * qtrocket->getEnvironment()->getAtmosphericModel()->getDensity(currentState[2])/ 2.0 * 0.008107 * rocket->getDragCoefficient() * currentState[3]* currentState[3]; + return (currentBodyState[3] >= 0 ? -1.0 : 1.0) * qtrocket->getEnvironment()->getAtmosphericModel()->getDensity(currentBodyState[2])/ 2.0 * 0.008107 * rocket->getDragCoefficient() * currentBodyState[3]* currentBodyState[3]; } double Propagator::getForceY() { QtRocket* qtrocket = QtRocket::getInstance(); - return (currentState[4] >= 0 ? -1.0 : 1.0) * qtrocket->getEnvironment()->getAtmosphericModel()->getDensity(currentState[2]) / 2.0 * 0.008107 * rocket->getDragCoefficient() * currentState[4]* currentState[4]; + return (currentBodyState[4] >= 0 ? -1.0 : 1.0) * qtrocket->getEnvironment()->getAtmosphericModel()->getDensity(currentBodyState[2]) / 2.0 * 0.008107 * rocket->getDragCoefficient() * currentBodyState[4]* currentBodyState[4]; } double Propagator::getForceZ() { QtRocket* qtrocket = QtRocket::getInstance(); - double gravity = (qtrocket->getEnvironment()->getGravityModel()->getAccel(currentState[0], currentState[1], currentState[2]))[2]; - double airDrag = (currentState[5] >= 0 ? -1.0 : 1.0) * qtrocket->getEnvironment()->getAtmosphericModel()->getDensity(currentState[2]) / 2.0 * 0.008107 * rocket->getDragCoefficient() * currentState[5]* currentState[5]; + double gravity = (qtrocket->getEnvironment()->getGravityModel()->getAccel(currentBodyState[0], currentBodyState[1], currentBodyState[2]))[2]; + double airDrag = (currentBodyState[5] >= 0 ? -1.0 : 1.0) * qtrocket->getEnvironment()->getAtmosphericModel()->getDensity(currentBodyState[2]) / 2.0 * 0.008107 * rocket->getDragCoefficient() * currentBodyState[5]* currentBodyState[5]; double thrust = rocket->getThrust(currentTime); return gravity + airDrag + thrust; } @@ -123,4 +186,28 @@ double Propagator::getTorqueP() { return 0.0; } double Propagator::getTorqueQ() { return 0.0; } double Propagator::getTorqueR() { return 0.0; } +Vector3 Propagator::getCurrentGravity() +{ + auto gravityModel = QtRocket::getInstance()->getEnvironment()->getGravityModel(); + auto gravityAccel = gravityModel->getAccel(currentBodyState[0], + currentBodyState[1], + currentBodyState[2]); + Vector3 gravityVector{gravityAccel[0], + gravityAccel[1], + gravityAccel[2]}; + + + Quaternion q{currentOrientation[3], + currentOrientation[4], + currentOrientation[5], + currentOrientation[6]}; + + Vector3 res = q * gravityVector; + + + return Vector3{0.0, 0.0, 0.0}; + + +} + } // namespace sim diff --git a/sim/Propagator.h b/sim/Propagator.h index 916251d..d2ef7ca 100644 --- a/sim/Propagator.h +++ b/sim/Propagator.h @@ -12,6 +12,8 @@ // qtrocket headers #include "sim/DESolver.h" +#include "utils/math/MathTypes.h" +#include "sim/StateData.h" // Forward declare @@ -29,17 +31,16 @@ public: void setInitialState(const std::vector& initialState) { - currentState.resize(initialState.size()); for(std::size_t i = 0; i < initialState.size(); ++i) { - currentState[i] = initialState[i]; + currentBodyState[i] = initialState[i]; } } - const std::vector& getCurrentState() const + const Vector6& getCurrentState() const { - return currentState; + return currentBodyState; } void runUntilTerminate(); @@ -49,43 +50,56 @@ public: saveStates = s; } - const std::vector>>& getStates() const { return states; } + const std::vector>& getStates() const { return states; } void clearStates() { states.clear(); } void setCurrentTime(double t) { currentTime = t; } - void setTimeStep(double ts) { timeStep = ts; } - void setSaveStats(bool s) { saveStates = s; } private: - double getMass(); - double getForceX(); - double getForceY(); - double getForceZ(); + double getMass(); + double getForceX(); + double getForceY(); + double getForceZ(); - double getTorqueP(); - double getTorqueQ(); - double getTorqueR(); + double getTorqueP(); + double getTorqueQ(); + double getTorqueR(); - double getIpitch() { return 1.0; } - double getIyaw() { return 1.0; } - double getIroll() { return 1.0; } + double getIpitch() { return 1.0; } + double getIyaw() { return 1.0; } + double getIroll() { return 1.0; } -//private: - - std::unique_ptr linearIntegrator; - //std::unique_ptr orientationIntegrator; + std::unique_ptr> linearIntegrator; + std::unique_ptr> orientationIntegrator; std::shared_ptr rocket; + StateData worldFrameState; + //StateData bodyFrameState; + Vector3 currentBodyPosition{0.0, 0.0, 0.0}; + Vector3 currentBodyVelocity{0.0, 0.0, 0.0}; + Vector3 nextBodyPosition{0.0, 0.0, 0.0}; + Vector3 nextBodyVelocity{0.0, 0.0, 0.0}; - std::vector currentState{0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; - std::vector tempRes{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; + std::vector currentWorldState{0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; + + Vector3 currentGravity{0.0, 0.0, 0.0}; + Vector3 currentWindSpeed{0.0, 0.0, 0.0}; + + + // orientation vectors in the form (yawDot, pitchDot, rollDot, q1, q2, q3, q4) + Quaternion currentOrientation{0.0, 0.0, 0.0, 0.0}; + Quaternion currentOrientationRate{0.0, 0.0, 0.0, 0.0}; + Quaternion nextOrientation{0.0, 0.0, 0.0, 0.0}; + Quaternion nextOrientationRate{0.0, 0.0, 0.0, 0.0}; bool saveStates{true}; double currentTime{0.0}; double timeStep{0.01}; - std::vector>> states; + std::vector> states; + + Vector3 getCurrentGravity(); }; } // namespace sim diff --git a/sim/RK4Solver.h b/sim/RK4Solver.h index ec481da..46a13ec 100644 --- a/sim/RK4Solver.h +++ b/sim/RK4Solver.h @@ -16,6 +16,7 @@ // qtrocket headers #include "sim/DESolver.h" #include "utils/Logger.h" +#include "utils/math/MathTypes.h" namespace sim { @@ -28,81 +29,67 @@ namespace sim { * * @tparam Ts */ -template -class RK4Solver : public DESolver +template +class RK4Solver : public DESolver { public: - RK4Solver(Ts... funcs) + RK4Solver(std::function(T&, T&)> func) { - (odes.push_back(funcs), ...); - temp.resize(sizeof...(Ts)); - + // This only works for Eigen Vector types. + // TODO: Figure out how to make this slightly more generic, but for now + // we're only using this for Vector3 and Quaternion types + static_assert(std::is_same::value + || std::is_same::value, + "You can only use Vector3 or Quaternion valued functions in RK4Solver"); + + odes = func; } virtual ~RK4Solver() {} void setTimeStep(double inTs) override { dt = inTs; halfDT = dt / 2.0; } - void step(const std::vector& curVal, std::vector& res, double t = 0.0) override + std::pair step(T& state, T& rate) override { + std::pair res; if(dt == std::numeric_limits::quiet_NaN()) { utils::Logger::getInstance()->error("Calling RK4Solver without setting dt first is an error"); - res[0] = std::numeric_limits::quiet_NaN(); + return res; } - for(size_t i = 0; i < len; ++i) - { - k1[i] = odes[i](curVal, t); - } + std::tie(k1State, k1Rate) = odes(state, rate); // 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](temp, t + halfDT); - } - // 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](temp, t + halfDT); - } + std::tie(tempState, tempRate) = std::make_pair(state + k1State*halfDT, rate + k1Rate*halfDT); + std::tie(k2State, k2Rate) = odes(tempState, tempRate); - // 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](temp, t + dt); - } + std::tie(tempState, tempRate) = std::make_pair(state + k2State*halfDT, rate + k2Rate*halfDT); + std::tie(k3State, k3Rate) = odes(tempState, tempRate); - // 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]); - } + std::tie(tempState, tempRate) = std::make_pair(state + k3State*dt, rate + k3Rate*dt); + std::tie(k4State, k4Rate) = odes(tempState, tempRate); + res = std::make_pair(state + (dt / 6.0)*(k1State + 2.0*k2State + 2.0*k3State + k4State), + rate + (dt / 6.0)*(k1Rate + 2.0*k2Rate + 2.0*k3Rate + k4Rate)); + + return res; } private: - std::vector&, double)>> odes; + std::function(T&, T&)> odes; - static constexpr size_t len = sizeof...(Ts); - double k1[len]; - double k2[len]; - double k3[len]; - double k4[len]; + T k1State; + T k2State; + T k3State; + T k4State; + T k1Rate; + T k2Rate; + T k3Rate; + T k4Rate; - std::vector temp; + T tempState; + T tempRate; double dt = std::numeric_limits::quiet_NaN(); double halfDT = 0.0; diff --git a/sim/StateData.h b/sim/StateData.h index f0a747b..f20aede 100644 --- a/sim/StateData.h +++ b/sim/StateData.h @@ -1,6 +1,13 @@ #ifndef STATEDATA_H #define STATEDATA_H +/// \cond +// C headers +// C++ headers +#include +// 3rd party headers +/// \endcond + // qtrocket headers #include "utils/math/MathTypes.h" @@ -15,17 +22,33 @@ public: StateData() {} ~StateData() {} + std::vector getPosStdVector() + { + return std::vector{position[0], position[1], position[2]}; + } + std::vector getVelStdVector() + { + return std::vector{velocity[0], velocity[1], velocity[2]}; + } + /// TODO: Put these behind an interface // private: + + // These are 4-vectors so quaternion multiplication works out. The last term (scalar) is always + // zero. I could just use quaternions here, but I want to make it clear that these aren't + // rotations, they're vectors Vector3 position{0.0, 0.0, 0.0}; Vector3 velocity{0.0, 0.0, 0.0}; - Quaternion orientation{0.0, 0.0, 0.0, 0.0}; /// (scalar, vector) - Quaternion orientationRate{0.0, 0.0, 0.0, 0.0}; /// (scalar, vector) + Quaternion orientation{0.0, 0.0, 0.0, 0.0}; /// (vector, scalar) + Quaternion orientationRate{0.0, 0.0, 0.0, 0.0}; /// (vector, scalar) Matrix3 dcm{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; /// Euler angles are yaw-pitch-roll, and (3-2-1) order + /// yaw - psi + /// pitch - theta + /// roll - phi Vector3 eulerAngles{0.0, 0.0, 0.0}; // This is an array because the integrator expects it diff --git a/utils/math/MathTypes.h b/utils/math/MathTypes.h index 41551bb..9cb354c 100644 --- a/utils/math/MathTypes.h +++ b/utils/math/MathTypes.h @@ -6,10 +6,18 @@ /// This is not in any namespace. These typedefs are intended to be used throughout QtRocket, /// so keeping them in the global namespace seems to make sense. -typedef Eigen::Matrix3d Matrix3; -typedef Eigen::Matrix4d Matrix4; -typedef Eigen::Vector3d Vector3; +template +using MyMatrix = Eigen::Matrix; + +template +using MyVector = Eigen::Matrix; + typedef Eigen::Quaterniond Quaternion; +using Matrix3 = MyMatrix<3>; +using Matrix4 = MyMatrix<4>; + +using Vector3 = MyVector<3>; +using Vector6 = MyVector<6>; #endif // UTILS_MATH_MATHTYPES_H \ No newline at end of file From 505a66a1bef3a640d60dc68c06e6cd029283b90e Mon Sep 17 00:00:00 2001 From: Travis Hunter Date: Mon, 16 Oct 2023 14:55:31 -0600 Subject: [PATCH 08/32] WIP --- sim/Propagator.cpp | 11 ++++++++++- sim/Propagator.h | 4 ++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/sim/Propagator.cpp b/sim/Propagator.cpp index c8344fb..b33a15d 100644 --- a/sim/Propagator.cpp +++ b/sim/Propagator.cpp @@ -67,7 +67,7 @@ Propagator::Propagator(std::shared_ptr r) }; - linearIntegrator.reset(new RK4Solver(linearODE)); + linearIntegrator.reset(new RK4Solver(linearODE)); linearIntegrator->setTimeStep(timeStep); // This is pure quaternion @@ -113,6 +113,15 @@ Propagator::Propagator(std::shared_ptr r) // })); // orientationIntegrator->setTimeStep(timeStep); + std::function(Quaternion&, Quaternion&)> orientationODE = + [this](Quaternion& qOri, Quaternion& qRate) -> std::pair + { + Quaternion dOri; + Quaternion dOriRate; + + Matrix4 + } + saveStates = true; } diff --git a/sim/Propagator.h b/sim/Propagator.h index d2ef7ca..ada9686 100644 --- a/sim/Propagator.h +++ b/sim/Propagator.h @@ -11,7 +11,7 @@ /// \endcond // qtrocket headers -#include "sim/DESolver.h" +#include "sim/RK4Solver.h" #include "utils/math/MathTypes.h" #include "sim/StateData.h" @@ -72,7 +72,7 @@ private: double getIroll() { return 1.0; } std::unique_ptr> linearIntegrator; - std::unique_ptr> orientationIntegrator; +// std::unique_ptr> orientationIntegrator; std::shared_ptr rocket; From 4da21707b688e3bae572d44af7a5b4e8a43a4c97 Mon Sep 17 00:00:00 2001 From: Travis Hunter Date: Mon, 16 Oct 2023 17:40:33 -0600 Subject: [PATCH 09/32] Building and running again --- QtRocket.h | 4 +-- gui/AnalysisWindow.cpp | 8 ++--- gui/MainWindow.cpp | 5 +++- model/Rocket.cpp | 4 +-- model/Rocket.h | 2 +- sim/Propagator.cpp | 63 ++++++++++++++++++++-------------------- sim/Propagator.h | 32 +++++--------------- sim/StateData.h | 47 ++++++++++++++++++++++++++---- utils/ThrustCurveAPI.cpp | 8 ++++- 9 files changed, 102 insertions(+), 71 deletions(-) diff --git a/QtRocket.h b/QtRocket.h index eaf2381..7852928 100644 --- a/QtRocket.h +++ b/QtRocket.h @@ -59,13 +59,13 @@ public: * @brief getStates returns a vector of time/state pairs generated during launch() * @return vector of pairs of doubles, where the first value is a time and the second a state vector */ - const std::vector>& getStates() const { return rocket.second->getStates(); } + const std::vector>& getStates() const { return rocket.second->getStates(); } /** * @brief setInitialState sets the initial state of the Rocket. * @param initState initial state vector (x, y, z, xDot, yDot, zDot, pitch, yaw, roll, pitchDot, yawDot, rollDot) */ - void setInitialState(const std::vector& initState) { rocket.second->setInitialState(initState); } + void setInitialState(const StateData& initState) { rocket.second->setInitialState(initState); } private: QtRocket(); diff --git a/gui/AnalysisWindow.cpp b/gui/AnalysisWindow.cpp index b8096f8..de60f10 100644 --- a/gui/AnalysisWindow.cpp +++ b/gui/AnalysisWindow.cpp @@ -36,7 +36,7 @@ AnalysisWindow::~AnalysisWindow() void AnalysisWindow::onButton_plotAltitude_clicked() { QtRocket* qtRocket = QtRocket::getInstance(); - const std::vector>& res = qtRocket->getStates(); + const std::vector>& res = qtRocket->getStates(); auto& plot = ui->plotWidget; plot->clearGraphs(); plot->setInteraction(QCP::iRangeDrag, true); @@ -46,7 +46,7 @@ void AnalysisWindow::onButton_plotAltitude_clicked() for (int i = 0; i < tData.size(); ++i) { tData[i] = res[i].first; - zData[i] = res[i].second[2]; + zData[i] = res[i].second.position[2]; } // create graph and assign data to it: plot->addGraph(); @@ -63,7 +63,7 @@ void AnalysisWindow::onButton_plotAltitude_clicked() void AnalysisWindow::onButton_plotVelocity_clicked() { QtRocket* qtRocket = QtRocket::getInstance(); - const std::vector>& res = qtRocket->getStates(); + const std::vector>& res = qtRocket->getStates(); auto& plot = ui->plotWidget; plot->clearGraphs(); plot->setInteraction(QCP::iRangeDrag, true); @@ -74,7 +74,7 @@ void AnalysisWindow::onButton_plotVelocity_clicked() for (int i = 0; i < tData.size(); ++i) { tData[i] = res[i].first; - zData[i] = res[i].second[5]; + zData[i] = res[i].second.velocity[2]; } // create graph and assign data to it: plot->addGraph(); diff --git a/gui/MainWindow.cpp b/gui/MainWindow.cpp index 054c266..c2ab9c0 100644 --- a/gui/MainWindow.cpp +++ b/gui/MainWindow.cpp @@ -123,7 +123,10 @@ void MainWindow::onButton_calculateTrajectory_clicked() double initialVelocityX = initialVelocity * std::cos(initialAngle / 57.2958); double initialVelocityZ = initialVelocity * std::sin(initialAngle / 57.2958); - std::vector initialState = {0.0, 0.0, 0.0, initialVelocityX, 0.0, initialVelocityZ, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; + //std::vector initialState = {0.0, 0.0, 0.0, initialVelocityX, 0.0, initialVelocityZ, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; + StateData initialState; + initialState.position = {0.0, 0.0, 0.0}; + initialState.velocity = {initialVelocityX, 0.0, initialVelocityZ}; auto rocket = QtRocket::getInstance()->getRocket(); rocket->setMass(mass); rocket->setDragCoefficient(dragCoeff); diff --git a/model/Rocket.cpp b/model/Rocket.cpp index 7bf0fa5..350dd65 100644 --- a/model/Rocket.cpp +++ b/model/Rocket.cpp @@ -18,10 +18,10 @@ void Rocket::setMotorModel(const model::MotorModel& motor) mm = motor; } -bool Rocket::terminateCondition(const std::pair& cond) +bool Rocket::terminateCondition(const std::pair& cond) { // Terminate propagation when the z coordinate drops below zero - if(cond.second[2] < 0) + if(cond.second.position[2] < 0) return true; else return false; diff --git a/model/Rocket.h b/model/Rocket.h index 2823843..d7c874f 100644 --- a/model/Rocket.h +++ b/model/Rocket.h @@ -87,7 +87,7 @@ public: * @param cond time/state pair * @return true if the passed-in time/state satisfies the terminate condition */ - bool terminateCondition(const std::pair& cond); + bool terminateCondition(const std::pair& cond); /** * @brief setName sets the rocket name diff --git a/sim/Propagator.cpp b/sim/Propagator.cpp index b33a15d..5f7a998 100644 --- a/sim/Propagator.cpp +++ b/sim/Propagator.cpp @@ -24,10 +24,16 @@ namespace sim { Propagator::Propagator(std::shared_ptr r) : linearIntegrator(), - orientationIntegrator(), + //orientationIntegrator(), rocket(r), - currentBodyState(), - worldFrameState() + currentState(), + nextState(), + currentGravity(), + currentWindSpeed(), + saveStates(true), + currentTime(0.0), + timeStep(0.01), + states() { @@ -113,16 +119,16 @@ Propagator::Propagator(std::shared_ptr r) // })); // orientationIntegrator->setTimeStep(timeStep); - std::function(Quaternion&, Quaternion&)> orientationODE = - [this](Quaternion& qOri, Quaternion& qRate) -> std::pair - { - Quaternion dOri; - Quaternion dOriRate; +// std::function(Quaternion&, Quaternion&)> orientationODE = +// [this](Quaternion& qOri, Quaternion& qRate) -> std::pair +// { +// Quaternion dOri; +// Quaternion dOriRate; - Matrix4 - } +// Matrix4 +// } - saveStates = true; + saveStates = true; } Propagator::~Propagator() @@ -134,27 +140,25 @@ void Propagator::runUntilTerminate() std::chrono::steady_clock::time_point startTime = std::chrono::steady_clock::now(); std::chrono::steady_clock::time_point endTime; + currentState.position = {0.0, 0.0, 0.0}; while(true) { - // Reset the body frame positions to zero since the origin of the body frame is the CM - currentBodyState[0] = 0.0; - currentBodyState[1] = 0.0; - currentBodyState[2] = 0.0; - // tempRes gets overwritten - tempRes = linearIntegrator->step(currentBodyState); + std::tie(nextState.position, nextState.velocity) = linearIntegrator->step(currentState.position, currentState.velocity); + + //tempRes = linearIntegrator->step(currentBodyState); - std::swap(currentBodyState, tempRes); if(saveStates) { - states.push_back(std::make_pair(currentTime, currentBodyState)); + states.push_back(std::make_pair(currentTime, nextState)); } - if(rocket->terminateCondition(std::make_pair(currentTime, currentBodyState))) + if(rocket->terminateCondition(std::make_pair(currentTime, currentState))) break; currentTime += timeStep; + currentState = nextState; } endTime = std::chrono::steady_clock::now(); @@ -173,20 +177,20 @@ double Propagator::getMass() double Propagator::getForceX() { QtRocket* qtrocket = QtRocket::getInstance(); - return (currentBodyState[3] >= 0 ? -1.0 : 1.0) * qtrocket->getEnvironment()->getAtmosphericModel()->getDensity(currentBodyState[2])/ 2.0 * 0.008107 * rocket->getDragCoefficient() * currentBodyState[3]* currentBodyState[3]; + return (currentState.velocity[0] >= 0 ? -1.0 : 1.0) * qtrocket->getEnvironment()->getAtmosphericModel()->getDensity(currentState.position[2])/ 2.0 * 0.008107 * rocket->getDragCoefficient() * currentState.velocity[0]* currentState.velocity[0]; } double Propagator::getForceY() { QtRocket* qtrocket = QtRocket::getInstance(); - return (currentBodyState[4] >= 0 ? -1.0 : 1.0) * qtrocket->getEnvironment()->getAtmosphericModel()->getDensity(currentBodyState[2]) / 2.0 * 0.008107 * rocket->getDragCoefficient() * currentBodyState[4]* currentBodyState[4]; + return (currentState.velocity[1] >= 0 ? -1.0 : 1.0) * qtrocket->getEnvironment()->getAtmosphericModel()->getDensity(currentState.position[2]) / 2.0 * 0.008107 * rocket->getDragCoefficient() * currentState.velocity[1]* currentState.velocity[1]; } double Propagator::getForceZ() { QtRocket* qtrocket = QtRocket::getInstance(); - double gravity = (qtrocket->getEnvironment()->getGravityModel()->getAccel(currentBodyState[0], currentBodyState[1], currentBodyState[2]))[2]; - double airDrag = (currentBodyState[5] >= 0 ? -1.0 : 1.0) * qtrocket->getEnvironment()->getAtmosphericModel()->getDensity(currentBodyState[2]) / 2.0 * 0.008107 * rocket->getDragCoefficient() * currentBodyState[5]* currentBodyState[5]; + double gravity = (qtrocket->getEnvironment()->getGravityModel()->getAccel(currentState.position[0], currentState.position[1], currentState.position[2]))[2]; + double airDrag = (currentState.velocity[2] >= 0 ? -1.0 : 1.0) * qtrocket->getEnvironment()->getAtmosphericModel()->getDensity(currentState.position[2]) / 2.0 * 0.008107 * rocket->getDragCoefficient() * currentState.velocity[2]* currentState.velocity[2]; double thrust = rocket->getThrust(currentTime); return gravity + airDrag + thrust; } @@ -198,18 +202,15 @@ double Propagator::getTorqueR() { return 0.0; } Vector3 Propagator::getCurrentGravity() { auto gravityModel = QtRocket::getInstance()->getEnvironment()->getGravityModel(); - auto gravityAccel = gravityModel->getAccel(currentBodyState[0], - currentBodyState[1], - currentBodyState[2]); + auto gravityAccel = gravityModel->getAccel(currentState.position[0], + currentState.position[1], + currentState.position[2]); Vector3 gravityVector{gravityAccel[0], gravityAccel[1], gravityAccel[2]}; - Quaternion q{currentOrientation[3], - currentOrientation[4], - currentOrientation[5], - currentOrientation[6]}; + Quaternion q = currentState.orientation; Vector3 res = q * gravityVector; diff --git a/sim/Propagator.h b/sim/Propagator.h index ada9686..2926101 100644 --- a/sim/Propagator.h +++ b/sim/Propagator.h @@ -29,18 +29,14 @@ public: Propagator(std::shared_ptr r); ~Propagator(); - void setInitialState(const std::vector& initialState) + void setInitialState(const StateData& initialState) { - for(std::size_t i = 0; i < initialState.size(); ++i) - { - currentBodyState[i] = initialState[i]; - } - + currentState = initialState; } - const Vector6& getCurrentState() const + const StateData& getCurrentState() const { - return currentBodyState; + return currentState; } void runUntilTerminate(); @@ -50,7 +46,7 @@ public: saveStates = s; } - const std::vector>& getStates() const { return states; } + const std::vector>& getStates() const { return states; } void clearStates() { states.clear(); } void setCurrentTime(double t) { currentTime = t; } @@ -76,28 +72,16 @@ private: std::shared_ptr rocket; - StateData worldFrameState; - //StateData bodyFrameState; - Vector3 currentBodyPosition{0.0, 0.0, 0.0}; - Vector3 currentBodyVelocity{0.0, 0.0, 0.0}; - Vector3 nextBodyPosition{0.0, 0.0, 0.0}; - Vector3 nextBodyVelocity{0.0, 0.0, 0.0}; - - std::vector currentWorldState{0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; + StateData currentState; + StateData nextState; Vector3 currentGravity{0.0, 0.0, 0.0}; Vector3 currentWindSpeed{0.0, 0.0, 0.0}; - - // orientation vectors in the form (yawDot, pitchDot, rollDot, q1, q2, q3, q4) - Quaternion currentOrientation{0.0, 0.0, 0.0, 0.0}; - Quaternion currentOrientationRate{0.0, 0.0, 0.0, 0.0}; - Quaternion nextOrientation{0.0, 0.0, 0.0, 0.0}; - Quaternion nextOrientationRate{0.0, 0.0, 0.0, 0.0}; bool saveStates{true}; double currentTime{0.0}; double timeStep{0.01}; - std::vector> states; + std::vector> states; Vector3 getCurrentGravity(); }; diff --git a/sim/StateData.h b/sim/StateData.h index f20aede..43c820f 100644 --- a/sim/StateData.h +++ b/sim/StateData.h @@ -22,16 +22,56 @@ public: StateData() {} ~StateData() {} - std::vector getPosStdVector() + StateData(const StateData&) = default; + StateData(StateData&&) = default; + + StateData& operator=(const StateData& rhs) + { + if(this != &rhs) + { + position = rhs.position; + velocity = rhs.velocity; + orientation = rhs.orientation; + orientationRate = rhs.orientationRate; + dcm = rhs.dcm; + eulerAngles = rhs.eulerAngles; + } + return *this; + } + StateData& operator=(StateData&& rhs) + { + if(this != &rhs) + { + position = std::move(rhs.position); + velocity = std::move(rhs.velocity); + orientation = std::move(rhs.orientation); + orientationRate = std::move(rhs.orientationRate); + dcm = std::move(rhs.dcm); + eulerAngles = std::move(rhs.eulerAngles); + } + return *this; + } + + std::vector getPosStdVector() const { return std::vector{position[0], position[1], position[2]}; } - std::vector getVelStdVector() + std::vector getVelStdVector() const { return std::vector{velocity[0], velocity[1], velocity[2]}; } + /// TODO: Put these behind an interface + //Vector3 getPosition() const + //{ + // return position; + //} + + //Vector3 getVelocity() const + //{ + // return velocity; + //} // private: // These are 4-vectors so quaternion multiplication works out. The last term (scalar) is always @@ -51,9 +91,6 @@ public: /// roll - phi Vector3 eulerAngles{0.0, 0.0, 0.0}; - // This is an array because the integrator expects it - double data[6]; - }; #endif // STATEDATA_H diff --git a/utils/ThrustCurveAPI.cpp b/utils/ThrustCurveAPI.cpp index bd4c8c5..04b7a01 100644 --- a/utils/ThrustCurveAPI.cpp +++ b/utils/ThrustCurveAPI.cpp @@ -235,7 +235,9 @@ std::vector ThrustCurveAPI::searchMotors(const SearchCriteria { Json::Reader reader; Json::Value jsonResult; +Logger::getInstance()->debug("1"); reader.parse(result, jsonResult); +Logger::getInstance()->debug("2"); for(Json::ValueConstIterator iter = jsonResult["results"].begin(); iter != jsonResult["results"].end(); @@ -244,6 +246,7 @@ std::vector ThrustCurveAPI::searchMotors(const SearchCriteria model::MotorModel motorModel; model::MotorModel::MetaData mm; mm.commonName = (*iter)["commonName"].asString(); +Logger::getInstance()->debug("3"); std::string availability = (*iter)["availability"].asString(); if(availability == "regular") @@ -253,6 +256,7 @@ std::vector ThrustCurveAPI::searchMotors(const SearchCriteria mm.avgThrust = (*iter)["avgThrustN"].asDouble(); mm.burnTime = (*iter)["burnTimeS"].asDouble(); +Logger::getInstance()->debug("4"); // TODO fill in certOrg // TODO fill in delays mm.designation = (*iter)["designation"].asString(); @@ -279,8 +283,10 @@ std::vector ThrustCurveAPI::searchMotors(const SearchCriteria else mm.type = model::MotorModel::MotorType(model::MotorModel::MOTORTYPE::HYBRID); - motorModel.moveMetaData(std::move(mm)); +Logger::getInstance()->debug("5"); auto tc = getThrustCurve(mm.motorIdTC); + motorModel.moveMetaData(std::move(mm)); +Logger::getInstance()->debug("6"); if(tc) { motorModel.addThrustCurve(*tc); From 9391b85a07379981c84704400b98a06efbcf807a Mon Sep 17 00:00:00 2001 From: Travis Hunter Date: Thu, 19 Oct 2023 08:33:34 -0600 Subject: [PATCH 10/32] WIP. Started building model/Part component --- model/CMakeLists.txt | 2 ++ model/MotorModel.cpp | 1 + model/Part.cpp | 6 +++++ model/Part.h | 61 ++++++++++++++++++++++++++++++++++++++++++ sim/Propagator.h | 8 +++--- utils/math/MathTypes.h | 51 +++++++++++++++++++++++++++++++++++ 6 files changed, 125 insertions(+), 4 deletions(-) create mode 100644 model/Part.cpp create mode 100644 model/Part.h diff --git a/model/CMakeLists.txt b/model/CMakeLists.txt index bdac965..72df3c1 100644 --- a/model/CMakeLists.txt +++ b/model/CMakeLists.txt @@ -3,6 +3,8 @@ add_library(model MotorModel.h MotorModelDatabase.cpp MotorModelDatabase.h + Part.cpp + Part.h Rocket.cpp Rocket.h ThrustCurve.cpp diff --git a/model/MotorModel.cpp b/model/MotorModel.cpp index 63c393e..a3aecbd 100644 --- a/model/MotorModel.cpp +++ b/model/MotorModel.cpp @@ -60,6 +60,7 @@ double MotorModel::getMass(double simTime) const double propMassEnd = i->second; double slope = (propMassEnd - propMassStart) / (tEnd - tStart); double currentMass = emptyMass + propMassStart + (thrustTime - tStart) * slope; + utils::Logger::getInstance()->info("simTime: " + std::to_string(simTime) + ": motor mass: " + std::to_string(currentMass)); return currentMass; } diff --git a/model/Part.cpp b/model/Part.cpp new file mode 100644 index 0000000..fd3e890 --- /dev/null +++ b/model/Part.cpp @@ -0,0 +1,6 @@ +#include "Part.h" + +namespace model +{ + +} // namespace model \ No newline at end of file diff --git a/model/Part.h b/model/Part.h new file mode 100644 index 0000000..e766fca --- /dev/null +++ b/model/Part.h @@ -0,0 +1,61 @@ +#ifndef MODEL_PART_H +#define MODEL_PART_H + +/// \cond +// C headers +// C++ headers +#include +#include +#include +#include + +// 3rd party headers +/// \endcond + +// qtrocket headers +#include "utils/math/MathTypes.h" + +namespace model +{ + +class Part +{ +public: + Part(); + virtual ~Part(); + + void setMass(double m) { mass = m; } + + // Set the inertia tensor + void setI(const Matrix3& I) { inertiaTensor = I; } + + void setCm(const Vector3& x) { cm = x; } + // Special version of setCM that assumes the cm lies along the body x-axis + void setCm(double x) { cm = {x, 0.0, 0.0}; } + + void setLength(double l) { length = l; } + + void setInnerRadius(double r) { innerRadiusTop = r; innerRadiusBottom = r; } + void setOuterRadius(double r) { outerRadiusTop = r; outerRadiusBottom = r; } +private: + + Matrix3 inertiaTensor; // moment of inertia tensor with respect to the part's center of mass and + // + double mass; // The moment of inertia tensor also has this, so don't double compute + + Vector3 cm; // center of mass wrt middle of component + + double length; + + double innerRadiusTop; + double outerRadiusTop; + + double innerRadiusBottom; + double outerRadiusBottom; + + +}; + +} + +#endif // MODEL_PART_H \ No newline at end of file diff --git a/sim/Propagator.h b/sim/Propagator.h index 2926101..ec7fbe4 100644 --- a/sim/Propagator.h +++ b/sim/Propagator.h @@ -59,12 +59,12 @@ private: double getForceY(); double getForceZ(); - double getTorqueP(); - double getTorqueQ(); - double getTorqueR(); + double getTorqueP(); // yaw + double getTorqueQ(); // pitch + double getTorqueR(); // roll - double getIpitch() { return 1.0; } double getIyaw() { return 1.0; } + double getIpitch() { return 1.0; } double getIroll() { return 1.0; } std::unique_ptr> linearIntegrator; diff --git a/utils/math/MathTypes.h b/utils/math/MathTypes.h index 9cb354c..9eba8a1 100644 --- a/utils/math/MathTypes.h +++ b/utils/math/MathTypes.h @@ -2,6 +2,7 @@ #define UTILS_MATH_MATHTYPES_H #include +#include /// This is not in any namespace. These typedefs are intended to be used throughout QtRocket, /// so keeping them in the global namespace seems to make sense. @@ -20,4 +21,54 @@ using Matrix4 = MyMatrix<4>; using Vector3 = MyVector<3>; using Vector6 = MyVector<6>; +/* +namespace utils +{ + std::vector myVectorToStdVector(const Vector3& x) + { + return std::vector{x.coeff(0), x.coeff(1), x.coeff(2)}; + } + + std::vector myVectorToStdVector(const Vector6& x) + { + return std::vector{x.coeff(0), + x.coeff(1), + x.coeff(2), + x.coeff(3), + x.coeff(4), + x.coeff(5)}; + } +} + +class Vector3 : public MyVector<3> +{ +public: + template + Vector3(Args&&... args) : MyVector<3>(std::forward(args)...) + {} + operator std::vector() + { + return std::vector{this->coeff(0), this->coeff(1), this->coeff(2)}; + } +}; + +class Vector6 : public MyVector<6> +{ +public: + template + Vector6(Args&&... args) : MyVector<6>(std::forward(args)...) + {} + operator std::vector() + { + return std::vector{this->coeff(0), + this->coeff(1), + this->coeff(2), + this->coeff(3), + this->coeff(4), + this->coeff(5)}; + } +}; +*/ + + #endif // UTILS_MATH_MATHTYPES_H \ No newline at end of file From 9b807d53a4e074f0c8f7ea7d53e473479479a189 Mon Sep 17 00:00:00 2001 From: Travis Hunter Date: Thu, 19 Oct 2023 10:58:01 -0600 Subject: [PATCH 11/32] Fixed US Standard Atmosphere class and tests --- sim/USStandardAtmosphere.cpp | 59 ++++++++++++++++++++----- sim/USStandardAtmosphere.h | 1 + sim/tests/USStandardAtmosphereTests.cpp | 42 +++++++++--------- utils/BinMap.cpp | 2 +- 4 files changed, 72 insertions(+), 32 deletions(-) diff --git a/sim/USStandardAtmosphere.cpp b/sim/USStandardAtmosphere.cpp index 83e4753..54231ba 100644 --- a/sim/USStandardAtmosphere.cpp +++ b/sim/USStandardAtmosphere.cpp @@ -36,13 +36,13 @@ utils::BinMap initTemps() utils::BinMap initLapseRates() { utils::BinMap map; - map.insert(std::make_pair(0.0, -0.0065)); + map.insert(std::make_pair(0.0, 0.0065)); map.insert(std::make_pair(11000.0, 0.0)); - map.insert(std::make_pair(20000.0, 0.001)); - map.insert(std::make_pair(32000.0, 0.0028)); + map.insert(std::make_pair(20000.0, -0.001)); + map.insert(std::make_pair(32000.0, -0.0028)); map.insert(std::make_pair(47000.0, 0.0)); - map.insert(std::make_pair(51000.0, -0.0028)); - map.insert(std::make_pair(71000.0, -0.002)); + map.insert(std::make_pair(51000.0, 0.0028)); + map.insert(std::make_pair(71000.0, 0.002)); return map; } @@ -61,9 +61,24 @@ utils::BinMap initDensities() return map; } +utils::BinMap initPressures() +{ + utils::BinMap map; + map.insert(std::make_pair(0.0, 101325)); + map.insert(std::make_pair(11000.0, 22632.1)); + map.insert(std::make_pair(20000.0, 5474.89)); + map.insert(std::make_pair(32000.0, 868.02)); + map.insert(std::make_pair(47000.0, 110.91)); + map.insert(std::make_pair(51000.0, 66.94)); + map.insert(std::make_pair(71000.0, 3.96)); + + return map; +} + utils::BinMap USStandardAtmosphere::temperatureLapseRate(initLapseRates()); utils::BinMap USStandardAtmosphere::standardTemperature(initTemps()); utils::BinMap USStandardAtmosphere::standardDensity(initDensities()); +utils::BinMap USStandardAtmosphere::standardPressure(initPressures()); @@ -81,28 +96,50 @@ double USStandardAtmosphere::getDensity(double altitude) { if(utils::math::floatingPointEqual(temperatureLapseRate[altitude], 0.0)) { - return standardDensity[altitude] * std::exp((-Constants::g0 * Constants::airMolarMass * (altitude - standardDensity.getBinBase(altitude))) + return standardDensity[altitude] * std::exp( + (-Constants::g0 * Constants::airMolarMass * (altitude - standardDensity.getBinBase(altitude))) / (Constants::Rstar * standardTemperature[altitude])); } else { - double base = (standardTemperature[altitude] - temperatureLapseRate[altitude] * (altitude - standardDensity.getBinBase(altitude))) / standardTemperature[altitude]; + double base = (standardTemperature[altitude] - temperatureLapseRate[altitude] * + (altitude - standardDensity.getBinBase(altitude))) / standardTemperature[altitude]; double exponent = (Constants::g0 * Constants::airMolarMass) / (Constants::Rstar * temperatureLapseRate[altitude]) - 1.0; return standardDensity[altitude] * std::pow(base, exponent); - } } -double USStandardAtmosphere::getTemperature(double /*altitude*/) +double USStandardAtmosphere::getTemperature(double altitude) { + double baseTemp = standardTemperature[altitude]; + double baseAltitude = standardTemperature.getBinBase(altitude); + return baseTemp - (altitude - baseAltitude) * temperatureLapseRate[altitude]; + return 0.0; } -double USStandardAtmosphere::getPressure(double /* altitude */) +double USStandardAtmosphere::getPressure(double altitude) { - return 0.0; + if(utils::math::floatingPointEqual(temperatureLapseRate[altitude], 0.0)) + { + return standardPressure[altitude] * std::exp( + (-Constants::g0 * Constants::airMolarMass * (altitude - standardPressure.getBinBase(altitude))) + / (Constants::Rstar * standardTemperature[altitude])); + + } + else + { + double base = (standardTemperature[altitude] - temperatureLapseRate[altitude] * + (altitude - standardPressure.getBinBase(altitude))) / standardTemperature[altitude]; + + double exponent = (Constants::g0 * Constants::airMolarMass) / + (Constants::Rstar * temperatureLapseRate[altitude]); + + return standardPressure[altitude] * std::pow(base, exponent); + } + } } // namespace sim diff --git a/sim/USStandardAtmosphere.h b/sim/USStandardAtmosphere.h index 2835d81..4b18083 100644 --- a/sim/USStandardAtmosphere.h +++ b/sim/USStandardAtmosphere.h @@ -32,6 +32,7 @@ private: static utils::BinMap temperatureLapseRate; static utils::BinMap standardTemperature; static utils::BinMap standardDensity; + static utils::BinMap standardPressure; }; diff --git a/sim/tests/USStandardAtmosphereTests.cpp b/sim/tests/USStandardAtmosphereTests.cpp index 1424232..a1375a4 100644 --- a/sim/tests/USStandardAtmosphereTests.cpp +++ b/sim/tests/USStandardAtmosphereTests.cpp @@ -6,24 +6,26 @@ TEST(USStandardAtmosphereTests, DensityTests) { sim::USStandardAtmosphere atmosphere; - EXPECT_DOUBLE_EQ(atmosphere.getDensity(0.0), 1.225); - EXPECT_DOUBLE_EQ(atmosphere.getDensity(1000.0), 1.112); - EXPECT_DOUBLE_EQ(atmosphere.getDensity(2000.0), 1.007); - EXPECT_DOUBLE_EQ(atmosphere.getDensity(3000.0), 0.9093); - EXPECT_DOUBLE_EQ(atmosphere.getDensity(4000.0), 0.8194); - EXPECT_DOUBLE_EQ(atmosphere.getDensity(5000.0), 0.7364); - EXPECT_DOUBLE_EQ(atmosphere.getDensity(6000.0), 0.6601); - EXPECT_DOUBLE_EQ(atmosphere.getDensity(7000.0), 0.5900); - EXPECT_DOUBLE_EQ(atmosphere.getDensity(8000.0), 0.5258); - EXPECT_DOUBLE_EQ(atmosphere.getDensity(9000.0), 0.4647); - EXPECT_DOUBLE_EQ(atmosphere.getDensity(10000.0), 0.4135); - EXPECT_DOUBLE_EQ(atmosphere.getDensity(15000.0), 0.1948); - EXPECT_DOUBLE_EQ(atmosphere.getDensity(20000.0), 0.08891); - EXPECT_DOUBLE_EQ(atmosphere.getDensity(25000.0), 0.04008); - EXPECT_DOUBLE_EQ(atmosphere.getDensity(30000.0), 0.01841); - EXPECT_DOUBLE_EQ(atmosphere.getDensity(40000.0), 0.003996); - EXPECT_DOUBLE_EQ(atmosphere.getDensity(50000.0), 0.001027); - EXPECT_DOUBLE_EQ(atmosphere.getDensity(60000.0), 0.0003097); - EXPECT_DOUBLE_EQ(atmosphere.getDensity(70000.0), 0.00008283); - EXPECT_DOUBLE_EQ(atmosphere.getDensity(80000.0), 0.00001846); + // Test that the calucated values are with 1% of the published values in the NOAA report + EXPECT_NEAR(atmosphere.getDensity(0.0) / 1.225, 1.0, 0.01); + EXPECT_NEAR(atmosphere.getDensity(1000.0) / 1.112, 1.0, 0.01); + EXPECT_NEAR(atmosphere.getDensity(2000.0) / 1.007, 1.0, 0.01); + EXPECT_NEAR(atmosphere.getDensity(3000.0) / 0.9093, 1.0, 0.01); + EXPECT_NEAR(atmosphere.getDensity(4000.0) / 0.8194, 1.0, 0.01); + EXPECT_NEAR(atmosphere.getDensity(5000.0) / 0.7364, 1.0, 0.01); + EXPECT_NEAR(atmosphere.getDensity(6000.0) / 0.6601, 1.0, 0.01); + EXPECT_NEAR(atmosphere.getDensity(7000.0) / 0.5900, 1.0, 0.01); + EXPECT_NEAR(atmosphere.getDensity(8000.0) / 0.5258, 1.0, 0.01); + EXPECT_NEAR(atmosphere.getDensity(9000.0) / 0.4647, 1.0, 0.01); + EXPECT_NEAR(atmosphere.getDensity(10000.0) / 0.4135, 1.0, 0.01); + EXPECT_NEAR(atmosphere.getDensity(15000.0) / 0.1948, 1.0, 0.01); + EXPECT_NEAR(atmosphere.getDensity(20000.0) / 0.08891, 1.0, 0.01); + EXPECT_NEAR(atmosphere.getDensity(25000.0) / 0.039466, 1.0, 0.01); + EXPECT_NEAR(atmosphere.getDensity(30000.0) / 0.018012, 1.0, 0.01); + EXPECT_NEAR(atmosphere.getDensity(40000.0) / 0.0038510, 1.0, 0.01); + EXPECT_NEAR(atmosphere.getDensity(50000.0) / 0.00097752, 1.0, 0.01); + EXPECT_NEAR(atmosphere.getDensity(60000.0) / 0.00028832, 1.0, 0.01); + EXPECT_NEAR(atmosphere.getDensity(70000.0) / 0.000074243, 1.0, 0.01); + EXPECT_NEAR(atmosphere.getDensity(80000.0) / 0.000015701, 1.0, 0.01); + } diff --git a/utils/BinMap.cpp b/utils/BinMap.cpp index 95d17d6..5ebe68b 100644 --- a/utils/BinMap.cpp +++ b/utils/BinMap.cpp @@ -61,7 +61,7 @@ double BinMap::operator[](double key) // Increment it and start searching If we reach the end without finding an existing key // greater than our search term, then we've just hit the last bin and return that iter++; - double retVal = bins.end()->second; + double retVal = bins.back().second; while(iter != bins.end()) { if(key < iter->first) From 41183b8397474db40ed9d37b187a6fe1464feeb2 Mon Sep 17 00:00:00 2001 From: Travis Hunter Date: Fri, 20 Oct 2023 14:48:35 -0600 Subject: [PATCH 12/32] WIP. Added Stage objects, and made Stage and Rocket a Propagatable. Changed Propagator to propagate a Propagatable instead of a Rocket. This is to allow for propagating dropped stages --- QtRocket.cpp | 2 +- QtRocket.h | 6 +- gui/AnalysisWindow.cpp | 4 +- model/CMakeLists.txt | 4 + model/Part.cpp | 97 +++++++++++++++++++++++ model/Part.h | 63 +++++++++++++-- model/Propagatable.cpp | 0 model/Propagatable.h | 40 ++++++++++ model/Rocket.cpp | 21 ++++- model/Rocket.h | 67 ++++++++-------- model/Stage.cpp | 27 +++++++ model/Stage.h | 69 ++++++++++++++++ sim/Aero.cpp | 0 sim/Aero.h | 41 ++++++++++ sim/AtmosphericModel.h | 4 + sim/CMakeLists.txt | 2 + sim/ConstantAtmosphere.h | 5 ++ sim/Propagator.cpp | 16 ++-- sim/Propagator.h | 8 +- sim/StateData.h | 5 +- sim/USStandardAtmosphere.cpp | 16 +++- sim/USStandardAtmosphere.h | 4 + sim/tests/USStandardAtmosphereTests.cpp | 101 +++++++++++++++++++----- utils/math/Constants.h | 12 ++- 24 files changed, 527 insertions(+), 87 deletions(-) create mode 100644 model/Propagatable.cpp create mode 100644 model/Propagatable.h create mode 100644 model/Stage.cpp create mode 100644 model/Stage.h create mode 100644 sim/Aero.cpp create mode 100644 sim/Aero.h diff --git a/QtRocket.cpp b/QtRocket.cpp index 49a8bc5..1fd1e9d 100644 --- a/QtRocket.cpp +++ b/QtRocket.cpp @@ -81,7 +81,7 @@ QtRocket::QtRocket() setEnvironment(std::make_shared()); rocket.first = - std::make_shared(); + std::make_shared(); rocket.second = std::make_shared(rocket.first); diff --git a/QtRocket.h b/QtRocket.h index 7852928..d1c124b 100644 --- a/QtRocket.h +++ b/QtRocket.h @@ -44,13 +44,13 @@ public: std::shared_ptr getEnvironment() { return environment; } void setTimeStep(double t) { rocket.second->setTimeStep(t); } - std::shared_ptr getRocket() { return rocket.first; } + std::shared_ptr getRocket() { return rocket.first; } std::shared_ptr getMotorDatabase() { return motorDatabase; } void addMotorModels(std::vector& m); - void addRocket(std::shared_ptr r) { rocket.first = r; } + void addRocket(std::shared_ptr r) { rocket.first = r; } void setEnvironment(std::shared_ptr e) { environment = e; } @@ -79,7 +79,7 @@ private: utils::Logger* logger; - std::pair, std::shared_ptr> rocket; + std::pair, std::shared_ptr> rocket; std::shared_ptr environment; std::shared_ptr motorDatabase; diff --git a/gui/AnalysisWindow.cpp b/gui/AnalysisWindow.cpp index de60f10..8b8fbda 100644 --- a/gui/AnalysisWindow.cpp +++ b/gui/AnalysisWindow.cpp @@ -91,8 +91,8 @@ void AnalysisWindow::onButton_plotVelocity_clicked() void AnalysisWindow::onButton_plotMotorCurve_clicked() { - std::shared_ptr rocket = QtRocket::getInstance()->getRocket(); - model::MotorModel motor = rocket->getCurrentMotorModel(); + std::shared_ptr rocket = QtRocket::getInstance()->getRocket(); + model::MotorModel& motor = rocket->getCurrentStage()->getMotorModel(); ThrustCurve tc = motor.getThrustCurve(); diff --git a/model/CMakeLists.txt b/model/CMakeLists.txt index 72df3c1..c943867 100644 --- a/model/CMakeLists.txt +++ b/model/CMakeLists.txt @@ -5,8 +5,12 @@ add_library(model MotorModelDatabase.h Part.cpp Part.h + Propagatable.cpp + Propagatable.h Rocket.cpp Rocket.h + Stage.cpp + Stage.h ThrustCurve.cpp ThrustCurve.h) diff --git a/model/Part.cpp b/model/Part.cpp index fd3e890..be05350 100644 --- a/model/Part.cpp +++ b/model/Part.cpp @@ -1,6 +1,103 @@ #include "Part.h" +#include "utils/Logger.h" namespace model { +Part::Part() +{} + +Part::~Part() +{} + +Part::Part(const Part& orig) + : parent(orig.parent), + name(orig.name), + inertiaTensor(orig.inertiaTensor), + compositeInertiaTensor(orig.compositeInertiaTensor), + mass(orig.mass), + compositeMass(orig.compositeMass), + cm(orig.cm), + length(orig.length), + innerRadiusTop(orig.innerRadiusTop), + outerRadiusTop(orig.outerRadiusTop), + innerRadiusBottom(orig.innerRadiusBottom), + outerRadiusBottom(orig.outerRadiusBottom), + needsRecomputing(orig.needsRecomputing), + childParts() +{ + + // We are copying the whole tree. If the part we're copying itself has child + // parts, we are also copying all of them! This may be inefficient and not what + // is desired, but it is less likely to lead to weird bugs with the same part + // appearing in multiple locations of the rocket + utils::Logger::getInstance()->debug("Calling model::Part copy constructor. Recursively copying all child parts. Check Part names for uniqueness"); + + + for(const auto& i : orig.childParts) + { + Part& x = *std::get<0>(i); + std::shared_ptr tempPart = std::make_shared(x); + childParts.emplace_back(tempPart, std::get<1>(i));; + } + +} + + +double Part::getChildMasses(double t) +{ + double childMasses{0.0}; + for(const auto& i : childParts) + { + childMasses += std::get<0>(i)->getMass(t); + } + return childMasses; + +} + +void Part::addChildPart(const Part& childPart, Vector3 position) +{ + + double childMass = childPart.compositeMass; + Matrix3 childInertiaTensor = childPart.compositeInertiaTensor; + std::shared_ptr newChild = std::make_shared(childPart); + // Set the parent pointer + newChild->parent = this; + + // Recompute inertia tensor + + childInertiaTensor += childMass * ( position.dot(position) * Matrix3::Identity() - position*position.transpose()); + + compositeInertiaTensor += childInertiaTensor; + compositeMass += childMass; + + childParts.emplace_back(std::move(newChild), std::move(position)); + + if(parent) + { + parent->markAsNeedsRecomputing(); + parent->recomputeInertiaTensor(); + } + +} + +void Part::recomputeInertiaTensor() +{ + if(!needsRecomputing) + { + return; + } + // recompute the whole composite inertia tensor + // Reset the composite inertia tensor + compositeInertiaTensor = inertiaTensor; + compositeMass = mass; + for(auto& [child, pos] : childParts) + { + child->recomputeInertiaTensor(); + compositeInertiaTensor += child->compositeInertiaTensor + child->compositeMass * ( pos.dot(pos) * Matrix3::Identity() - pos*pos.transpose()); + compositeMass += child->compositeMass; + } + needsRecomputing = false; +} + } // namespace model \ No newline at end of file diff --git a/model/Part.h b/model/Part.h index e766fca..7926c21 100644 --- a/model/Part.h +++ b/model/Part.h @@ -4,16 +4,16 @@ /// \cond // C headers // C++ headers -#include -#include -#include +#include #include +#include // 3rd party headers /// \endcond // qtrocket headers #include "utils/math/MathTypes.h" +#include "model/Part.h" namespace model { @@ -24,8 +24,10 @@ public: Part(); virtual ~Part(); + Part(const Part&); + void setMass(double m) { mass = m; } - + // Set the inertia tensor void setI(const Matrix3& I) { inertiaTensor = I; } @@ -37,11 +39,58 @@ public: void setInnerRadius(double r) { innerRadiusTop = r; innerRadiusBottom = r; } void setOuterRadius(double r) { outerRadiusTop = r; outerRadiusBottom = r; } + + double getMass(double t) + { + return mass; + } + + double getCompositeMass(double t) + { + return compositeMass; + } + + /** + * @brief Add a child part to this part. + * + * @param childPart Child part to add + * @param position Relative position of the child part's center-of-mass w.r.t the + * parent's center of mass + */ + void addChildPart(const Part& childPart, Vector3 position); + + /** + * @brief Recomputes the inertia tensor. If the change is due to the change in inertia + * of a child part, an optional name of the child part can be given to + * only recompute that change rather than recompute all child inertia + * tensors + * + * @param name Optional name of the child part to recompute. If empty, it will + * recompute all child inertia tensors + */ + //void recomputeInertiaTensor(std::string name = ""); + void recomputeInertiaTensor(); private: + // This is a pointer to the parent Part, if it has one. Purpose is to be able to + // tell the parent if it needs to recompute anything if this part changes. e.g. + // if a change to this part's inertia tensor occurs, the parent needs to recompute + // it's total inertia tensor. + Part* parent{nullptr}; + + std::string name; + + double getChildMasses(double t); + void markAsNeedsRecomputing() + { needsRecomputing = true; if(parent) { parent->markAsNeedsRecomputing(); }} + + // Because a part is both a simple part and the composite of itself with all of it's children, + // we will keep track of this object's inertia tensor (without children), and the composite + // one with all of it's children attached Matrix3 inertiaTensor; // moment of inertia tensor with respect to the part's center of mass and - // + Matrix3 compositeInertiaTensor; double mass; // The moment of inertia tensor also has this, so don't double compute + double compositeMass; // The mass of this part along with all attached parts Vector3 cm; // center of mass wrt middle of component @@ -53,7 +102,11 @@ private: double innerRadiusBottom; double outerRadiusBottom; + bool needsRecomputing{false}; + /// @brief child parts and the relative positions of their center of mass w.r.t. + /// the center of mass of this part + std::vector, Vector3>> childParts; }; } diff --git a/model/Propagatable.cpp b/model/Propagatable.cpp new file mode 100644 index 0000000..e69de29 diff --git a/model/Propagatable.h b/model/Propagatable.h new file mode 100644 index 0000000..434665b --- /dev/null +++ b/model/Propagatable.h @@ -0,0 +1,40 @@ +#ifndef MODEL_PROPAGATABLE_H +#define MODEL_PROPAGATABLE_H + +/// \cond +// C headers +// C++ headers +#include +// 3rd party headers +/// \endcond + +// qtrocket headers +#include "sim/Aero.h" +#include "sim/StateData.h" + +namespace model +{ + +class Propagatable +{ +public: + Propagatable() {} + virtual ~Propagatable() {} + + virtual double getDragCoefficient() = 0; + virtual void setDragCoefficient(double d) = 0; + + virtual bool terminateCondition(const std::pair&) = 0; + + virtual double getMass(double t) = 0; + virtual double getThrust(double t) = 0; +private: + + sim::Aero aeroData; + + StateData state; +}; + +} + +#endif // MODEL_PROPAGATABLE_H \ No newline at end of file diff --git a/model/Rocket.cpp b/model/Rocket.cpp index 350dd65..3dafa4c 100644 --- a/model/Rocket.cpp +++ b/model/Rocket.cpp @@ -3,6 +3,9 @@ #include "Rocket.h" #include "QtRocket.h" +namespace model +{ + Rocket::Rocket() { @@ -10,12 +13,12 @@ Rocket::Rocket() void Rocket::launch() { - mm.startMotor(0.0); + currentStage->getMotorModel().startMotor(0.0); } void Rocket::setMotorModel(const model::MotorModel& motor) { - mm = motor; + currentStage->setMotorModel(motor); } bool Rocket::terminateCondition(const std::pair& cond) @@ -29,5 +32,17 @@ bool Rocket::terminateCondition(const std::pair& cond) double Rocket::getThrust(double t) { - return mm.getThrust(t); + return currentStage->getMotorModel().getThrust(t); } + +double Rocket::getMass(double t) +{ + double totalMass = 0.0; + for(const auto& stage : stages) + { + totalMass += stage.second->getMass(t); + } + return totalMass; +} + +} // namespace model \ No newline at end of file diff --git a/model/Rocket.h b/model/Rocket.h index d7c874f..a7f2120 100644 --- a/model/Rocket.h +++ b/model/Rocket.h @@ -4,6 +4,7 @@ /// \cond // C headers // C++ headers +#include #include #include #include // std::move @@ -13,15 +14,20 @@ // qtrocket headers #include "model/ThrustCurve.h" -#include "model/MotorModel.h" #include "sim/Propagator.h" #include "utils/math/MathTypes.h" +#include "model/Stage.h" +#include "model/Propagatable.h" + +namespace model +{ + /** * @brief The Rocket class holds all rocket components * */ -class Rocket +class Rocket : public Propagatable { public: /** @@ -29,40 +35,18 @@ public: */ Rocket(); + /** + * @brief Rocket class destructor + * + */ + virtual ~Rocket() {} + /** * @brief launch Propagates the Rocket object until termination, * normally when altitude crosses from positive to negative */ void launch(); - /** - * @brief getMass returns the current mass of the rocket. This is the sum of all components' masses - * @return total current mass of the Rocket - */ - double getMass(double simTime) const { return mass + mm.getMass(simTime); } - - /** - * @brief setMass sets the current total mass of the Rocket - * @param m total Rocket mass - * @todo This should be dynamically computed, not set. Fix this - */ - void setMass(double m) { mass = m;} - - /** - * @brief setDragCoefficient sets the current total drag coefficient of the Rocket - * @param d drag coefficient - * @todo This should be dynamically computed, not set. Fix this - */ - void setDragCoefficient(double d) { dragCoeff = d; } - - /** - * @brief getDragCoefficient returns the current drag coefficient - * - * This is intended to be called by the propagator during propagation. - * @return the coefficient of drag - */ - double getDragCoefficient() const { return dragCoeff; } - /** * @brief getThrust returns current motor thrust * @param t current simulation time @@ -70,6 +54,13 @@ public: */ double getThrust(double t); + /** + * @brief getMass returns current rocket + * @param t current simulation time + * @return mass in kg + */ + virtual double getMass(double t) override; + /** * @brief setMotorModel * @param motor @@ -80,14 +71,14 @@ public: * @brief Returns the current motor model. * @return The current motor model */ - const model::MotorModel& getCurrentMotorModel() const { return mm; } + //const model::MotorModel& getCurrentMotorModel() const { return mm; } /** * @brief terminateCondition returns true or false, whether the passed-in time/state matches the terminate condition * @param cond time/state pair * @return true if the passed-in time/state satisfies the terminate condition */ - bool terminateCondition(const std::pair& cond); + virtual bool terminateCondition(const std::pair& cond) override; /** * @brief setName sets the rocket name @@ -95,16 +86,22 @@ public: */ void setName(const std::string& n) { name = n; } + virtual double getDragCoefficient() override { return 1.0; } + virtual void setDragCoefficient(double d) override { } + void setMass(double m) { } + std::shared_ptr getCurrentStage() { return currentStage; } private: std::string name; /// Rocket name - double dragCoeff; /// @todo get rid of this, should be dynamically calculated - double mass; /// @todo get rid of this, should be dynamically computed, but is the current rocket mass - model::MotorModel mm; /// Current Motor Model + std::map> stages; + std::shared_ptr currentStage; + //model::MotorModel mm; /// Current Motor Model + }; +} // namespace model #endif // ROCKET_H diff --git a/model/Stage.cpp b/model/Stage.cpp new file mode 100644 index 0000000..ab6a0c1 --- /dev/null +++ b/model/Stage.cpp @@ -0,0 +1,27 @@ +/// \cond +// C headers +// C++ headers +#include +#include + +// 3rd party headers +/// \endcond + +// qtrocket headers +#include "Stage.h" + +namespace model +{ + +Stage::Stage() +{} + +Stage::~Stage() +{} + +void Stage::setMotorModel(const model::MotorModel& motor) +{ + mm = motor; +} + +} // namespace model \ No newline at end of file diff --git a/model/Stage.h b/model/Stage.h new file mode 100644 index 0000000..c3df69f --- /dev/null +++ b/model/Stage.h @@ -0,0 +1,69 @@ +#ifndef SIM_STAGE_H +#define SIM_STAGE_H + +/// \cond +// C headers +// C++ headers +#include + +// 3rd party headers +/// \endcond + +// qtrocket headers +#include "model/MotorModel.h" +#include "model/Part.h" +#include "model/Propagatable.h" + +namespace model +{ + +class Stage : public Propagatable +{ +public: + Stage(); + virtual ~Stage(); + + /** + * @brief setMotorModel + * @param motor + */ + void setMotorModel(const model::MotorModel& motor); + + /** + * @brief Returns the current motor model. + * @return The current motor model + */ + model::MotorModel& getMotorModel() { return mm; } + + virtual double getMass(double t) override + { + return topPart.getCompositeMass(t) + mm.getMass(t); + } + + virtual double getDragCoefficient() override { return 1.0 ;} + virtual void setDragCoefficient(double d) override {} + + virtual bool terminateCondition(const std::pair& cond) override + { + // Terminate propagation when the z coordinate drops below zero + if(cond.second.position[2] < 0) + return true; + else + return false; + } + + virtual double getThrust(double t) override { return mm.getThrust(t); } + + +private: + std::string name; + + Part topPart; + + model::MotorModel mm; + Vector3 motorModelPosition; // position of motor cg w.r.t. the stage c.g. +}; + +} // namespace model + +#endif // SIM_STAGE_H \ No newline at end of file diff --git a/sim/Aero.cpp b/sim/Aero.cpp new file mode 100644 index 0000000..e69de29 diff --git a/sim/Aero.h b/sim/Aero.h new file mode 100644 index 0000000..08636f7 --- /dev/null +++ b/sim/Aero.h @@ -0,0 +1,41 @@ +#ifndef SIM_AERO_H +#define SIM_AERO_H + +/// \cond +// C headers +// C++ headers +#include + +// 3rd party headers +/// \endcond + +// qtrocket headers +#include "utils/math/MathTypes.h" + +namespace sim +{ + +class Aero +{ +public: + +private: + + Vector3 cp; /// center of pressure + + double Cx; /// longitudinal coefficient + double Cy; /// These are probably the same for axial symmetric + double Cz; /// rockets. The coeffients in the y and z body directions + + double Cl; // roll moment coefficient + double Cm; // pitch moment coefficient + double Cn; // yaw moment coefficient + + double baseCd; // coefficient of drag due to base drag + double Cd; // total coeffient of drag + + +}; +} + +#endif // SIM_AERO_H \ No newline at end of file diff --git a/sim/AtmosphericModel.h b/sim/AtmosphericModel.h index deac8f5..0c61b14 100644 --- a/sim/AtmosphericModel.h +++ b/sim/AtmosphericModel.h @@ -13,6 +13,10 @@ public: virtual double getDensity(double altitude) = 0; virtual double getPressure(double altitude) = 0; virtual double getTemperature(double altitude) = 0; + + virtual double getSpeedOfSound(double altitude) = 0; + virtual double getDynamicViscosity(double altitude) = 0; + }; } // namespace sim diff --git a/sim/CMakeLists.txt b/sim/CMakeLists.txt index 41e05e5..b372b31 100644 --- a/sim/CMakeLists.txt +++ b/sim/CMakeLists.txt @@ -1,4 +1,6 @@ add_library(sim + Aero.cpp + Aero.h AtmosphericModel.h ConstantAtmosphere.h ConstantGravityModel.h diff --git a/sim/ConstantAtmosphere.h b/sim/ConstantAtmosphere.h index c37ad43..226eead 100644 --- a/sim/ConstantAtmosphere.h +++ b/sim/ConstantAtmosphere.h @@ -3,6 +3,7 @@ // qtrocket headers #include "AtmosphericModel.h" +#include "utils/math/Constants.h" namespace sim { @@ -15,6 +16,10 @@ public: double getDensity(double) override { return 1.225; } double getPressure(double) override { return 101325.0; } double getTemperature(double) override { return 288.15; } + + double getSpeedOfSound(double) override { return 340.294; } + + double getDynamicViscosity(double) override { return 1.78938e-5; } }; } // namespace sim diff --git a/sim/Propagator.cpp b/sim/Propagator.cpp index 5f7a998..3b322d2 100644 --- a/sim/Propagator.cpp +++ b/sim/Propagator.cpp @@ -22,10 +22,10 @@ namespace sim { -Propagator::Propagator(std::shared_ptr r) +Propagator::Propagator(std::shared_ptr r) : linearIntegrator(), //orientationIntegrator(), - rocket(r), + object(r), currentState(), nextState(), currentGravity(), @@ -154,7 +154,7 @@ void Propagator::runUntilTerminate() { states.push_back(std::make_pair(currentTime, nextState)); } - if(rocket->terminateCondition(std::make_pair(currentTime, currentState))) + if(object->terminateCondition(std::make_pair(currentTime, currentState))) break; currentTime += timeStep; @@ -171,27 +171,27 @@ void Propagator::runUntilTerminate() double Propagator::getMass() { - return rocket->getMass(currentTime); + return object->getMass(currentTime); } double Propagator::getForceX() { QtRocket* qtrocket = QtRocket::getInstance(); - return (currentState.velocity[0] >= 0 ? -1.0 : 1.0) * qtrocket->getEnvironment()->getAtmosphericModel()->getDensity(currentState.position[2])/ 2.0 * 0.008107 * rocket->getDragCoefficient() * currentState.velocity[0]* currentState.velocity[0]; + return (currentState.velocity[0] >= 0 ? -1.0 : 1.0) * qtrocket->getEnvironment()->getAtmosphericModel()->getDensity(currentState.position[2])/ 2.0 * 0.008107 * object->getDragCoefficient() * currentState.velocity[0]* currentState.velocity[0]; } double Propagator::getForceY() { QtRocket* qtrocket = QtRocket::getInstance(); - return (currentState.velocity[1] >= 0 ? -1.0 : 1.0) * qtrocket->getEnvironment()->getAtmosphericModel()->getDensity(currentState.position[2]) / 2.0 * 0.008107 * rocket->getDragCoefficient() * currentState.velocity[1]* currentState.velocity[1]; + return (currentState.velocity[1] >= 0 ? -1.0 : 1.0) * qtrocket->getEnvironment()->getAtmosphericModel()->getDensity(currentState.position[2]) / 2.0 * 0.008107 * object->getDragCoefficient() * currentState.velocity[1]* currentState.velocity[1]; } double Propagator::getForceZ() { QtRocket* qtrocket = QtRocket::getInstance(); double gravity = (qtrocket->getEnvironment()->getGravityModel()->getAccel(currentState.position[0], currentState.position[1], currentState.position[2]))[2]; - double airDrag = (currentState.velocity[2] >= 0 ? -1.0 : 1.0) * qtrocket->getEnvironment()->getAtmosphericModel()->getDensity(currentState.position[2]) / 2.0 * 0.008107 * rocket->getDragCoefficient() * currentState.velocity[2]* currentState.velocity[2]; - double thrust = rocket->getThrust(currentTime); + double airDrag = (currentState.velocity[2] >= 0 ? -1.0 : 1.0) * qtrocket->getEnvironment()->getAtmosphericModel()->getDensity(currentState.position[2]) / 2.0 * 0.008107 * object->getDragCoefficient() * currentState.velocity[2]* currentState.velocity[2]; + double thrust = object->getThrust(currentTime); return gravity + airDrag + thrust; } diff --git a/sim/Propagator.h b/sim/Propagator.h index ec7fbe4..37568c6 100644 --- a/sim/Propagator.h +++ b/sim/Propagator.h @@ -14,10 +14,14 @@ #include "sim/RK4Solver.h" #include "utils/math/MathTypes.h" #include "sim/StateData.h" +#include "model/Propagatable.h" // Forward declare +namespace model +{ class Rocket; +} class QtRocket; namespace sim @@ -26,7 +30,7 @@ namespace sim class Propagator { public: - Propagator(std::shared_ptr r); + Propagator(std::shared_ptr r); ~Propagator(); void setInitialState(const StateData& initialState) @@ -70,7 +74,7 @@ private: std::unique_ptr> linearIntegrator; // std::unique_ptr> orientationIntegrator; - std::shared_ptr rocket; + std::shared_ptr object; StateData currentState; StateData nextState; diff --git a/sim/StateData.h b/sim/StateData.h index 43c820f..c7a268b 100644 --- a/sim/StateData.h +++ b/sim/StateData.h @@ -74,12 +74,11 @@ public: //} // private: - // These are 4-vectors so quaternion multiplication works out. The last term (scalar) is always - // zero. I could just use quaternions here, but I want to make it clear that these aren't - // rotations, they're vectors + // Intended to be used as world state data Vector3 position{0.0, 0.0, 0.0}; Vector3 velocity{0.0, 0.0, 0.0}; + // Orientation of body coordinates w.r.t. world coordinates Quaternion orientation{0.0, 0.0, 0.0, 0.0}; /// (vector, scalar) Quaternion orientationRate{0.0, 0.0, 0.0, 0.0}; /// (vector, scalar) diff --git a/sim/USStandardAtmosphere.cpp b/sim/USStandardAtmosphere.cpp index 54231ba..1081b00 100644 --- a/sim/USStandardAtmosphere.cpp +++ b/sim/USStandardAtmosphere.cpp @@ -80,8 +80,6 @@ utils::BinMap USStandardAtmosphere::standardTemperature(initTemps()); utils::BinMap USStandardAtmosphere::standardDensity(initDensities()); utils::BinMap USStandardAtmosphere::standardPressure(initPressures()); - - USStandardAtmosphere::USStandardAtmosphere() { @@ -119,7 +117,6 @@ double USStandardAtmosphere::getTemperature(double altitude) double baseAltitude = standardTemperature.getBinBase(altitude); return baseTemp - (altitude - baseAltitude) * temperatureLapseRate[altitude]; - return 0.0; } double USStandardAtmosphere::getPressure(double altitude) { @@ -142,4 +139,17 @@ double USStandardAtmosphere::getPressure(double altitude) } } + +double USStandardAtmosphere::getSpeedOfSound(double altitude) +{ + return std::sqrt( (Constants::gamma * Constants::Rstar * getTemperature(altitude)) + / + Constants::airMolarMass); +} + +double USStandardAtmosphere::getDynamicViscosity(double altitude) +{ + double temperature = getTemperature(altitude); + return (Constants::beta * std::pow(temperature, 1.5)) / ( temperature + Constants::S); +} } // namespace sim diff --git a/sim/USStandardAtmosphere.h b/sim/USStandardAtmosphere.h index 4b18083..740df66 100644 --- a/sim/USStandardAtmosphere.h +++ b/sim/USStandardAtmosphere.h @@ -28,6 +28,10 @@ public: double getPressure(double altitude) override; double getTemperature(double altitude) override; + double getSpeedOfSound(double altitude) override; + + double getDynamicViscosity(double altitude) override; + private: static utils::BinMap temperatureLapseRate; static utils::BinMap standardTemperature; diff --git a/sim/tests/USStandardAtmosphereTests.cpp b/sim/tests/USStandardAtmosphereTests.cpp index a1375a4..cafe226 100644 --- a/sim/tests/USStandardAtmosphereTests.cpp +++ b/sim/tests/USStandardAtmosphereTests.cpp @@ -6,26 +6,85 @@ TEST(USStandardAtmosphereTests, DensityTests) { sim::USStandardAtmosphere atmosphere; - // Test that the calucated values are with 1% of the published values in the NOAA report - EXPECT_NEAR(atmosphere.getDensity(0.0) / 1.225, 1.0, 0.01); - EXPECT_NEAR(atmosphere.getDensity(1000.0) / 1.112, 1.0, 0.01); - EXPECT_NEAR(atmosphere.getDensity(2000.0) / 1.007, 1.0, 0.01); - EXPECT_NEAR(atmosphere.getDensity(3000.0) / 0.9093, 1.0, 0.01); - EXPECT_NEAR(atmosphere.getDensity(4000.0) / 0.8194, 1.0, 0.01); - EXPECT_NEAR(atmosphere.getDensity(5000.0) / 0.7364, 1.0, 0.01); - EXPECT_NEAR(atmosphere.getDensity(6000.0) / 0.6601, 1.0, 0.01); - EXPECT_NEAR(atmosphere.getDensity(7000.0) / 0.5900, 1.0, 0.01); - EXPECT_NEAR(atmosphere.getDensity(8000.0) / 0.5258, 1.0, 0.01); - EXPECT_NEAR(atmosphere.getDensity(9000.0) / 0.4647, 1.0, 0.01); - EXPECT_NEAR(atmosphere.getDensity(10000.0) / 0.4135, 1.0, 0.01); - EXPECT_NEAR(atmosphere.getDensity(15000.0) / 0.1948, 1.0, 0.01); - EXPECT_NEAR(atmosphere.getDensity(20000.0) / 0.08891, 1.0, 0.01); - EXPECT_NEAR(atmosphere.getDensity(25000.0) / 0.039466, 1.0, 0.01); - EXPECT_NEAR(atmosphere.getDensity(30000.0) / 0.018012, 1.0, 0.01); - EXPECT_NEAR(atmosphere.getDensity(40000.0) / 0.0038510, 1.0, 0.01); - EXPECT_NEAR(atmosphere.getDensity(50000.0) / 0.00097752, 1.0, 0.01); - EXPECT_NEAR(atmosphere.getDensity(60000.0) / 0.00028832, 1.0, 0.01); - EXPECT_NEAR(atmosphere.getDensity(70000.0) / 0.000074243, 1.0, 0.01); - EXPECT_NEAR(atmosphere.getDensity(80000.0) / 0.000015701, 1.0, 0.01); + // Test that the calucated values are with 0.1% of the published values in the NOAA report + EXPECT_NEAR(atmosphere.getDensity(0.0) / 1.225, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getDensity(1000.0) / 1.112, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getDensity(2000.0) / 1.007, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getDensity(3000.0) / 0.9093, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getDensity(4000.0) / 0.8194, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getDensity(5000.0) / 0.7364, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getDensity(6000.0) / 0.6601, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getDensity(7000.0) / 0.5900, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getDensity(15000.0) / 0.19367, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getDensity(20000.0) / 0.088035, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getDensity(25000.0) / 0.039466, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getDensity(30000.0) / 0.018012, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getDensity(40000.0) / 0.0038510, 1.0, 0.001); + + // These are generally accurate to ~0.5%. Slight deviation of calculated + // density from the given density in the report table + EXPECT_NEAR(atmosphere.getDensity(8000.0) / 0.52579, 1.0, 0.005); + EXPECT_NEAR(atmosphere.getDensity(9000.0) / 0.46706, 1.0, 0.005); + EXPECT_NEAR(atmosphere.getDensity(10000.0) / 0.41351, 1.0, 0.005); + EXPECT_NEAR(atmosphere.getDensity(50000.0) / 0.00097752, 1.0, 0.005); + EXPECT_NEAR(atmosphere.getDensity(60000.0) / 0.00028832, 1.0, 0.005); + EXPECT_NEAR(atmosphere.getDensity(70000.0) / 0.000074243, 1.0, 0.005); + EXPECT_NEAR(atmosphere.getDensity(80000.0) / 0.000015701, 1.0, 0.005); } + +TEST(USStandardAtmosphereTests, PressureTests) +{ + sim::USStandardAtmosphere atmosphere; + + // Test that the calucated values are with 0.1% of the published values in the NOAA report + EXPECT_NEAR(atmosphere.getPressure(0.0) / 101325.0, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getPressure(1000.0) / 89876.0, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getPressure(2000.0) / 79501.0, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getPressure(3000.0) / 70108.0, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getPressure(4000.0) / 61640.0, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getPressure(5000.0) / 54019.0, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getPressure(6000.0) / 47181.0, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getPressure(7000.0) / 41060.0, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getPressure(8000.0) / 35599.0, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getPressure(9000.0) / 30742.0, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getPressure(10000.0) / 26436.0, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getPressure(15000.0) / 12044.0, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getPressure(20000.0) / 5474.8, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getPressure(25000.0) / 2511.0, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getPressure(30000.0) / 1171.8, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getPressure(40000.0) / 277.52, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getPressure(50000.0) / 75.944, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getPressure(60000.0) / 20.314, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getPressure(70000.0) / 4.6342, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getPressure(80000.0) / 0.88627, 1.0, 0.001); + +} + +TEST(USStandardAtmosphereTests, TemperatureTests) +{ + sim::USStandardAtmosphere atmosphere; + + // Test that the calucated values are with 0.1% of the published values in the NOAA report + EXPECT_NEAR(atmosphere.getTemperature(0.0) / 288.15, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getTemperature(1000.0) / 281.651, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getTemperature(2000.0) / 275.154, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getTemperature(3000.0) / 268.659, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getTemperature(4000.0) / 262.166, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getTemperature(5000.0) / 255.676, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getTemperature(6000.0) / 249.187, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getTemperature(7000.0) / 242.7, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getTemperature(8000.0) / 236.215, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getTemperature(9000.0) / 229.733, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getTemperature(10000.0) / 223.252, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getTemperature(15000.0) / 216.65, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getTemperature(20000.0) / 216.65, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getTemperature(25000.0) / 221.552, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getTemperature(30000.0) / 226.509, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getTemperature(40000.0) / 251.05, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getTemperature(50000.0) / 270.65, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getTemperature(60000.0) / 245.45, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getTemperature(70000.0) / 217.450, 1.0, 0.001); + EXPECT_NEAR(atmosphere.getTemperature(80000.0) / 196.650, 1.0, 0.001); + +} \ No newline at end of file diff --git a/utils/math/Constants.h b/utils/math/Constants.h index 13c0f18..cfc1bbe 100644 --- a/utils/math/Constants.h +++ b/utils/math/Constants.h @@ -6,9 +6,19 @@ namespace utils::math namespace Constants { - constexpr double Rstar = 8.3144598; + constexpr double Rstar = 8.31432; constexpr const double g0 = 9.80665; constexpr const double airMolarMass = 0.0289644; + + // gamma is the ratio of the specific heat of air at constant pressure to that at + // constant volume. Used in computing the speed of sound + constexpr const double gamma = 1.4; + + // beta is used in calculating the dynamic viscosity of air. Based on the 1976 US Standard + // Atmosphere report, it is empirically measured to be: + constexpr const double beta = 1.458e-6; + // Sutherland's constant + constexpr const double S = 110.4; constexpr const double standardTemperature = 288.15; constexpr const double standardDensity = 1.2250; constexpr const double meanEarthRadiusWGS84 = 6371008.8; From 9517b5b68227ae6cf3ef6f6cf216b46e35121a6b Mon Sep 17 00:00:00 2001 From: Travis Hunter Date: Fri, 20 Oct 2023 17:03:52 -0600 Subject: [PATCH 13/32] Added first model tests. Super basic Part test --- model/CMakeLists.txt | 3 ++ model/Part.cpp | 26 ++++++++++++- model/Part.h | 11 +++++- model/Stage.h | 10 ++++- model/tests/CMakeLists.txt | 15 +++++++ model/tests/PartTests.cpp | 80 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 140 insertions(+), 5 deletions(-) create mode 100644 model/tests/CMakeLists.txt create mode 100644 model/tests/PartTests.cpp diff --git a/model/CMakeLists.txt b/model/CMakeLists.txt index c943867..d3a8f14 100644 --- a/model/CMakeLists.txt +++ b/model/CMakeLists.txt @@ -16,3 +16,6 @@ add_library(model target_link_libraries(model PRIVATE utils) + +# Unit tests +add_subdirectory(tests) diff --git a/model/Part.cpp b/model/Part.cpp index be05350..8cae619 100644 --- a/model/Part.cpp +++ b/model/Part.cpp @@ -4,8 +4,30 @@ namespace model { -Part::Part() -{} +Part::Part(const std::string& n, + const Matrix3& I, + double m, + const Vector3& centerMass, + double l, + double inRadTop, + double outRadTop, + double inRadBottom, + double outRadBottom) + : parent(nullptr), + name(n), + inertiaTensor(I), + compositeInertiaTensor(I), + mass(m), + compositeMass(m), + cm(centerMass), + length(l), + innerRadiusTop(inRadTop), + outerRadiusTop(outRadTop), + innerRadiusBottom(inRadBottom), + outerRadiusBottom(outRadBottom), + needsRecomputing(false), + childParts() +{ } Part::~Part() {} diff --git a/model/Part.h b/model/Part.h index 7926c21..03da6bf 100644 --- a/model/Part.h +++ b/model/Part.h @@ -21,7 +21,16 @@ namespace model class Part { public: - Part(); + Part(const std::string& name, + const Matrix3& I, + double m, + const Vector3& centerMass, + double length, + double inRadTop, + double outRadTop, + double inRadBottom, + double outRadBottom); + virtual ~Part(); Part(const Part&); diff --git a/model/Stage.h b/model/Stage.h index c3df69f..f215674 100644 --- a/model/Stage.h +++ b/model/Stage.h @@ -5,6 +5,7 @@ // C headers // C++ headers #include +#include // 3rd party headers /// \endcond @@ -37,7 +38,12 @@ public: virtual double getMass(double t) override { - return topPart.getCompositeMass(t) + mm.getMass(t); + if(topPart) + { + return topPart->getCompositeMass(t) + mm.getMass(t); + } + else + return 0.0; } virtual double getDragCoefficient() override { return 1.0 ;} @@ -58,7 +64,7 @@ public: private: std::string name; - Part topPart; + std::shared_ptr topPart; model::MotorModel mm; Vector3 motorModelPosition; // position of motor cg w.r.t. the stage c.g. diff --git a/model/tests/CMakeLists.txt b/model/tests/CMakeLists.txt new file mode 100644 index 0000000..5ff53a7 --- /dev/null +++ b/model/tests/CMakeLists.txt @@ -0,0 +1,15 @@ +enable_testing() + +add_executable(model_tests + PartTests.cpp +) + +target_link_libraries(model_tests PRIVATE + model + utils + GTest::gtest_main +) + +include(GoogleTest) +gtest_discover_tests(model_tests) + diff --git a/model/tests/PartTests.cpp b/model/tests/PartTests.cpp new file mode 100644 index 0000000..f985d3a --- /dev/null +++ b/model/tests/PartTests.cpp @@ -0,0 +1,80 @@ +#include + +#include "model/Part.h" + +class PartTest : public testing::Test +{ +protected: + // Per-test-suite set-up. + // Called before the first test in this test suite. + // Can be omitted if not needed. + static void SetUpTestSuite() + { + //shared_resource_ = new ...; + + // If `shared_resource_` is **not deleted** in `TearDownTestSuite()`, + // reallocation should be prevented because `SetUpTestSuite()` may be called + // in subclasses of FooTest and lead to memory leak. + // + // if (shared_resource_ == nullptr) { + // shared_resource_ = new ...; + // } + } + + // Per-test-suite tear-down. + // Called after the last test in this test suite. + // Can be omitted if not needed. + static void TearDownTestSuite() + { + //delete shared_resource_; + //shared_resource_ = nullptr; + } + + // You can define per-test set-up logic as usual. + void SetUp() override { } + + // You can define per-test tear-down logic as usual. + void TearDown() override { } + + // Some expensive resource shared by all tests. + //static T* shared_resource_; +}; + +//T* FooTest::shared_resource_ = nullptr; + +TEST(PartTest, CreationTests) +{ + Matrix3 inertia; + inertia << 1, 0, 0, + 0, 1, 0, + 0, 0, 1; + Vector3 cm{1, 0, 0}; + model::Part testPart("testPart", + inertia, + 1.0, + cm, + 2.0, + 1.0, + 1.1, + 1.1, + 1.0); + + Matrix3 inertia2; + inertia2 << 1, 0, 0, + 0, 1, 0, + 0, 0, 1; + Vector3 cm2{1, 0, 0}; + model::Part testPart2("testPart2", + inertia2, + 1.0, + cm2, + 2.0, + 1.0, + 1.1, + 1.1, + 1.0); + Vector3 R{2.0, 2.0, 2.0}; + testPart.addChildPart(testPart2, R); + + +} \ No newline at end of file From d9cc4e4aecf19719f2d9a0258256e1688c0737c3 Mon Sep 17 00:00:00 2001 From: Travis Hunter Date: Sun, 18 Feb 2024 09:03:51 -0700 Subject: [PATCH 14/32] Remove CMakeLists.txt.user --- CMakeLists.txt.user | 416 -------------------------------------------- 1 file changed, 416 deletions(-) delete mode 100644 CMakeLists.txt.user diff --git a/CMakeLists.txt.user b/CMakeLists.txt.user deleted file mode 100644 index 5fc28a1..0000000 --- a/CMakeLists.txt.user +++ /dev/null @@ -1,416 +0,0 @@ - - - - - - EnvironmentId - {126c64d7-12e8-468a-ad3f-06f0fbdaeca1} - - - ProjectExplorer.Project.ActiveTarget - 0 - - - ProjectExplorer.Project.EditorSettings - - true - false - true - - Cpp - - CppGlobal - - - - QmlJS - - QmlJSGlobal - - - 2 - UTF-8 - false - 4 - false - 80 - true - true - 1 - false - true - false - 1 - true - true - 0 - 8 - true - false - 1 - true - true - true - *.md, *.MD, Makefile - false - true - true - - - - ProjectExplorer.Project.PluginSettings - - - true - false - true - true - true - true - - - 0 - true - - true - true - Builtin.DefaultTidyAndClazy - 6 - - - - true - - - - - ProjectExplorer.Project.Target.0 - - Desktop - Desktop Qt6 - Desktop Qt6 - {834bc66d-170a-454c-a2b0-c17798dc7b12} - 0 - 0 - 0 - - Debug - 2 - false - - -DCMAKE_GENERATOR:STRING=Ninja --DCMAKE_BUILD_TYPE:STRING=Debug --DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{buildDir}/.qtc/package-manager/auto-setup.cmake --DQT_QMAKE_EXECUTABLE:STRING=%{Qt:qmakeExecutable} --DCMAKE_PREFIX_PATH:STRING=%{Qt:QT_INSTALL_PREFIX} --DCMAKE_C_COMPILER:STRING=%{Compiler:Executable:C} --DCMAKE_CXX_COMPILER:STRING=%{Compiler:Executable:Cxx} --DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} - 0 - /home/travis/build-qtrocket-Desktop_Qt6-Debug - - - - - all - - false - - true - Build - CMakeProjectManager.MakeStep - - 1 - Build - Build - ProjectExplorer.BuildSteps.Build - - - - - - clean - - false - - true - Build - CMakeProjectManager.MakeStep - - 1 - Clean - Clean - ProjectExplorer.BuildSteps.Clean - - 2 - false - - false - - Debug - CMakeProjectManager.CMakeBuildConfiguration - - - Release - 2 - false - - -DCMAKE_GENERATOR:STRING=Ninja --DCMAKE_BUILD_TYPE:STRING=Release --DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{buildDir}/.qtc/package-manager/auto-setup.cmake --DQT_QMAKE_EXECUTABLE:STRING=%{Qt:qmakeExecutable} --DCMAKE_PREFIX_PATH:STRING=%{Qt:QT_INSTALL_PREFIX} --DCMAKE_C_COMPILER:STRING=%{Compiler:Executable:C} --DCMAKE_CXX_COMPILER:STRING=%{Compiler:Executable:Cxx} --DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} - /home/travis/build-qtrocket-Desktop_Qt6-Release - - - - - all - - false - - true - CMakeProjectManager.MakeStep - - 1 - Build - Build - ProjectExplorer.BuildSteps.Build - - - - - - clean - - false - - true - CMakeProjectManager.MakeStep - - 1 - Clean - Clean - ProjectExplorer.BuildSteps.Clean - - 2 - false - - false - - Release - CMakeProjectManager.CMakeBuildConfiguration - - - RelWithDebInfo - 2 - false - - -DCMAKE_GENERATOR:STRING=Ninja --DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo --DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{buildDir}/.qtc/package-manager/auto-setup.cmake --DQT_QMAKE_EXECUTABLE:STRING=%{Qt:qmakeExecutable} --DCMAKE_PREFIX_PATH:STRING=%{Qt:QT_INSTALL_PREFIX} --DCMAKE_C_COMPILER:STRING=%{Compiler:Executable:C} --DCMAKE_CXX_COMPILER:STRING=%{Compiler:Executable:Cxx} --DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} - /home/travis/build-qtrocket-Desktop_Qt6-RelWithDebInfo - - - - - all - - false - - true - CMakeProjectManager.MakeStep - - 1 - Build - Build - ProjectExplorer.BuildSteps.Build - - - - - - clean - - false - - true - CMakeProjectManager.MakeStep - - 1 - Clean - Clean - ProjectExplorer.BuildSteps.Clean - - 2 - false - - false - - Release with Debug Information - CMakeProjectManager.CMakeBuildConfiguration - - - RelWithDebInfo - 2 - false - - -DCMAKE_GENERATOR:STRING=Ninja --DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo --DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{buildDir}/.qtc/package-manager/auto-setup.cmake --DQT_QMAKE_EXECUTABLE:STRING=%{Qt:qmakeExecutable} --DCMAKE_PREFIX_PATH:STRING=%{Qt:QT_INSTALL_PREFIX} --DCMAKE_C_COMPILER:STRING=%{Compiler:Executable:C} --DCMAKE_CXX_COMPILER:STRING=%{Compiler:Executable:Cxx} --DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} - 0 - /home/travis/build-qtrocket-Desktop_Qt6-Profile - - - - - all - - false - - true - CMakeProjectManager.MakeStep - - 1 - Build - Build - ProjectExplorer.BuildSteps.Build - - - - - - clean - - false - - true - CMakeProjectManager.MakeStep - - 1 - Clean - Clean - ProjectExplorer.BuildSteps.Clean - - 2 - false - - false - - Profile - CMakeProjectManager.CMakeBuildConfiguration - - - MinSizeRel - 2 - false - - -DCMAKE_GENERATOR:STRING=Ninja --DCMAKE_BUILD_TYPE:STRING=MinSizeRel --DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{buildDir}/.qtc/package-manager/auto-setup.cmake --DQT_QMAKE_EXECUTABLE:STRING=%{Qt:qmakeExecutable} --DCMAKE_PREFIX_PATH:STRING=%{Qt:QT_INSTALL_PREFIX} --DCMAKE_C_COMPILER:STRING=%{Compiler:Executable:C} --DCMAKE_CXX_COMPILER:STRING=%{Compiler:Executable:Cxx} --DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} - /home/travis/build-qtrocket-Desktop_Qt6-MinSizeRel - - - - - all - - false - - true - CMakeProjectManager.MakeStep - - 1 - Build - Build - ProjectExplorer.BuildSteps.Build - - - - - - clean - - false - - true - CMakeProjectManager.MakeStep - - 1 - Clean - Clean - ProjectExplorer.BuildSteps.Clean - - 2 - false - - false - - Minimum Size Release - CMakeProjectManager.CMakeBuildConfiguration - - 5 - - - 0 - Deploy - Deploy - ProjectExplorer.BuildSteps.Deploy - - 1 - - false - ProjectExplorer.DefaultDeployConfiguration - - 1 - - true - true - true - - 2 - - qtrocket - CMakeProjectManager.CMakeRunConfiguration.qtrocket - qtrocket - false - true - true - false - true - /home/travis/build-qtrocket-Desktop_Qt6-Debug - - 1 - - - - ProjectExplorer.Project.TargetCount - 1 - - - ProjectExplorer.Project.Updater.FileVersion - 22 - - - Version - 22 - - From 5da279a8e6af021fd2bd52ad6f291760370807f6 Mon Sep 17 00:00:00 2001 From: Travis Hunter Date: Sun, 18 Feb 2024 16:25:45 -0700 Subject: [PATCH 15/32] Cleanup --- .gitignore | 1 + CMakeLists.txt | 8 +++++++- QtRocket.cpp | 17 ++++++++++++++--- QtRocket.h | 14 ++++++++++++-- main.cpp | 9 +++++---- model/Part.h | 4 +--- model/Rocket.cpp | 3 +-- model/Rocket.h | 4 +--- model/Stage.cpp | 4 +--- model/ThrustCurve.cpp | 2 -- sim/Aero.h | 3 +-- sim/ConstantAtmosphere.h | 1 - sim/DESolver.h | 3 +-- sim/Environment.h | 1 - sim/Propagator.cpp | 1 - sim/RK4Solver.h | 2 -- sim/USStandardAtmosphere.cpp | 24 ++++++++++++------------ sim/USStandardAtmosphere.h | 10 +++++----- utils/{BinMap.cpp => Bin.cpp} | 14 +++++++------- utils/{BinMap.h => Bin.h} | 14 +++++++------- utils/CMakeLists.txt | 8 ++++++-- utils/Logger.cpp | 7 +++++++ utils/Logger.h | 10 +++------- 23 files changed, 92 insertions(+), 72 deletions(-) rename utils/{BinMap.cpp => Bin.cpp} (91%) rename utils/{BinMap.h => Bin.h} (86%) diff --git a/.gitignore b/.gitignore index 045eed0..5b8a0ff 100644 --- a/.gitignore +++ b/.gitignore @@ -40,4 +40,5 @@ docs/doxygen/* # IDE qtrocket.pro.user .qmake.stash +CMakeLists.txt.user diff --git a/CMakeLists.txt b/CMakeLists.txt index 2ec252c..bc3dc87 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,6 +53,12 @@ FetchContent_Declare(eigen GIT_TAG 3.4.0) FetchContent_MakeAvailable(eigen) +# boost dependency +FetchContent_Declare(Boost + GIT_REPOSITORY https://github.com/boostorg/boost + GIT_TAG boost-1.82.0) +FetchContent_MakeAvailable(Boost) + # Add qtrocket subdirectories. These are components that will be linked in @@ -63,7 +69,7 @@ set(CMAKE_AUTORCC ON) if(WIN32) set(CMAKE_PREFIX_PATH $ENV{QTDIR}) - include_directories("C:\\boost\\boost_1_82_0\\") +# include_directories("C:\\boost\\boost_1_82_0\\") # find_package(Qt6Core REQUIRED) # find_package(Qt6Widgets REQUIRED) endif() diff --git a/QtRocket.cpp b/QtRocket.cpp index 1fd1e9d..a42dadc 100644 --- a/QtRocket.cpp +++ b/QtRocket.cpp @@ -1,4 +1,3 @@ - /// \cond // C headers // C++ headers @@ -45,7 +44,7 @@ void guiWorker(int argc, char* argv[], int& ret) // Go! MainWindow w(QtRocket::getInstance()); - logger->info("Showing MainWindow"); + logger->debug("Showing MainWindow"); w.show(); ret = a.exec(); @@ -65,7 +64,7 @@ void QtRocket::init() std::lock_guard lck(mtx); if(!initialized) { - utils::Logger::getInstance()->info("Instantiating new QtRocket"); + utils::Logger::getInstance()->debug("Instantiating new QtRocket"); instance = new QtRocket(); initialized = true; } @@ -88,6 +87,13 @@ QtRocket::QtRocket() motorDatabase = std::make_shared(); + logger->debug("Initial states vector size: " + states.capacity() ); + // Reserve at least 1024 spaces for StateData + if(states.capacity() < 1024) + { + states.reserve(1024); + } + logger->debug("New states vector size: " + states.capacity() ); } int QtRocket::run(int argc, char* argv[]) @@ -122,3 +128,8 @@ void QtRocket::addMotorModels(std::vector& m) motorDatabase->addMotorModels(m); // TODO: Now clear any duplicates? } + +void QtRocket::appendState(const StateData& state) +{ + states.emplace_back(state); +} diff --git a/QtRocket.h b/QtRocket.h index d1c124b..613bfd3 100644 --- a/QtRocket.h +++ b/QtRocket.h @@ -15,12 +15,11 @@ // qtrocket headers #include "model/MotorModel.h" #include "model/Rocket.h" -#include "sim/AtmosphericModel.h" -#include "sim/GravityModel.h" #include "sim/Environment.h" #include "sim/Propagator.h" #include "utils/Logger.h" #include "utils/MotorModelDatabase.h" +#include "utils/math/MathTypes.h" /** * @brief The QtRocket class is the master controller for the QtRocket application. @@ -67,6 +66,8 @@ public: */ void setInitialState(const StateData& initState) { rocket.second->setInitialState(initState); } + void appendState(const StateData& state); + private: QtRocket(); @@ -84,6 +85,15 @@ private: std::shared_ptr environment; std::shared_ptr motorDatabase; + // Launch site + // ECEF coordinates + Vector3 launchSitePosition{0.0, 0.0, 0.0}; + + // Table of state data + std::vector states; + + + }; #endif // QTROCKET_H diff --git a/main.cpp b/main.cpp index 02d1e70..379e1b5 100644 --- a/main.cpp +++ b/main.cpp @@ -1,11 +1,9 @@ - /// \cond // C headers // C++ headers // 3rd party headers /// \endcond - #include "QtRocket.h" #include "utils/Logger.h" @@ -14,13 +12,16 @@ int main(int argc, char *argv[]) // Instantiate logger utils::Logger* logger = utils::Logger::getInstance(); - logger->setLogLevel(utils::Logger::DEBUG_); + logger->setLogLevel(utils::Logger::PERF_); + logger->info("Logger instantiated at PERF level"); // instantiate QtRocket + logger->debug("Starting QtRocket"); QtRocket* qtrocket = QtRocket::getInstance(); // Run QtRocket. This'll start the GUI thread and block until the user // exits the program + logger->debug("QtRocket->run()"); int retVal = qtrocket->run(argc, argv); logger->debug("Returning"); return retVal; -} \ No newline at end of file +} diff --git a/model/Part.h b/model/Part.h index 03da6bf..434a65c 100644 --- a/model/Part.h +++ b/model/Part.h @@ -5,7 +5,6 @@ // C headers // C++ headers #include -#include #include // 3rd party headers @@ -13,7 +12,6 @@ // qtrocket headers #include "utils/math/MathTypes.h" -#include "model/Part.h" namespace model { @@ -120,4 +118,4 @@ private: } -#endif // MODEL_PART_H \ No newline at end of file +#endif // MODEL_PART_H diff --git a/model/Rocket.cpp b/model/Rocket.cpp index 3dafa4c..64d39be 100644 --- a/model/Rocket.cpp +++ b/model/Rocket.cpp @@ -1,7 +1,6 @@ // qtrocket headers #include "Rocket.h" -#include "QtRocket.h" namespace model { @@ -45,4 +44,4 @@ double Rocket::getMass(double t) return totalMass; } -} // namespace model \ No newline at end of file +} // namespace model diff --git a/model/Rocket.h b/model/Rocket.h index a7f2120..9949583 100644 --- a/model/Rocket.h +++ b/model/Rocket.h @@ -13,9 +13,7 @@ /// \endcond // qtrocket headers -#include "model/ThrustCurve.h" #include "sim/Propagator.h" -#include "utils/math/MathTypes.h" #include "model/Stage.h" #include "model/Propagatable.h" @@ -52,7 +50,7 @@ public: * @param t current simulation time * @return thrust in Newtons */ - double getThrust(double t); + double getThrust(double t) override; /** * @brief getMass returns current rocket diff --git a/model/Stage.cpp b/model/Stage.cpp index ab6a0c1..6d17794 100644 --- a/model/Stage.cpp +++ b/model/Stage.cpp @@ -1,8 +1,6 @@ /// \cond // C headers // C++ headers -#include -#include // 3rd party headers /// \endcond @@ -24,4 +22,4 @@ void Stage::setMotorModel(const model::MotorModel& motor) mm = motor; } -} // namespace model \ No newline at end of file +} // namespace model diff --git a/model/ThrustCurve.cpp b/model/ThrustCurve.cpp index fd7a803..8351d39 100644 --- a/model/ThrustCurve.cpp +++ b/model/ThrustCurve.cpp @@ -8,8 +8,6 @@ /// \endcond #include "model/ThrustCurve.h" -#include "utils/Logger.h" - ThrustCurve::ThrustCurve(std::vector>& tc) : thrustCurve(tc), diff --git a/sim/Aero.h b/sim/Aero.h index 08636f7..c57a778 100644 --- a/sim/Aero.h +++ b/sim/Aero.h @@ -4,7 +4,6 @@ /// \cond // C headers // C++ headers -#include // 3rd party headers /// \endcond @@ -38,4 +37,4 @@ private: }; } -#endif // SIM_AERO_H \ No newline at end of file +#endif // SIM_AERO_H diff --git a/sim/ConstantAtmosphere.h b/sim/ConstantAtmosphere.h index 226eead..2b33762 100644 --- a/sim/ConstantAtmosphere.h +++ b/sim/ConstantAtmosphere.h @@ -3,7 +3,6 @@ // qtrocket headers #include "AtmosphericModel.h" -#include "utils/math/Constants.h" namespace sim { diff --git a/sim/DESolver.h b/sim/DESolver.h index 54a4dee..f264e7c 100644 --- a/sim/DESolver.h +++ b/sim/DESolver.h @@ -4,13 +4,12 @@ /// \cond // C headers // C++ headers -#include +#include // 3rd party headers /// \endcond // qtrocket headers -#include "utils/math/MathTypes.h" namespace sim { diff --git a/sim/Environment.h b/sim/Environment.h index b866420..d6d759e 100644 --- a/sim/Environment.h +++ b/sim/Environment.h @@ -17,7 +17,6 @@ #include "sim/ConstantAtmosphere.h" #include "sim/USStandardAtmosphere.h" -#include "sim/GeoidModel.h" namespace sim { diff --git a/sim/Propagator.cpp b/sim/Propagator.cpp index 3b322d2..1ad0634 100644 --- a/sim/Propagator.cpp +++ b/sim/Propagator.cpp @@ -2,7 +2,6 @@ /// \cond // C headers // C++ headers -#include #include #include #include diff --git a/sim/RK4Solver.h b/sim/RK4Solver.h index 46a13ec..8365cad 100644 --- a/sim/RK4Solver.h +++ b/sim/RK4Solver.h @@ -4,10 +4,8 @@ /// \cond // C headers // C++ headers -#include #include #include -#include // 3rd party headers diff --git a/sim/USStandardAtmosphere.cpp b/sim/USStandardAtmosphere.cpp index 1081b00..c6a0cf3 100644 --- a/sim/USStandardAtmosphere.cpp +++ b/sim/USStandardAtmosphere.cpp @@ -18,9 +18,9 @@ namespace sim { // Populate static data -utils::BinMap initTemps() +utils::Bin initTemps() { - utils::BinMap map; + utils::Bin map; map.insert(std::make_pair(0.0, 288.15)); map.insert(std::make_pair(11000.0, 216.65)); map.insert(std::make_pair(20000.0, 216.65)); @@ -33,9 +33,9 @@ utils::BinMap initTemps() } -utils::BinMap initLapseRates() +utils::Bin initLapseRates() { - utils::BinMap map; + utils::Bin map; map.insert(std::make_pair(0.0, 0.0065)); map.insert(std::make_pair(11000.0, 0.0)); map.insert(std::make_pair(20000.0, -0.001)); @@ -47,9 +47,9 @@ utils::BinMap initLapseRates() return map; } -utils::BinMap initDensities() +utils::Bin initDensities() { - utils::BinMap map; + utils::Bin map; map.insert(std::make_pair(0.0, 1.225)); map.insert(std::make_pair(11000.0, 0.36391)); map.insert(std::make_pair(20000.0, 0.08803)); @@ -61,9 +61,9 @@ utils::BinMap initDensities() return map; } -utils::BinMap initPressures() +utils::Bin initPressures() { - utils::BinMap map; + utils::Bin map; map.insert(std::make_pair(0.0, 101325)); map.insert(std::make_pair(11000.0, 22632.1)); map.insert(std::make_pair(20000.0, 5474.89)); @@ -75,10 +75,10 @@ utils::BinMap initPressures() return map; } -utils::BinMap USStandardAtmosphere::temperatureLapseRate(initLapseRates()); -utils::BinMap USStandardAtmosphere::standardTemperature(initTemps()); -utils::BinMap USStandardAtmosphere::standardDensity(initDensities()); -utils::BinMap USStandardAtmosphere::standardPressure(initPressures()); +utils::Bin USStandardAtmosphere::temperatureLapseRate(initLapseRates()); +utils::Bin USStandardAtmosphere::standardTemperature(initTemps()); +utils::Bin USStandardAtmosphere::standardDensity(initDensities()); +utils::Bin USStandardAtmosphere::standardPressure(initPressures()); USStandardAtmosphere::USStandardAtmosphere() { diff --git a/sim/USStandardAtmosphere.h b/sim/USStandardAtmosphere.h index 740df66..3821295 100644 --- a/sim/USStandardAtmosphere.h +++ b/sim/USStandardAtmosphere.h @@ -3,7 +3,7 @@ // qtrocket headers #include "sim/AtmosphericModel.h" -#include "utils/BinMap.h" +#include "utils/Bin.h" namespace sim { @@ -33,10 +33,10 @@ public: double getDynamicViscosity(double altitude) override; private: - static utils::BinMap temperatureLapseRate; - static utils::BinMap standardTemperature; - static utils::BinMap standardDensity; - static utils::BinMap standardPressure; + static utils::Bin temperatureLapseRate; + static utils::Bin standardTemperature; + static utils::Bin standardDensity; + static utils::Bin standardPressure; }; diff --git a/utils/BinMap.cpp b/utils/Bin.cpp similarity index 91% rename from utils/BinMap.cpp rename to utils/Bin.cpp index 5ebe68b..5130bd1 100644 --- a/utils/BinMap.cpp +++ b/utils/Bin.cpp @@ -11,7 +11,7 @@ /// \endcond // qtrocket headers -#include "BinMap.h" +#include "Bin.h" // TODO: Check on the availability of this in Clang. // Replace libfmt with format when LLVM libc++ supports it @@ -20,33 +20,33 @@ namespace utils { -BinMap::BinMap() +Bin::Bin() : bins() { } -BinMap::BinMap(BinMap&& o) +Bin::Bin(Bin&& o) : bins(std::move(o.bins)) { } -BinMap::~BinMap() +Bin::~Bin() { } // TODO: Very low priority, but if anyone wants to make this more efficient it could be // interesting -void BinMap::insert(const std::pair& toInsert) +void Bin::insert(const std::pair& toInsert) { bins.push_back(toInsert); std::sort(bins.begin(), bins.end(), [](const auto& a, const auto& b){ return a.first < b.first; }); } -double BinMap::operator[](double key) +double Bin::operator[](double key) { auto iter = bins.begin(); // If the key is less than the lowest bin value, then it is out of range @@ -74,7 +74,7 @@ double BinMap::operator[](double key) return retVal; } -double BinMap::getBinBase(double key) +double Bin::getBinBase(double key) { auto iter = bins.begin(); // If the key is less than the lowest bin value, then it is out of range diff --git a/utils/BinMap.h b/utils/Bin.h similarity index 86% rename from utils/BinMap.h rename to utils/Bin.h index 216cd7d..86843b0 100644 --- a/utils/BinMap.h +++ b/utils/Bin.h @@ -1,5 +1,5 @@ -#ifndef UTILS_BINMAP_H -#define UTILS_BINMAP_H +#ifndef UTILS_BIN_H +#define UTILS_BIN_H /// \cond // C headers @@ -22,12 +22,12 @@ namespace utils { * @todo Make this class behave more like a proper STL container. Templetize it for one * */ -class BinMap +class Bin { public: - BinMap(); - BinMap(BinMap&& o); - ~BinMap(); + Bin(); + Bin(Bin&& o); + ~Bin(); void insert(const std::pair& toInsert); double operator[](double key); @@ -40,4 +40,4 @@ private: } // namespace utils -#endif // UTILS_BINMAP_H +#endif // UTILS_BIN_H diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt index eebe01d..c748e02 100644 --- a/utils/CMakeLists.txt +++ b/utils/CMakeLists.txt @@ -1,6 +1,6 @@ add_library(utils - BinMap.cpp - BinMap.h + Bin.cpp + Bin.h CurlConnection.cpp CurlConnection.h Logger.cpp @@ -18,6 +18,10 @@ add_library(utils math/MathTypes.h math/UtilityMathFunctions.h) + +target_include_directories(utils PRIVATE + ${Boost_INCLUDE_DIR}) + target_link_libraries(utils PUBLIC libcurl jsoncpp_static diff --git a/utils/Logger.cpp b/utils/Logger.cpp index bce72dc..2860874 100644 --- a/utils/Logger.cpp +++ b/utils/Logger.cpp @@ -40,6 +40,13 @@ void Logger::log(std::string_view msg, const LogLevel& lvl) // all levels at or lower than the current level. switch(currentLevel) { + case PERF_: + if(lvl == PERF_) + { + outFile << "[PERF] " << msg << std::endl; + std::cout << "[PERF] " << msg << "\n"; + } + [[fallthrough]]; case DEBUG_: if(lvl == DEBUG_) { diff --git a/utils/Logger.h b/utils/Logger.h index 18c7467..752baf8 100644 --- a/utils/Logger.h +++ b/utils/Logger.h @@ -25,7 +25,8 @@ public: ERROR_, WARN_, INFO_, - DEBUG_ + DEBUG_, + PERF_ }; static Logger* getInstance(); @@ -38,16 +39,11 @@ public: void setLogLevel(const LogLevel& lvl); - /* - std::function error; - std::function warn; - std::function info; - std::function debug; - */ inline void error(std::string_view msg) { log(msg, ERROR_); } inline void warn(std::string_view msg) { log(msg, WARN_); } inline void info(std::string_view msg) { log(msg, INFO_); } inline void debug(std::string_view msg) { log(msg, DEBUG_); } + inline void perf(std::string_view msg) { log(msg, PERF_); } void log(std::ostream& o, const std::string& msg); From f4e560fcd9d56653615d61e364fb19c6e5661372 Mon Sep 17 00:00:00 2001 From: Travis Hunter Date: Sun, 18 Feb 2024 16:38:22 -0700 Subject: [PATCH 16/32] quick cleanup --- QtRocket.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/QtRocket.cpp b/QtRocket.cpp index a42dadc..764a057 100644 --- a/QtRocket.cpp +++ b/QtRocket.cpp @@ -87,13 +87,13 @@ QtRocket::QtRocket() motorDatabase = std::make_shared(); - logger->debug("Initial states vector size: " + states.capacity() ); + logger->debug("Initial states vector size: " + std::to_string(states.capacity()) ); // Reserve at least 1024 spaces for StateData if(states.capacity() < 1024) { states.reserve(1024); } - logger->debug("New states vector size: " + states.capacity() ); + logger->debug("New states vector size: " + std::to_string(states.capacity()) ); } int QtRocket::run(int argc, char* argv[]) From 6a015d97975bb0b4744dac9277c186b1a747e32f Mon Sep 17 00:00:00 2001 From: Travis Hunter Date: Thu, 14 Mar 2024 07:05:16 -0600 Subject: [PATCH 17/32] Fix crash on motor load --- model/Rocket.cpp | 6 +++++- model/Rocket.h | 10 +++++----- model/Stage.cpp | 3 ++- model/Stage.h | 4 ++-- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/model/Rocket.cpp b/model/Rocket.cpp index 64d39be..f81cd5d 100644 --- a/model/Rocket.cpp +++ b/model/Rocket.cpp @@ -7,6 +7,10 @@ namespace model Rocket::Rocket() { + // A rocket needs at least one stage. Upon creation, we need to create at least one stage + currentStage.reset(new Stage("sustainer")); + stages.push_back(currentStage); + } @@ -39,7 +43,7 @@ double Rocket::getMass(double t) double totalMass = 0.0; for(const auto& stage : stages) { - totalMass += stage.second->getMass(t); + totalMass += stage->getMass(t); } return totalMass; } diff --git a/model/Rocket.h b/model/Rocket.h index 9949583..4078924 100644 --- a/model/Rocket.h +++ b/model/Rocket.h @@ -4,7 +4,7 @@ /// \cond // C headers // C++ headers -#include +#include #include #include #include // std::move @@ -57,7 +57,7 @@ public: * @param t current simulation time * @return mass in kg */ - virtual double getMass(double t) override; + double getMass(double t) override; /** * @brief setMotorModel @@ -84,8 +84,8 @@ public: */ void setName(const std::string& n) { name = n; } - virtual double getDragCoefficient() override { return 1.0; } - virtual void setDragCoefficient(double d) override { } + double getDragCoefficient() override { return 1.0; } + void setDragCoefficient(double d) override { } void setMass(double m) { } std::shared_ptr getCurrentStage() { return currentStage; } @@ -94,7 +94,7 @@ private: std::string name; /// Rocket name - std::map> stages; + std::vector> stages; std::shared_ptr currentStage; //model::MotorModel mm; /// Current Motor Model diff --git a/model/Stage.cpp b/model/Stage.cpp index 6d17794..af7c593 100644 --- a/model/Stage.cpp +++ b/model/Stage.cpp @@ -11,7 +11,8 @@ namespace model { -Stage::Stage() +Stage::Stage(const std::string& inName) + : name(inName) {} Stage::~Stage() diff --git a/model/Stage.h b/model/Stage.h index f215674..bf61a40 100644 --- a/model/Stage.h +++ b/model/Stage.h @@ -21,7 +21,7 @@ namespace model class Stage : public Propagatable { public: - Stage(); + Stage(const std::string& inName); virtual ~Stage(); /** @@ -72,4 +72,4 @@ private: } // namespace model -#endif // SIM_STAGE_H \ No newline at end of file +#endif // SIM_STAGE_H From e5c068ddf75f544b03b87750cef076bd4b767220 Mon Sep 17 00:00:00 2001 From: Travis Hunter Date: Thu, 14 Mar 2024 18:06:05 -0600 Subject: [PATCH 18/32] Remove external dependency on fmtlib in favor of std::format. Begin transition of model::Part into an interface --- CMakeLists.txt | 8 +++--- model/Part.cpp | 20 ++------------- model/Part.h | 51 ++++++++++++++++++++++++--------------- model/Rocket.h | 2 +- model/tests/PartTests.cpp | 16 +++--------- utils/Bin.cpp | 7 +++--- utils/CMakeLists.txt | 1 - 7 files changed, 45 insertions(+), 60 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bc3dc87..122da0c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,10 +17,10 @@ endif() FetchContent_MakeAvailable(googletest) # fmtlib dependency -FetchContent_Declare(fmt - GIT_REPOSITORY https://github.com/fmtlib/fmt - GIT_TAG 9.1.0) -FetchContent_MakeAvailable(fmt) +#FetchContent_Declare(fmt +# GIT_REPOSITORY https://github.com/fmtlib/fmt +# GIT_TAG 9.1.0) +#FetchContent_MakeAvailable(fmt) # jsoncpp dependency FetchContent_Declare(jsoncpp diff --git a/model/Part.cpp b/model/Part.cpp index 8cae619..dedb0c7 100644 --- a/model/Part.cpp +++ b/model/Part.cpp @@ -7,12 +7,7 @@ namespace model Part::Part(const std::string& n, const Matrix3& I, double m, - const Vector3& centerMass, - double l, - double inRadTop, - double outRadTop, - double inRadBottom, - double outRadBottom) + const Vector3& centerMass) : parent(nullptr), name(n), inertiaTensor(I), @@ -20,11 +15,6 @@ Part::Part(const std::string& n, mass(m), compositeMass(m), cm(centerMass), - length(l), - innerRadiusTop(inRadTop), - outerRadiusTop(outRadTop), - innerRadiusBottom(inRadBottom), - outerRadiusBottom(outRadBottom), needsRecomputing(false), childParts() { } @@ -40,11 +30,6 @@ Part::Part(const Part& orig) mass(orig.mass), compositeMass(orig.compositeMass), cm(orig.cm), - length(orig.length), - innerRadiusTop(orig.innerRadiusTop), - outerRadiusTop(orig.outerRadiusTop), - innerRadiusBottom(orig.innerRadiusBottom), - outerRadiusBottom(orig.outerRadiusBottom), needsRecomputing(orig.needsRecomputing), childParts() { @@ -65,7 +50,6 @@ Part::Part(const Part& orig) } - double Part::getChildMasses(double t) { double childMasses{0.0}; @@ -122,4 +106,4 @@ void Part::recomputeInertiaTensor() needsRecomputing = false; } -} // namespace model \ No newline at end of file +} // namespace model diff --git a/model/Part.h b/model/Part.h index 434a65c..42fe0bc 100644 --- a/model/Part.h +++ b/model/Part.h @@ -22,17 +22,43 @@ public: Part(const std::string& name, const Matrix3& I, double m, - const Vector3& centerMass, - double length, - double inRadTop, - double outRadTop, - double inRadBottom, - double outRadBottom); + const Vector3& centerMass); virtual ~Part(); Part(const Part&); + Part& operator=(Part other) + { + if(this != &other) + { + std::swap(parent, other.parent); + std::swap(name, other.name); + std::swap(inertiaTensor, other.inertiaTensor); + std::swap(compositeInertiaTensor, other.compositeInertiaTensor); + std::swap(mass, other.mass); + std::swap(compositeMass, other.compositeMass); + std::swap(cm, other.cm); + std::swap(needsRecomputing, other.needsRecomputing); + std::swap(childParts, other.childParts); + } + return *this; + } + Part& operator=(Part&& other) + { + parent = std::move(other.parent); + name = std::move(other.name); + inertiaTensor = std::move(other.inertiaTensor); + compositeInertiaTensor = std::move(other.compositeInertiaTensor); + mass = std::move(other.mass); + compositeMass = std::move(other.compositeMass); + cm = std::move(other.cm); + needsRecomputing = std::move(other.needsRecomputing); + childParts = std::move(other.childParts); + + return *this; + } + void setMass(double m) { mass = m; } // Set the inertia tensor @@ -42,11 +68,6 @@ public: // Special version of setCM that assumes the cm lies along the body x-axis void setCm(double x) { cm = {x, 0.0, 0.0}; } - void setLength(double l) { length = l; } - - void setInnerRadius(double r) { innerRadiusTop = r; innerRadiusBottom = r; } - void setOuterRadius(double r) { outerRadiusTop = r; outerRadiusBottom = r; } - double getMass(double t) { return mass; @@ -101,14 +122,6 @@ private: Vector3 cm; // center of mass wrt middle of component - double length; - - double innerRadiusTop; - double outerRadiusTop; - - double innerRadiusBottom; - double outerRadiusBottom; - bool needsRecomputing{false}; /// @brief child parts and the relative positions of their center of mass w.r.t. diff --git a/model/Rocket.h b/model/Rocket.h index 4078924..d0c3bb4 100644 --- a/model/Rocket.h +++ b/model/Rocket.h @@ -76,7 +76,7 @@ public: * @param cond time/state pair * @return true if the passed-in time/state satisfies the terminate condition */ - virtual bool terminateCondition(const std::pair& cond) override; + bool terminateCondition(const std::pair& cond) override; /** * @brief setName sets the rocket name diff --git a/model/tests/PartTests.cpp b/model/tests/PartTests.cpp index f985d3a..6e23d7d 100644 --- a/model/tests/PartTests.cpp +++ b/model/tests/PartTests.cpp @@ -52,12 +52,7 @@ TEST(PartTest, CreationTests) model::Part testPart("testPart", inertia, 1.0, - cm, - 2.0, - 1.0, - 1.1, - 1.1, - 1.0); + cm); Matrix3 inertia2; inertia2 << 1, 0, 0, @@ -67,14 +62,9 @@ TEST(PartTest, CreationTests) model::Part testPart2("testPart2", inertia2, 1.0, - cm2, - 2.0, - 1.0, - 1.1, - 1.1, - 1.0); + cm2); Vector3 R{2.0, 2.0, 2.0}; testPart.addChildPart(testPart2, R); -} \ No newline at end of file +} diff --git a/utils/Bin.cpp b/utils/Bin.cpp index 5130bd1..8f9b15f 100644 --- a/utils/Bin.cpp +++ b/utils/Bin.cpp @@ -3,11 +3,10 @@ // C headers // C++ headers #include -#include +#include #include // 3rd party headers -#include /// \endcond // qtrocket headers @@ -56,7 +55,7 @@ double Bin::operator[](double key) if(key < iter->first) { throw std::out_of_range( - fmt::format("{} less than lower bound {} of BinMap", key, iter->first)); + std::format("{} less than lower bound {} of BinMap", key, iter->first)); } // Increment it and start searching If we reach the end without finding an existing key // greater than our search term, then we've just hit the last bin and return that @@ -84,7 +83,7 @@ double Bin::getBinBase(double key) if(key < iter->first) { throw std::out_of_range( - fmt::format("{} less than lower bound {} of BinMap", key, iter->first)); + std::format("{} less than lower bound {} of BinMap", key, iter->first)); } // Increment it and start searching If we reach the end without finding an existing key // greater than our search term, then we've just hit the last bin and return that diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt index c748e02..b3a1765 100644 --- a/utils/CMakeLists.txt +++ b/utils/CMakeLists.txt @@ -25,5 +25,4 @@ target_include_directories(utils PRIVATE target_link_libraries(utils PUBLIC libcurl jsoncpp_static - fmt::fmt-header-only eigen) From 5db186e8da4bbde45dcf462abde40498ab765d71 Mon Sep 17 00:00:00 2001 From: Travis Hunter Date: Fri, 15 Mar 2024 10:50:00 -0600 Subject: [PATCH 19/32] Fixed build on Fedora with boost. I'm not sure why it worked on FreeBSD but in Fedora 39 CMake didn't set boost up properly, or maybe the FetchContent(Boost) just never worked and it always found the installed boost library? But it fetches and sets up dynamically now... --- CMakeLists.txt | 5 +++-- utils/CMakeLists.txt | 6 ++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 122da0c..dec1a0f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,11 +54,12 @@ FetchContent_Declare(eigen FetchContent_MakeAvailable(eigen) # boost dependency + FetchContent_Declare(Boost GIT_REPOSITORY https://github.com/boostorg/boost - GIT_TAG boost-1.82.0) + GIT_TAG boost-1.84.0) +set(BOOST_INCLUDE_LIBRARIES property_tree) FetchContent_MakeAvailable(Boost) - # Add qtrocket subdirectories. These are components that will be linked in diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt index b3a1765..251b567 100644 --- a/utils/CMakeLists.txt +++ b/utils/CMakeLists.txt @@ -19,10 +19,12 @@ add_library(utils math/UtilityMathFunctions.h) -target_include_directories(utils PRIVATE - ${Boost_INCLUDE_DIR}) +#target_include_directories(utils PRIVATE +# ${Boost_INCLUDE_DIRS}) + target_link_libraries(utils PUBLIC libcurl + Boost::property_tree jsoncpp_static eigen) From bf83ffc9ecc575d0822dec6f086882c48d77a8b1 Mon Sep 17 00:00:00 2001 From: cthunter01 Date: Fri, 15 Mar 2024 11:01:15 -0600 Subject: [PATCH 20/32] Add github actions: Create cmake-multi-platform.yml --- .github/workflows/cmake-multi-platform.yml | 75 ++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 .github/workflows/cmake-multi-platform.yml diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/cmake-multi-platform.yml new file mode 100644 index 0000000..d019efc --- /dev/null +++ b/.github/workflows/cmake-multi-platform.yml @@ -0,0 +1,75 @@ +# This starter workflow is for a CMake project running on multiple platforms. There is a different starter workflow if you just want a single platform. +# See: https://github.com/actions/starter-workflows/blob/main/ci/cmake-single-platform.yml +name: CMake on multiple platforms + +on: + push: + branches: [ "development" ] + pull_request: + branches: [ "development" ] + +jobs: + build: + runs-on: ${{ matrix.os }} + + strategy: + # Set fail-fast to false to ensure that feedback is delivered for all matrix combinations. Consider changing this to true when your workflow is stable. + fail-fast: false + + # Set up a matrix to run the following 3 configurations: + # 1. + # 2. + # 3. + # + # To add more build types (Release, Debug, RelWithDebInfo, etc.) customize the build_type list. + matrix: + os: [ubuntu-latest, windows-latest] + build_type: [Release] + c_compiler: [gcc, clang, cl] + include: + #- os: windows-latest + # c_compiler: cl + # cpp_compiler: cl + - os: ubuntu-latest + c_compiler: gcc + cpp_compiler: g++ + - os: ubuntu-latest + c_compiler: clang + cpp_compiler: clang++ + exclude: + - os: windows-latest + c_compiler: gcc + - os: windows-latest + c_compiler: clang + - os: ubuntu-latest + c_compiler: cl + + steps: + - uses: actions/checkout@v3 + + - name: Set reusable strings + # Turn repeated input strings (such as the build output directory) into step outputs. These step outputs can be used throughout the workflow file. + id: strings + shell: bash + run: | + echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT" + + - name: Configure CMake + # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. + # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type + run: > + cmake -B ${{ steps.strings.outputs.build-output-dir }} + -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }} + -DCMAKE_C_COMPILER=${{ matrix.c_compiler }} + -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} + -S ${{ github.workspace }} + + - name: Build + # Build your program with the given configuration. Note that --config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). + run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config ${{ matrix.build_type }} + + - name: Test + working-directory: ${{ steps.strings.outputs.build-output-dir }} + # Execute tests defined by the CMake configuration. Note that --build-config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). + # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + run: ctest --build-config ${{ matrix.build_type }} From 46c5caa1ab27620a8f44b146ac45c4268e14ec03 Mon Sep 17 00:00:00 2001 From: cthunter01 Date: Fri, 15 Mar 2024 11:17:44 -0600 Subject: [PATCH 21/32] Add "install Qt" to github actions: Update cmake-multi-platform.yml --- .github/workflows/cmake-multi-platform.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/cmake-multi-platform.yml index d019efc..2261783 100644 --- a/.github/workflows/cmake-multi-platform.yml +++ b/.github/workflows/cmake-multi-platform.yml @@ -47,6 +47,11 @@ jobs: steps: - uses: actions/checkout@v3 + - name: Install Qt + uses: jurplel/install-qt-action@v3 + version: 6.6.2 + target: desktop + - name: Set reusable strings # Turn repeated input strings (such as the build output directory) into step outputs. These step outputs can be used throughout the workflow file. id: strings From 17e87e2c4093452733efb0542c7d81505a80d5c1 Mon Sep 17 00:00:00 2001 From: cthunter01 Date: Fri, 15 Mar 2024 11:20:38 -0600 Subject: [PATCH 22/32] Update cmake-multi-platform.yml --- .github/workflows/cmake-multi-platform.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/cmake-multi-platform.yml index 2261783..35938e4 100644 --- a/.github/workflows/cmake-multi-platform.yml +++ b/.github/workflows/cmake-multi-platform.yml @@ -27,9 +27,9 @@ jobs: build_type: [Release] c_compiler: [gcc, clang, cl] include: - #- os: windows-latest - # c_compiler: cl - # cpp_compiler: cl + - os: windows-latest + c_compiler: cl + cpp_compiler: cl - os: ubuntu-latest c_compiler: gcc cpp_compiler: g++ @@ -49,8 +49,9 @@ jobs: - name: Install Qt uses: jurplel/install-qt-action@v3 - version: 6.6.2 - target: desktop + with: + version: 6.6.2 + target: desktop - name: Set reusable strings # Turn repeated input strings (such as the build output directory) into step outputs. These step outputs can be used throughout the workflow file. From fb7e267bb45a14c6d3094e5fc8e165cbcc49a4fe Mon Sep 17 00:00:00 2001 From: cthunter01 Date: Fri, 15 Mar 2024 11:58:14 -0600 Subject: [PATCH 23/32] Test gcc13 on ubuntu-latest: Update cmake-multi-platform.yml --- .github/workflows/cmake-multi-platform.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/cmake-multi-platform.yml index 35938e4..9d34c6a 100644 --- a/.github/workflows/cmake-multi-platform.yml +++ b/.github/workflows/cmake-multi-platform.yml @@ -23,7 +23,8 @@ jobs: # # To add more build types (Release, Debug, RelWithDebInfo, etc.) customize the build_type list. matrix: - os: [ubuntu-latest, windows-latest] + #os: [ubuntu-latest, windows-latest] + os: [ubuntu-latest] build_type: [Release] c_compiler: [gcc, clang, cl] include: @@ -31,6 +32,7 @@ jobs: c_compiler: cl cpp_compiler: cl - os: ubuntu-latest + container: gcc:13 c_compiler: gcc cpp_compiler: g++ - os: ubuntu-latest From 903b0ee8e690c773c2cff213008f47b7c73e069d Mon Sep 17 00:00:00 2001 From: cthunter01 Date: Fri, 15 Mar 2024 12:08:08 -0600 Subject: [PATCH 24/32] Update cmake-multi-platform.yml --- .github/workflows/cmake-multi-platform.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/cmake-multi-platform.yml index 9d34c6a..f41a136 100644 --- a/.github/workflows/cmake-multi-platform.yml +++ b/.github/workflows/cmake-multi-platform.yml @@ -32,9 +32,10 @@ jobs: c_compiler: cl cpp_compiler: cl - os: ubuntu-latest - container: gcc:13 c_compiler: gcc + gcc: 13 cpp_compiler: g++ + g++: 13 - os: ubuntu-latest c_compiler: clang cpp_compiler: clang++ From 78bfecd8a3a976bda6dddd61b12d5392ac029209 Mon Sep 17 00:00:00 2001 From: Travis Hunter Date: Fri, 15 Mar 2024 12:39:40 -0600 Subject: [PATCH 25/32] Split github actions by OS --- .github/workflows/ubuntu-latest-gcc13.yml | 64 +++++++++++++++++++ ...-multi-platform.yml => windows-latest.yml} | 57 +++++++---------- 2 files changed, 86 insertions(+), 35 deletions(-) create mode 100644 .github/workflows/ubuntu-latest-gcc13.yml rename .github/workflows/{cmake-multi-platform.yml => windows-latest.yml} (58%) diff --git a/.github/workflows/ubuntu-latest-gcc13.yml b/.github/workflows/ubuntu-latest-gcc13.yml new file mode 100644 index 0000000..2c98da0 --- /dev/null +++ b/.github/workflows/ubuntu-latest-gcc13.yml @@ -0,0 +1,64 @@ +# This starter workflow is for a CMake project running on multiple platforms. There is a different starter workflow if you just want a single platform. +# See: https://github.com/actions/starter-workflows/blob/main/ci/cmake-single-platform.yml +name: CMake on multiple platforms + +on: + push: + branches: [ "development" ] + pull_request: + branches: [ "development" ] + +env: + # Customize the CMake built type here (Release, Debug, RelWithDebInfo, etc) + BUILD_TYPE: Release + cpp_compiler: g++-13 + c_compiler: gcc-13 + +jobs: + build: + runs-on: ubuntu-latest + + strategy: + # Set fail-fast to false to ensure that feedback is delivered for all matrix combinations. + # Consider changing this to true when your workflow is stable. + fail-fast: false + + steps: + - uses: actions/checkout@v3 + + - name: Install Qt + uses: jurplel/install-qt-action@v3 + with: + version: 6.6.2 + target: desktop + + - name: Set reusable strings + # Turn repeated input strings (such as the build output directory) into step outputs. + # These step outputs can be used throughout the workflow file. + id: strings + shell: bash + run: | + echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT" + + - name: Configure CMake + # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you + # are using a single-configuration generator such as make. + # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type + run: > + cmake -B ${{ steps.strings.outputs.build-output-dir }} + -DCMAKE_CXX_COMPILER=${{ env.cpp_compiler }} + -DCMAKE_C_COMPILER=${{ env.c_compiler }} + -DCMAKE_BUILD_TYPE=${{ env.build_type }} + -S ${{ github.workspace }} + + - name: Build + # Build your program with the given configuration. Note that --config is needed because the + # default Windows generator is a multi-config generator (Visual Studio generator). + run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config ${{ env.build_type }} + + - name: Test + working-directory: ${{ steps.strings.outputs.build-output-dir }} + # Execute tests defined by the CMake configuration. Note that --build-config is needed + # because the default Windows generator is a multi-config generator (Visual Studio generator). + # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + run: ctest --build-config ${{ env.build_type }} diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/windows-latest.yml similarity index 58% rename from .github/workflows/cmake-multi-platform.yml rename to .github/workflows/windows-latest.yml index f41a136..de4f5ef 100644 --- a/.github/workflows/cmake-multi-platform.yml +++ b/.github/workflows/windows-latest.yml @@ -8,12 +8,18 @@ on: pull_request: branches: [ "development" ] +env: + build_type: Release + c_compiler: cl + cpp_compiler: cl + jobs: build: - runs-on: ${{ matrix.os }} + runs-on: ${{ windows-latest }} strategy: - # Set fail-fast to false to ensure that feedback is delivered for all matrix combinations. Consider changing this to true when your workflow is stable. + # Set fail-fast to false to ensure that feedback is delivered for all matrix combinations. + # Consider changing this to true when your workflow is stable. fail-fast: false # Set up a matrix to run the following 3 configurations: @@ -22,30 +28,7 @@ jobs: # 3. # # To add more build types (Release, Debug, RelWithDebInfo, etc.) customize the build_type list. - matrix: - #os: [ubuntu-latest, windows-latest] - os: [ubuntu-latest] - build_type: [Release] - c_compiler: [gcc, clang, cl] - include: - - os: windows-latest - c_compiler: cl - cpp_compiler: cl - - os: ubuntu-latest - c_compiler: gcc - gcc: 13 - cpp_compiler: g++ - g++: 13 - - os: ubuntu-latest - c_compiler: clang - cpp_compiler: clang++ - exclude: - - os: windows-latest - c_compiler: gcc - - os: windows-latest - c_compiler: clang - - os: ubuntu-latest - c_compiler: cl + #os: [ubuntu-latest, windows-latest] steps: - uses: actions/checkout@v3 @@ -57,28 +40,32 @@ jobs: target: desktop - name: Set reusable strings - # Turn repeated input strings (such as the build output directory) into step outputs. These step outputs can be used throughout the workflow file. + # Turn repeated input strings (such as the build output directory) into step outputs. + # These step outputs can be used throughout the workflow file. id: strings shell: bash run: | echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT" - name: Configure CMake - # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. + # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you + # are using a single-configuration generator such as make. # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type run: > cmake -B ${{ steps.strings.outputs.build-output-dir }} - -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }} - -DCMAKE_C_COMPILER=${{ matrix.c_compiler }} - -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} + -DCMAKE_CXX_COMPILER=${{ env.cpp_compiler }} + -DCMAKE_C_COMPILER=${{ env.c_compiler }} + -DCMAKE_BUILD_TYPE=${{ env.build_type }} -S ${{ github.workspace }} - name: Build - # Build your program with the given configuration. Note that --config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). - run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config ${{ matrix.build_type }} + # Build your program with the given configuration. Note that --config is needed because the + # default Windows generator is a multi-config generator (Visual Studio generator). + run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config ${{ env.build_type }} - name: Test working-directory: ${{ steps.strings.outputs.build-output-dir }} - # Execute tests defined by the CMake configuration. Note that --build-config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). + # Execute tests defined by the CMake configuration. Note that --build-config is needed because + # the default Windows generator is a multi-config generator (Visual Studio generator). # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail - run: ctest --build-config ${{ matrix.build_type }} + run: ctest --build-config ${{ env.build_type }} From f938d5e21c6cf46060f8546a26fc97c66688560d Mon Sep 17 00:00:00 2001 From: Travis Hunter Date: Fri, 15 Mar 2024 12:41:12 -0600 Subject: [PATCH 26/32] fix typo in windows-latest.yml action --- .github/workflows/windows-latest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/windows-latest.yml b/.github/workflows/windows-latest.yml index de4f5ef..656ad21 100644 --- a/.github/workflows/windows-latest.yml +++ b/.github/workflows/windows-latest.yml @@ -15,7 +15,7 @@ env: jobs: build: - runs-on: ${{ windows-latest }} + runs-on: windows-latest strategy: # Set fail-fast to false to ensure that feedback is delivered for all matrix combinations. From 1a29d0b04e818d70a65ebf96092b64b015b83707 Mon Sep 17 00:00:00 2001 From: Travis Hunter Date: Fri, 15 Mar 2024 12:58:37 -0600 Subject: [PATCH 27/32] Tweaking workflow. Trying to use gcc-13 on ubuntu --- ...ws-latest.yml => cmake-multi-platform.yml} | 54 ++++++++++------ .github/workflows/ubuntu-latest-gcc13.yml | 64 ------------------- .gitignore | 1 + 3 files changed, 34 insertions(+), 85 deletions(-) rename .github/workflows/{windows-latest.yml => cmake-multi-platform.yml} (59%) delete mode 100644 .github/workflows/ubuntu-latest-gcc13.yml diff --git a/.github/workflows/windows-latest.yml b/.github/workflows/cmake-multi-platform.yml similarity index 59% rename from .github/workflows/windows-latest.yml rename to .github/workflows/cmake-multi-platform.yml index 656ad21..65651d1 100644 --- a/.github/workflows/windows-latest.yml +++ b/.github/workflows/cmake-multi-platform.yml @@ -1,4 +1,5 @@ -# This starter workflow is for a CMake project running on multiple platforms. There is a different starter workflow if you just want a single platform. +# This starter workflow is for a CMake project running on multiple platforms. There is a different +# starter workflow if you just want a single platform. # See: https://github.com/actions/starter-workflows/blob/main/ci/cmake-single-platform.yml name: CMake on multiple platforms @@ -8,14 +9,9 @@ on: pull_request: branches: [ "development" ] -env: - build_type: Release - c_compiler: cl - cpp_compiler: cl - jobs: build: - runs-on: windows-latest + runs-on: ${{ matrix.os }} strategy: # Set fail-fast to false to ensure that feedback is delivered for all matrix combinations. @@ -28,7 +24,27 @@ jobs: # 3. # # To add more build types (Release, Debug, RelWithDebInfo, etc.) customize the build_type list. - #os: [ubuntu-latest, windows-latest] + matrix: + os: [ubuntu-latest, windows-latest] + build_type: [Release] + c_compiler: [gcc-13, clang, cl] + include: + - os: windows-latest + c_compiler: cl + cpp_compiler: cl + - os: ubuntu-latest + c_compiler: gcc-13 + cpp_compiler: g++-13 + - os: ubuntu-latest + c_compiler: clang + cpp_compiler: clang++ + exclude: + - os: windows-latest + c_compiler: gcc-13 + - os: windows-latest + c_compiler: clang + - os: ubuntu-latest + c_compiler: cl steps: - uses: actions/checkout@v3 @@ -40,32 +56,28 @@ jobs: target: desktop - name: Set reusable strings - # Turn repeated input strings (such as the build output directory) into step outputs. - # These step outputs can be used throughout the workflow file. + # Turn repeated input strings (such as the build output directory) into step outputs. These step outputs can be used throughout the workflow file. id: strings shell: bash run: | echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT" - name: Configure CMake - # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you - # are using a single-configuration generator such as make. + # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type run: > cmake -B ${{ steps.strings.outputs.build-output-dir }} - -DCMAKE_CXX_COMPILER=${{ env.cpp_compiler }} - -DCMAKE_C_COMPILER=${{ env.c_compiler }} - -DCMAKE_BUILD_TYPE=${{ env.build_type }} + -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }} + -DCMAKE_C_COMPILER=${{ matrix.c_compiler }} + -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -S ${{ github.workspace }} - name: Build - # Build your program with the given configuration. Note that --config is needed because the - # default Windows generator is a multi-config generator (Visual Studio generator). - run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config ${{ env.build_type }} + # Build your program with the given configuration. Note that --config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). + run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config ${{ matrix.build_type }} - name: Test working-directory: ${{ steps.strings.outputs.build-output-dir }} - # Execute tests defined by the CMake configuration. Note that --build-config is needed because - # the default Windows generator is a multi-config generator (Visual Studio generator). + # Execute tests defined by the CMake configuration. Note that --build-config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail - run: ctest --build-config ${{ env.build_type }} + run: ctest --build-config ${{ matrix.build_type }} diff --git a/.github/workflows/ubuntu-latest-gcc13.yml b/.github/workflows/ubuntu-latest-gcc13.yml deleted file mode 100644 index 2c98da0..0000000 --- a/.github/workflows/ubuntu-latest-gcc13.yml +++ /dev/null @@ -1,64 +0,0 @@ -# This starter workflow is for a CMake project running on multiple platforms. There is a different starter workflow if you just want a single platform. -# See: https://github.com/actions/starter-workflows/blob/main/ci/cmake-single-platform.yml -name: CMake on multiple platforms - -on: - push: - branches: [ "development" ] - pull_request: - branches: [ "development" ] - -env: - # Customize the CMake built type here (Release, Debug, RelWithDebInfo, etc) - BUILD_TYPE: Release - cpp_compiler: g++-13 - c_compiler: gcc-13 - -jobs: - build: - runs-on: ubuntu-latest - - strategy: - # Set fail-fast to false to ensure that feedback is delivered for all matrix combinations. - # Consider changing this to true when your workflow is stable. - fail-fast: false - - steps: - - uses: actions/checkout@v3 - - - name: Install Qt - uses: jurplel/install-qt-action@v3 - with: - version: 6.6.2 - target: desktop - - - name: Set reusable strings - # Turn repeated input strings (such as the build output directory) into step outputs. - # These step outputs can be used throughout the workflow file. - id: strings - shell: bash - run: | - echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT" - - - name: Configure CMake - # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you - # are using a single-configuration generator such as make. - # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type - run: > - cmake -B ${{ steps.strings.outputs.build-output-dir }} - -DCMAKE_CXX_COMPILER=${{ env.cpp_compiler }} - -DCMAKE_C_COMPILER=${{ env.c_compiler }} - -DCMAKE_BUILD_TYPE=${{ env.build_type }} - -S ${{ github.workspace }} - - - name: Build - # Build your program with the given configuration. Note that --config is needed because the - # default Windows generator is a multi-config generator (Visual Studio generator). - run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config ${{ env.build_type }} - - - name: Test - working-directory: ${{ steps.strings.outputs.build-output-dir }} - # Execute tests defined by the CMake configuration. Note that --build-config is needed - # because the default Windows generator is a multi-config generator (Visual Studio generator). - # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail - run: ctest --build-config ${{ env.build_type }} diff --git a/.gitignore b/.gitignore index 5b8a0ff..3575ea4 100644 --- a/.gitignore +++ b/.gitignore @@ -38,6 +38,7 @@ build/ docs/doxygen/* # IDE +*.swp qtrocket.pro.user .qmake.stash CMakeLists.txt.user From b72d6ff67f629b6cd55c9fba3484a119769fdf0c Mon Sep 17 00:00:00 2001 From: Travis Hunter Date: Fri, 15 Mar 2024 13:27:05 -0600 Subject: [PATCH 28/32] Add unit tests to workflow. Disable clang for now because it's broken in ubuntu-latest build container --- .github/workflows/cmake-multi-platform.yml | 3 +++ model/tests/CMakeLists.txt | 2 ++ sim/tests/CMakeLists.txt | 2 ++ 3 files changed, 7 insertions(+) diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/cmake-multi-platform.yml index 65651d1..65c5df6 100644 --- a/.github/workflows/cmake-multi-platform.yml +++ b/.github/workflows/cmake-multi-platform.yml @@ -45,6 +45,9 @@ jobs: c_compiler: clang - os: ubuntu-latest c_compiler: cl + # Clang is broken on ubuntu-latest. C++20 brings in inconsistent libraries + - os: ubuntu-latest + c_compiler: clang steps: - uses: actions/checkout@v3 diff --git a/model/tests/CMakeLists.txt b/model/tests/CMakeLists.txt index 5ff53a7..a75b55e 100644 --- a/model/tests/CMakeLists.txt +++ b/model/tests/CMakeLists.txt @@ -13,3 +13,5 @@ target_link_libraries(model_tests PRIVATE include(GoogleTest) gtest_discover_tests(model_tests) +add_test(model_tests model_tests) + diff --git a/sim/tests/CMakeLists.txt b/sim/tests/CMakeLists.txt index f928cd9..2fcec54 100644 --- a/sim/tests/CMakeLists.txt +++ b/sim/tests/CMakeLists.txt @@ -12,3 +12,5 @@ target_link_libraries(sim_tests include(GoogleTest) gtest_discover_tests(sim_tests) +add_test(sim_tests sim_tests) + From e0f3eb1c451c7c64da85586de9947242bb23753b Mon Sep 17 00:00:00 2001 From: Travis Hunter Date: Fri, 15 Mar 2024 13:29:30 -0600 Subject: [PATCH 29/32] Exclude ubuntu-latest clang build since it's broken (not my breakage, Github's config of ubuntu-latest) --- .github/workflows/cmake-multi-platform.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/cmake-multi-platform.yml index 65c5df6..3903e8d 100644 --- a/.github/workflows/cmake-multi-platform.yml +++ b/.github/workflows/cmake-multi-platform.yml @@ -35,9 +35,9 @@ jobs: - os: ubuntu-latest c_compiler: gcc-13 cpp_compiler: g++-13 - - os: ubuntu-latest - c_compiler: clang - cpp_compiler: clang++ + #- os: ubuntu-latest + #c_compiler: clang + #cpp_compiler: clang++ exclude: - os: windows-latest c_compiler: gcc-13 From 63a5b8995f0b918a36dd8188cdd8dd796c87d58b Mon Sep 17 00:00:00 2001 From: Travis Hunter Date: Fri, 15 Mar 2024 15:17:26 -0600 Subject: [PATCH 30/32] Update CTest infrastructure to automatically run qtrocket unit tests --- .github/workflows/cmake-multi-platform.yml | 2 +- CMakeLists.txt | 6 ++++-- model/tests/CMakeLists.txt | 3 +-- sim/tests/CMakeLists.txt | 3 +-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/cmake-multi-platform.yml index 3903e8d..13553a1 100644 --- a/.github/workflows/cmake-multi-platform.yml +++ b/.github/workflows/cmake-multi-platform.yml @@ -83,4 +83,4 @@ jobs: working-directory: ${{ steps.strings.outputs.build-output-dir }} # Execute tests defined by the CMake configuration. Note that --build-config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail - run: ctest --build-config ${{ matrix.build_type }} + run: ctest --build-config ${{ matrix.build_type }} -R 'qtrocket_*' diff --git a/CMakeLists.txt b/CMakeLists.txt index dec1a0f..b6cdec5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,6 +5,8 @@ project(qtrocket VERSION 0.1 LANGUAGES CXX) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) +enable_testing() + include(FetchContent) # Google Test framework @@ -48,10 +50,10 @@ endif() FetchContent_MakeAvailable(CURL) # eigen dependency -FetchContent_Declare(eigen +FetchContent_Declare(Eigen GIT_REPOSITORY https://gitlab.com/libeigen/eigen GIT_TAG 3.4.0) -FetchContent_MakeAvailable(eigen) +FetchContent_MakeAvailable(Eigen) # boost dependency diff --git a/model/tests/CMakeLists.txt b/model/tests/CMakeLists.txt index a75b55e..294019a 100644 --- a/model/tests/CMakeLists.txt +++ b/model/tests/CMakeLists.txt @@ -1,4 +1,3 @@ -enable_testing() add_executable(model_tests PartTests.cpp @@ -13,5 +12,5 @@ target_link_libraries(model_tests PRIVATE include(GoogleTest) gtest_discover_tests(model_tests) -add_test(model_tests model_tests) +add_test(NAME qtrocket_model_tests COMMAND model_tests) diff --git a/sim/tests/CMakeLists.txt b/sim/tests/CMakeLists.txt index 2fcec54..aca0eb7 100644 --- a/sim/tests/CMakeLists.txt +++ b/sim/tests/CMakeLists.txt @@ -1,4 +1,3 @@ -enable_testing() add_executable(sim_tests USStandardAtmosphereTests.cpp @@ -12,5 +11,5 @@ target_link_libraries(sim_tests include(GoogleTest) gtest_discover_tests(sim_tests) -add_test(sim_tests sim_tests) +add_test(NAME qtrocket_sim_tests COMMAND sim_tests) From 46eca1136ff9ead648a990c629c6a4e6d7c723e7 Mon Sep 17 00:00:00 2001 From: Travis Hunter Date: Sun, 17 Mar 2024 08:13:24 -0600 Subject: [PATCH 31/32] Remove model::Stage for now. It'll be good in the future, but makes initial development more complex. Prototype with single stage rocket for now while working out the physics engine --- gui/AnalysisWindow.cpp | 4 +-- model/CMakeLists.txt | 5 ++- model/InertiaTensors.h | 70 +++++++++++++++++++++++++++++++++++++++ model/Part.h | 4 ++- model/Propagatable.h | 16 +++++---- model/Rocket.cpp | 66 ++++++++++++++++++++++++++----------- model/Rocket.h | 53 +++++++++++++++++------------ model/Stage.cpp | 26 --------------- model/Stage.h | 75 ------------------------------------------ sim/Propagator.cpp | 23 +++++++------ 10 files changed, 178 insertions(+), 164 deletions(-) create mode 100644 model/InertiaTensors.h delete mode 100644 model/Stage.cpp delete mode 100644 model/Stage.h diff --git a/gui/AnalysisWindow.cpp b/gui/AnalysisWindow.cpp index 8b8fbda..65d5d6f 100644 --- a/gui/AnalysisWindow.cpp +++ b/gui/AnalysisWindow.cpp @@ -92,7 +92,7 @@ void AnalysisWindow::onButton_plotVelocity_clicked() void AnalysisWindow::onButton_plotMotorCurve_clicked() { std::shared_ptr rocket = QtRocket::getInstance()->getRocket(); - model::MotorModel& motor = rocket->getCurrentStage()->getMotorModel(); + model::MotorModel motor = rocket->getMotorModel(); ThrustCurve tc = motor.getThrustCurve(); @@ -122,4 +122,4 @@ void AnalysisWindow::onButton_plotMotorCurve_clicked() plot->yAxis->setRange(*std::min_element(std::begin(fData), std::end(fData)), *std::max_element(std::begin(fData), std::end(fData))); plot->replot(); -} \ No newline at end of file +} diff --git a/model/CMakeLists.txt b/model/CMakeLists.txt index d3a8f14..bf9be41 100644 --- a/model/CMakeLists.txt +++ b/model/CMakeLists.txt @@ -9,10 +9,9 @@ add_library(model Propagatable.h Rocket.cpp Rocket.h - Stage.cpp - Stage.h ThrustCurve.cpp - ThrustCurve.h) + ThrustCurve.h + InertiaTensors.h) target_link_libraries(model PRIVATE utils) diff --git a/model/InertiaTensors.h b/model/InertiaTensors.h new file mode 100644 index 0000000..e0d09dc --- /dev/null +++ b/model/InertiaTensors.h @@ -0,0 +1,70 @@ +#ifndef INERTIATENSORS_H +#define INERTIATENSORS_H + +#include "utils/math/MathTypes.h" + +namespace model +{ + +/** + * @brief The InertiaTensors class provides a collection of methods to + * deliver some common inertia tensors centered about the center of mass + */ +class InertiaTensors +{ +public: + +/** + * @brief SolidSphere + * @param radius (meters) + * @return + */ +static Matrix3 SolidSphere(double radius) +{ + double xx = 0.4*radius*radius; + double yy = xx; + double zz = xx; + return Matrix3{{xx, 0, 0}, + {0, yy, 0}, + {0, 0, zz}}; +} + +/** + * @brief HollowSphere + * @param radius (meters) + * @return + */ +static Matrix3 HollowSphere(double radius) +{ + double xx = (2.0/3.0)*radius*radius; + double yy = xx; + double zz = xx; + return Matrix3{{xx, 0, 0}, + {0, yy, 0}, + {0, 0, zz}}; +} + +/** + * @brief Tube - The longitudinal axis is the z-axis. Can also be used for a solid cylinder + * when innerRadius = 0.0 + * @param innerRadius (meters) + * @param outerRadius (meters) + * @param length (meters) + * @return + */ +static Matrix3 Tube(double innerRadius, double outerRadius, double length) +{ + double xx = (1.0/12.0)*(3.0*(innerRadius*innerRadius + outerRadius*outerRadius) + length*length); + double yy = xx; + double zz = (1.0/2.0)*(innerRadius*innerRadius + outerRadius*outerRadius); + return Matrix3{{xx, 0.0, 0.0}, + {0.0, yy, 0.0}, + {0.0, 0.0, zz}}; + +} + +}; + +} + +#endif // INERTIATENSORS_H diff --git a/model/Part.h b/model/Part.h index 42fe0bc..9e63cf2 100644 --- a/model/Part.h +++ b/model/Part.h @@ -63,7 +63,9 @@ public: // Set the inertia tensor void setI(const Matrix3& I) { inertiaTensor = I; } - + Matrix3 getI() { return inertiaTensor; } + Matrix3 getCompositeI() { return compositeInertiaTensor; } + void setCm(const Vector3& x) { cm = x; } // Special version of setCM that assumes the cm lies along the body x-axis void setCm(double x) { cm = {x, 0.0, 0.0}; } diff --git a/model/Propagatable.h b/model/Propagatable.h index 434665b..02e50c8 100644 --- a/model/Propagatable.h +++ b/model/Propagatable.h @@ -11,6 +11,7 @@ // qtrocket headers #include "sim/Aero.h" #include "sim/StateData.h" +#include "utils/math/MathTypes.h" namespace model { @@ -21,14 +22,15 @@ public: Propagatable() {} virtual ~Propagatable() {} - virtual double getDragCoefficient() = 0; - virtual void setDragCoefficient(double d) = 0; - - virtual bool terminateCondition(const std::pair&) = 0; + virtual Vector3 getForces(double t) = 0; + virtual Vector3 getTorques(double t) = 0; virtual double getMass(double t) = 0; - virtual double getThrust(double t) = 0; -private: + virtual Matrix3 getInertiaTensor(double t) = 0; + + virtual bool terminateCondition(double t) = 0; + +protected: sim::Aero aeroData; @@ -37,4 +39,4 @@ private: } -#endif // MODEL_PROPAGATABLE_H \ No newline at end of file +#endif // MODEL_PROPAGATABLE_H diff --git a/model/Rocket.cpp b/model/Rocket.cpp index f81cd5d..032be0b 100644 --- a/model/Rocket.cpp +++ b/model/Rocket.cpp @@ -1,51 +1,79 @@ // qtrocket headers #include "Rocket.h" +#include "QtRocket.h" +#include "InertiaTensors.h" namespace model { Rocket::Rocket() + : topPart("NoseCone", InertiaTensors::SolidSphere(1.0), 1.0, {0.0, 0.0, 1.0}) { - // A rocket needs at least one stage. Upon creation, we need to create at least one stage - currentStage.reset(new Stage("sustainer")); - stages.push_back(currentStage); - } -void Rocket::launch() + +double Rocket::getMass(double t) { - currentStage->getMotorModel().startMotor(0.0); + double mass = mm.getMass(t); + mass += topPart.getCompositeMass(t); + return mass; } -void Rocket::setMotorModel(const model::MotorModel& motor) +Matrix3 Rocket::getInertiaTensor(double) { - currentStage->setMotorModel(motor); + return topPart.getCompositeI(); } -bool Rocket::terminateCondition(const std::pair& cond) +bool Rocket::terminateCondition(double) { // Terminate propagation when the z coordinate drops below zero - if(cond.second.position[2] < 0) + if(state.position[2] < 0) return true; else return false; } -double Rocket::getThrust(double t) +Vector3 Rocket::getForces(double t) { - return currentStage->getMotorModel().getThrust(t); + // Get thrust + // Assume that thrust is always through the center of mass and in the rocket's Z-axis + Vector3 forces{0.0, 0.0, mm.getThrust(t)}; + + + // Get gravity + auto gravityModel = QtRocket::getInstance()->getEnvironment()->getGravityModel(); + + Vector3 gravity = gravityModel->getAccel(state.position)*getMass(t); + + forces += gravity; + + // Calculate aero forces + + + return forces; } -double Rocket::getMass(double t) +Vector3 Rocket::getTorques(double t) { - double totalMass = 0.0; - for(const auto& stage : stages) - { - totalMass += stage->getMass(t); - } - return totalMass; + return Vector3{0.0, 0.0, 0.0}; + +} + +double Rocket::getThrust(double t) +{ + return mm.getThrust(t); +} + +void Rocket::launch() +{ + mm.startMotor(0.0); +} + +void Rocket::setMotorModel(const model::MotorModel& motor) +{ + mm = motor; } } // namespace model diff --git a/model/Rocket.h b/model/Rocket.h index d0c3bb4..22a2fb2 100644 --- a/model/Rocket.h +++ b/model/Rocket.h @@ -13,10 +13,13 @@ /// \endcond // qtrocket headers +#include "model/Part.h" #include "sim/Propagator.h" +#include "model/MotorModel.h" -#include "model/Stage.h" #include "model/Propagatable.h" +// Not yet +//#include "model/Stage.h" namespace model { @@ -45,19 +48,30 @@ public: */ void launch(); + Vector3 getForces(double t) override; + Vector3 getTorques(double t) override; + /** + * @brief getMass returns current rocket mass + * @param t current simulation time + * @return mass in kg + */ + double getMass(double t) override; + /** + * @brief terminateCondition returns true or false, whether the passed-in time/state matches the terminate condition + * @param cond time/state pair + * @return true if the passed-in time/state satisfies the terminate condition + */ + bool terminateCondition(double t) override; + + Matrix3 getInertiaTensor(double t) override; + /** * @brief getThrust returns current motor thrust * @param t current simulation time * @return thrust in Newtons */ - double getThrust(double t) override; + double getThrust(double t); - /** - * @brief getMass returns current rocket - * @param t current simulation time - * @return mass in kg - */ - double getMass(double t) override; /** * @brief setMotorModel @@ -65,18 +79,18 @@ public: */ void setMotorModel(const model::MotorModel& motor); + + /** + * @brief getMotorModel + */ + MotorModel getMotorModel() { return mm; } + /** * @brief Returns the current motor model. * @return The current motor model */ //const model::MotorModel& getCurrentMotorModel() const { return mm; } - /** - * @brief terminateCondition returns true or false, whether the passed-in time/state matches the terminate condition - * @param cond time/state pair - * @return true if the passed-in time/state satisfies the terminate condition - */ - bool terminateCondition(const std::pair& cond) override; /** * @brief setName sets the rocket name @@ -84,20 +98,17 @@ public: */ void setName(const std::string& n) { name = n; } - double getDragCoefficient() override { return 1.0; } - void setDragCoefficient(double d) override { } + double getDragCoefficient() { return 1.0; } + void setDragCoefficient(double d) { } void setMass(double m) { } - std::shared_ptr getCurrentStage() { return currentStage; } - private: std::string name; /// Rocket name - std::vector> stages; - std::shared_ptr currentStage; - //model::MotorModel mm; /// Current Motor Model + model::MotorModel mm; /// Current Motor Model + model::Part topPart; }; diff --git a/model/Stage.cpp b/model/Stage.cpp deleted file mode 100644 index af7c593..0000000 --- a/model/Stage.cpp +++ /dev/null @@ -1,26 +0,0 @@ -/// \cond -// C headers -// C++ headers - -// 3rd party headers -/// \endcond - -// qtrocket headers -#include "Stage.h" - -namespace model -{ - -Stage::Stage(const std::string& inName) - : name(inName) -{} - -Stage::~Stage() -{} - -void Stage::setMotorModel(const model::MotorModel& motor) -{ - mm = motor; -} - -} // namespace model diff --git a/model/Stage.h b/model/Stage.h deleted file mode 100644 index bf61a40..0000000 --- a/model/Stage.h +++ /dev/null @@ -1,75 +0,0 @@ -#ifndef SIM_STAGE_H -#define SIM_STAGE_H - -/// \cond -// C headers -// C++ headers -#include -#include - -// 3rd party headers -/// \endcond - -// qtrocket headers -#include "model/MotorModel.h" -#include "model/Part.h" -#include "model/Propagatable.h" - -namespace model -{ - -class Stage : public Propagatable -{ -public: - Stage(const std::string& inName); - virtual ~Stage(); - - /** - * @brief setMotorModel - * @param motor - */ - void setMotorModel(const model::MotorModel& motor); - - /** - * @brief Returns the current motor model. - * @return The current motor model - */ - model::MotorModel& getMotorModel() { return mm; } - - virtual double getMass(double t) override - { - if(topPart) - { - return topPart->getCompositeMass(t) + mm.getMass(t); - } - else - return 0.0; - } - - virtual double getDragCoefficient() override { return 1.0 ;} - virtual void setDragCoefficient(double d) override {} - - virtual bool terminateCondition(const std::pair& cond) override - { - // Terminate propagation when the z coordinate drops below zero - if(cond.second.position[2] < 0) - return true; - else - return false; - } - - virtual double getThrust(double t) override { return mm.getThrust(t); } - - -private: - std::string name; - - std::shared_ptr topPart; - - model::MotorModel mm; - Vector3 motorModelPosition; // position of motor cg w.r.t. the stage c.g. -}; - -} // namespace model - -#endif // SIM_STAGE_H diff --git a/sim/Propagator.cpp b/sim/Propagator.cpp index 1ad0634..699d61b 100644 --- a/sim/Propagator.cpp +++ b/sim/Propagator.cpp @@ -153,7 +153,7 @@ void Propagator::runUntilTerminate() { states.push_back(std::make_pair(currentTime, nextState)); } - if(object->terminateCondition(std::make_pair(currentTime, currentState))) + if(object->terminateCondition(currentTime)) break; currentTime += timeStep; @@ -175,23 +175,26 @@ double Propagator::getMass() double Propagator::getForceX() { - QtRocket* qtrocket = QtRocket::getInstance(); - return (currentState.velocity[0] >= 0 ? -1.0 : 1.0) * qtrocket->getEnvironment()->getAtmosphericModel()->getDensity(currentState.position[2])/ 2.0 * 0.008107 * object->getDragCoefficient() * currentState.velocity[0]* currentState.velocity[0]; + return 0.0; + //QtRocket* qtrocket = QtRocket::getInstance(); + //return (currentState.velocity[0] >= 0 ? -1.0 : 1.0) * qtrocket->getEnvironment()->getAtmosphericModel()->getDensity(currentState.position[2])/ 2.0 * 0.008107 * object->getDragCoefficient() * currentState.velocity[0]* currentState.velocity[0]; } double Propagator::getForceY() { - QtRocket* qtrocket = QtRocket::getInstance(); - return (currentState.velocity[1] >= 0 ? -1.0 : 1.0) * qtrocket->getEnvironment()->getAtmosphericModel()->getDensity(currentState.position[2]) / 2.0 * 0.008107 * object->getDragCoefficient() * currentState.velocity[1]* currentState.velocity[1]; + return 0.0; + //QtRocket* qtrocket = QtRocket::getInstance(); + //return (currentState.velocity[1] >= 0 ? -1.0 : 1.0) * qtrocket->getEnvironment()->getAtmosphericModel()->getDensity(currentState.position[2]) / 2.0 * 0.008107 * object->getDragCoefficient() * currentState.velocity[1]* currentState.velocity[1]; } double Propagator::getForceZ() { - QtRocket* qtrocket = QtRocket::getInstance(); - double gravity = (qtrocket->getEnvironment()->getGravityModel()->getAccel(currentState.position[0], currentState.position[1], currentState.position[2]))[2]; - double airDrag = (currentState.velocity[2] >= 0 ? -1.0 : 1.0) * qtrocket->getEnvironment()->getAtmosphericModel()->getDensity(currentState.position[2]) / 2.0 * 0.008107 * object->getDragCoefficient() * currentState.velocity[2]* currentState.velocity[2]; - double thrust = object->getThrust(currentTime); - return gravity + airDrag + thrust; + return 0.0; + //QtRocket* qtrocket = QtRocket::getInstance(); + //double gravity = (qtrocket->getEnvironment()->getGravityModel()->getAccel(currentState.position[0], currentState.position[1], currentState.position[2]))[2]; + //double airDrag = (currentState.velocity[2] >= 0 ? -1.0 : 1.0) * qtrocket->getEnvironment()->getAtmosphericModel()->getDensity(currentState.position[2]) / 2.0 * 0.008107 * object->getDragCoefficient() * currentState.velocity[2]* currentState.velocity[2]; + //double thrust = object->getThrust(currentTime); + //return gravity + airDrag + thrust; } double Propagator::getTorqueP() { return 0.0; } From 5a332ec0606f31eefe2bf5f45644f5487e7f3eba Mon Sep 17 00:00:00 2001 From: Travis Hunter Date: Sun, 17 Mar 2024 10:19:39 -0600 Subject: [PATCH 32/32] Substantial refactor and cleanup of Propagator and Rocket class (now RocketModel class) --- QtRocket.cpp | 9 +- QtRocket.h | 17 +- gui/AnalysisWindow.cpp | 2 +- gui/MainWindow.cpp | 2 +- model/CMakeLists.txt | 4 +- model/Propagatable.h | 18 +- model/{Rocket.cpp => RocketModel.cpp} | 26 +-- model/{Rocket.h => RocketModel.h} | 12 +- sim/Propagator.cpp | 232 ++++++-------------------- sim/Propagator.h | 30 +--- 10 files changed, 101 insertions(+), 251 deletions(-) rename model/{Rocket.cpp => RocketModel.cpp} (64%) rename model/{Rocket.h => RocketModel.h} (93%) diff --git a/QtRocket.cpp b/QtRocket.cpp index 764a057..b544ed9 100644 --- a/QtRocket.cpp +++ b/QtRocket.cpp @@ -80,7 +80,7 @@ QtRocket::QtRocket() setEnvironment(std::make_shared()); rocket.first = - std::make_shared(); + std::make_shared(); rocket.second = std::make_shared(rocket.first); @@ -113,7 +113,7 @@ int QtRocket::run(int argc, char* argv[]) void QtRocket::launchRocket() { // initialize the propagator - rocket.second->clearStates(); + rocket.first->clearStates(); rocket.second->setCurrentTime(0.0); // start the rocket motor @@ -128,8 +128,3 @@ void QtRocket::addMotorModels(std::vector& m) motorDatabase->addMotorModels(m); // TODO: Now clear any duplicates? } - -void QtRocket::appendState(const StateData& state) -{ - states.emplace_back(state); -} diff --git a/QtRocket.h b/QtRocket.h index 613bfd3..a9efd7b 100644 --- a/QtRocket.h +++ b/QtRocket.h @@ -14,7 +14,7 @@ // qtrocket headers #include "model/MotorModel.h" -#include "model/Rocket.h" +#include "model/RocketModel.h" #include "sim/Environment.h" #include "sim/Propagator.h" #include "utils/Logger.h" @@ -43,13 +43,13 @@ public: std::shared_ptr getEnvironment() { return environment; } void setTimeStep(double t) { rocket.second->setTimeStep(t); } - std::shared_ptr getRocket() { return rocket.first; } + std::shared_ptr getRocket() { return rocket.first; } std::shared_ptr getMotorDatabase() { return motorDatabase; } void addMotorModels(std::vector& m); - void addRocket(std::shared_ptr r) { rocket.first = r; } + void addRocket(std::shared_ptr r) { rocket.first = r; rocket.second = std::make_shared(r); } void setEnvironment(std::shared_ptr e) { environment = e; } @@ -58,15 +58,13 @@ public: * @brief getStates returns a vector of time/state pairs generated during launch() * @return vector of pairs of doubles, where the first value is a time and the second a state vector */ - const std::vector>& getStates() const { return rocket.second->getStates(); } + const std::vector>& getStates() const { return rocket.first->getStates(); } /** * @brief setInitialState sets the initial state of the Rocket. * @param initState initial state vector (x, y, z, xDot, yDot, zDot, pitch, yaw, roll, pitchDot, yawDot, rollDot) */ - void setInitialState(const StateData& initState) { rocket.second->setInitialState(initState); } - - void appendState(const StateData& state); + void setInitialState(const StateData& initState) { rocket.first->setInitialState(initState); } private: QtRocket(); @@ -80,7 +78,8 @@ private: utils::Logger* logger; - std::pair, std::shared_ptr> rocket; + using Rocket = std::pair, std::shared_ptr>; + Rocket rocket; std::shared_ptr environment; std::shared_ptr motorDatabase; @@ -92,8 +91,6 @@ private: // Table of state data std::vector states; - - }; #endif // QTROCKET_H diff --git a/gui/AnalysisWindow.cpp b/gui/AnalysisWindow.cpp index 65d5d6f..7d0e783 100644 --- a/gui/AnalysisWindow.cpp +++ b/gui/AnalysisWindow.cpp @@ -91,7 +91,7 @@ void AnalysisWindow::onButton_plotVelocity_clicked() void AnalysisWindow::onButton_plotMotorCurve_clicked() { - std::shared_ptr rocket = QtRocket::getInstance()->getRocket(); + std::shared_ptr rocket = QtRocket::getInstance()->getRocket(); model::MotorModel motor = rocket->getMotorModel(); ThrustCurve tc = motor.getThrustCurve(); diff --git a/gui/MainWindow.cpp b/gui/MainWindow.cpp index c2ab9c0..12ec10a 100644 --- a/gui/MainWindow.cpp +++ b/gui/MainWindow.cpp @@ -20,7 +20,7 @@ #include "gui/MainWindow.h" #include "gui/ThrustCurveMotorSelector.h" #include "gui/SimOptionsWindow.h" -#include "model/Rocket.h" +#include "model/RocketModel.h" #include "utils/RSEDatabaseLoader.h" diff --git a/model/CMakeLists.txt b/model/CMakeLists.txt index bf9be41..afa0fd1 100644 --- a/model/CMakeLists.txt +++ b/model/CMakeLists.txt @@ -7,8 +7,8 @@ add_library(model Part.h Propagatable.cpp Propagatable.h - Rocket.cpp - Rocket.h + RocketModel.cpp + RocketModel.h ThrustCurve.cpp ThrustCurve.h InertiaTensors.h) diff --git a/model/Propagatable.h b/model/Propagatable.h index 02e50c8..d8a8a6e 100644 --- a/model/Propagatable.h +++ b/model/Propagatable.h @@ -30,11 +30,27 @@ public: virtual bool terminateCondition(double t) = 0; + void setCurrentState(const StateData& st) { currentState = st; } + const StateData& getCurrentState() { return currentState; } + + const StateData& getInitialState() { return initialState; } + void setInitialState(const StateData& init) { initialState = init; } + + void appendState(double t, const StateData& st) { states.emplace_back(t, st); } + + const std::vector>& getStates() { return states; } + + void clearStates() { states.clear(); } + protected: sim::Aero aeroData; - StateData state; + StateData initialState; + StateData currentState; + StateData nextState; + + std::vector> states; }; } diff --git a/model/Rocket.cpp b/model/RocketModel.cpp similarity index 64% rename from model/Rocket.cpp rename to model/RocketModel.cpp index 032be0b..145f8a3 100644 --- a/model/Rocket.cpp +++ b/model/RocketModel.cpp @@ -1,41 +1,41 @@ // qtrocket headers -#include "Rocket.h" +#include "RocketModel.h" #include "QtRocket.h" #include "InertiaTensors.h" namespace model -{ +{ -Rocket::Rocket() +RocketModel::RocketModel() : topPart("NoseCone", InertiaTensors::SolidSphere(1.0), 1.0, {0.0, 0.0, 1.0}) { } -double Rocket::getMass(double t) +double RocketModel::getMass(double t) { double mass = mm.getMass(t); mass += topPart.getCompositeMass(t); return mass; } -Matrix3 Rocket::getInertiaTensor(double) +Matrix3 RocketModel::getInertiaTensor(double) { return topPart.getCompositeI(); } -bool Rocket::terminateCondition(double) +bool RocketModel::terminateCondition(double) { // Terminate propagation when the z coordinate drops below zero - if(state.position[2] < 0) + if(currentState.position[2] < 0) return true; else return false; } -Vector3 Rocket::getForces(double t) +Vector3 RocketModel::getForces(double t) { // Get thrust // Assume that thrust is always through the center of mass and in the rocket's Z-axis @@ -45,7 +45,7 @@ Vector3 Rocket::getForces(double t) // Get gravity auto gravityModel = QtRocket::getInstance()->getEnvironment()->getGravityModel(); - Vector3 gravity = gravityModel->getAccel(state.position)*getMass(t); + Vector3 gravity = gravityModel->getAccel(currentState.position)*getMass(t); forces += gravity; @@ -55,23 +55,23 @@ Vector3 Rocket::getForces(double t) return forces; } -Vector3 Rocket::getTorques(double t) +Vector3 RocketModel::getTorques(double t) { return Vector3{0.0, 0.0, 0.0}; } -double Rocket::getThrust(double t) +double RocketModel::getThrust(double t) { return mm.getThrust(t); } -void Rocket::launch() +void RocketModel::launch() { mm.startMotor(0.0); } -void Rocket::setMotorModel(const model::MotorModel& motor) +void RocketModel::setMotorModel(const model::MotorModel& motor) { mm = motor; } diff --git a/model/Rocket.h b/model/RocketModel.h similarity index 93% rename from model/Rocket.h rename to model/RocketModel.h index 22a2fb2..c6be016 100644 --- a/model/Rocket.h +++ b/model/RocketModel.h @@ -1,5 +1,5 @@ -#ifndef ROCKET_H -#define ROCKET_H +#ifndef ROCKETMODEL_H +#define ROCKETMODEL_H /// \cond // C headers @@ -28,19 +28,19 @@ namespace model * @brief The Rocket class holds all rocket components * */ -class Rocket : public Propagatable +class RocketModel : public Propagatable { public: /** * @brief Rocket class constructor */ - Rocket(); + RocketModel(); /** * @brief Rocket class destructor * */ - virtual ~Rocket() {} + virtual ~RocketModel() {} /** * @brief launch Propagates the Rocket object until termination, @@ -113,4 +113,4 @@ private: }; } // namespace model -#endif // ROCKET_H +#endif // ROCKETMODEL_H diff --git a/sim/Propagator.cpp b/sim/Propagator.cpp index 699d61b..2337a9e 100644 --- a/sim/Propagator.cpp +++ b/sim/Propagator.cpp @@ -13,121 +13,36 @@ // qtrocket headers #include "Propagator.h" -#include "QtRocket.h" -#include "model/Rocket.h" #include "sim/RK4Solver.h" #include "utils/Logger.h" - -namespace sim { - -Propagator::Propagator(std::shared_ptr r) - : linearIntegrator(), - //orientationIntegrator(), - object(r), - currentState(), - nextState(), - currentGravity(), - currentWindSpeed(), - saveStates(true), - currentTime(0.0), - timeStep(0.01), - states() +namespace sim { +Propagator::Propagator(std::shared_ptr r) + : linearIntegrator(), + object(r), + saveStates(true), + timeStep(0.01) +{ + // Linear velocity and acceleration + std::function(Vector3&, Vector3&)> linearODEs = [this](Vector3& state, Vector3& rate) -> std::pair + { + Vector3 dPosition; + Vector3 dVelocity; + // dx/dt + dPosition = rate; - // This is a little strange, but I have to populate the integrator unique_ptr - // with reset. make_unique() doesn't work because the compiler can't seem to - // deduce the template parameters correctly, and I don't want to specify them - // manually either. RK4Solver constructor is perfectly capable of deducing it's - // template types, and it derives from DESolver, so we can just reset the unique_ptr - // and pass it a freshly allocated RK4Solver pointer + // dvx/dt + dVelocity = object->getForces(currentTime) / object->getMass(currentTime); - // The state vector has components of the form: + return std::make_pair(dPosition, dVelocity); + }; - // Linear velocity and acceleration - std::function(Vector3&, Vector3&)> linearODE = [this](Vector3& state, Vector3& rate) -> std::pair - { - Vector3 dPosition; - Vector3 dVelocity; - // dx/dt - dPosition[0] = rate[0]; + linearIntegrator.reset(new RK4Solver(linearODEs)); + linearIntegrator->setTimeStep(timeStep); - // dy/dt - dPosition[1] = rate[1]; - - // dz/dt - dPosition[2] = rate[2]; - - // dvx/dt - dVelocity[0] = getForceX() / getMass(); - - // dvy/dt - dVelocity[1] = getForceY() / getMass(); - - // dvz/dt - dVelocity[2] = getForceZ() / getMass(); - - return std::make_pair(dPosition, dVelocity); - - }; - - linearIntegrator.reset(new RK4Solver(linearODE)); - linearIntegrator->setTimeStep(timeStep); - - // This is pure quaternion - // This formula is taken from https://www.vectornav.com/resources/inertial-navigation-primer/math-fundamentals/math-attitudetran - // - // Convention for the quaternions is (vector, scalar). So q4 is scalar term - // - // q1Dot = 1/2 * [(q4*omega1) - (q3*omega2) + (q2*omega3)] - // q2Dot = 1/2 * [(q3*omega1) + (q4*omega2) - (q1*omega3)] - // q3Dot = 1/2 * [(-q2*omega1) + (q1*omega2) + (q4*omega3)] - // q4Dot = -1/2 *[(q1*omega1) + (q2*omega2) + (q3*omega3)] - // - // omega1 = yawRate - // omega2 = pitchRate - // omega3 = rollRate - // -// orientationIntegrator.reset(new RK4Solver( -// /* dyawRate/dt */ [this](const std::vector& s, double) -> double -// { return getTorqueP() / getIyaw(); }, -// /* dpitchRate/dt */ [this](const std::vector& s, double) -> double -// { return getTorqueQ() / getIpitch(); }, -// /* drollRate/dt */ [this](const std::vector& s, double) -> double -// { return getTorqueR() / getIroll(); }, -// /* q1Dot */ [](const std::vector& s, double) -> double -// { -// double retVal = s[6]*s[0] - s[5]*s[1] + s[4]*s[2]; -// return 0.5 * retVal; -// }, -// /* q2Dot */ [](const std::vector& s, double) -> double -// { -// double retVal = s[5]*s[0] + s[6]*s[1] - s[3]*s[2]; -// return 0.5 * retVal; -// }, -// /* q3Dot */ [](const std::vector& s, double) -> double -// { -// double retVal = -s[4]*s[0] + s[3]*s[1] + s[6]*s[2]; -// return 0.5 * retVal; -// }, -// /* q4Dot */ [](const std::vector& s, double) -> double -// { -// double retVal = s[3]*s[0] + s[4]*s[1] + s[5]*s[2]; -// return -0.5 * retVal; -// })); -// orientationIntegrator->setTimeStep(timeStep); - -// std::function(Quaternion&, Quaternion&)> orientationODE = -// [this](Quaternion& qOri, Quaternion& qRate) -> std::pair -// { -// Quaternion dOri; -// Quaternion dOriRate; - -// Matrix4 -// } - - saveStates = true; + saveStates = true; } Propagator::~Propagator() @@ -136,89 +51,40 @@ Propagator::~Propagator() void Propagator::runUntilTerminate() { - std::chrono::steady_clock::time_point startTime = std::chrono::steady_clock::now(); - std::chrono::steady_clock::time_point endTime; + std::chrono::steady_clock::time_point startTime = std::chrono::steady_clock::now(); + std::chrono::steady_clock::time_point endTime; - currentState.position = {0.0, 0.0, 0.0}; - while(true) - { + Vector3 currentPosition; + Vector3 currentVelocity; + Vector3 nextPosition; + Vector3 nextVelocity; + while(true) + { + currentPosition = object->getCurrentState().position; + currentVelocity = object->getCurrentState().velocity; - // tempRes gets overwritten - std::tie(nextState.position, nextState.velocity) = linearIntegrator->step(currentState.position, currentState.velocity); + std::tie(nextPosition, nextVelocity) = linearIntegrator->step(currentPosition, currentVelocity); - //tempRes = linearIntegrator->step(currentBodyState); + StateData nextState; + nextState.position = nextPosition; + nextState.velocity = nextVelocity; + object->setCurrentState(nextState); - - if(saveStates) - { - states.push_back(std::make_pair(currentTime, nextState)); - } - if(object->terminateCondition(currentTime)) - break; + if(saveStates) + { + object->appendState(currentTime, nextState); + } + if(object->terminateCondition(currentTime)) + break; - currentTime += timeStep; - currentState = nextState; - } - endTime = std::chrono::steady_clock::now(); - - std::stringstream duration; - duration << "runUntilTerminate time (microseconds): "; - duration << std::chrono::duration_cast(endTime - startTime).count(); - utils::Logger::getInstance()->debug(duration.str()); - -} - -double Propagator::getMass() -{ - return object->getMass(currentTime); -} - -double Propagator::getForceX() -{ - return 0.0; - //QtRocket* qtrocket = QtRocket::getInstance(); - //return (currentState.velocity[0] >= 0 ? -1.0 : 1.0) * qtrocket->getEnvironment()->getAtmosphericModel()->getDensity(currentState.position[2])/ 2.0 * 0.008107 * object->getDragCoefficient() * currentState.velocity[0]* currentState.velocity[0]; -} - -double Propagator::getForceY() -{ - return 0.0; - //QtRocket* qtrocket = QtRocket::getInstance(); - //return (currentState.velocity[1] >= 0 ? -1.0 : 1.0) * qtrocket->getEnvironment()->getAtmosphericModel()->getDensity(currentState.position[2]) / 2.0 * 0.008107 * object->getDragCoefficient() * currentState.velocity[1]* currentState.velocity[1]; -} - -double Propagator::getForceZ() -{ - return 0.0; - //QtRocket* qtrocket = QtRocket::getInstance(); - //double gravity = (qtrocket->getEnvironment()->getGravityModel()->getAccel(currentState.position[0], currentState.position[1], currentState.position[2]))[2]; - //double airDrag = (currentState.velocity[2] >= 0 ? -1.0 : 1.0) * qtrocket->getEnvironment()->getAtmosphericModel()->getDensity(currentState.position[2]) / 2.0 * 0.008107 * object->getDragCoefficient() * currentState.velocity[2]* currentState.velocity[2]; - //double thrust = object->getThrust(currentTime); - //return gravity + airDrag + thrust; -} - -double Propagator::getTorqueP() { return 0.0; } -double Propagator::getTorqueQ() { return 0.0; } -double Propagator::getTorqueR() { return 0.0; } - -Vector3 Propagator::getCurrentGravity() -{ - auto gravityModel = QtRocket::getInstance()->getEnvironment()->getGravityModel(); - auto gravityAccel = gravityModel->getAccel(currentState.position[0], - currentState.position[1], - currentState.position[2]); - Vector3 gravityVector{gravityAccel[0], - gravityAccel[1], - gravityAccel[2]}; - - - Quaternion q = currentState.orientation; - - Vector3 res = q * gravityVector; - - - return Vector3{0.0, 0.0, 0.0}; + currentTime += timeStep; + } + endTime = std::chrono::steady_clock::now(); + std::stringstream duration; + duration << "runUntilTerminate time (microseconds): "; + duration << std::chrono::duration_cast(endTime - startTime).count(); + utils::Logger::getInstance()->debug(duration.str()); } diff --git a/sim/Propagator.h b/sim/Propagator.h index 37568c6..b9bcdcf 100644 --- a/sim/Propagator.h +++ b/sim/Propagator.h @@ -5,7 +5,6 @@ // C headers // C++ headers #include -#include // 3rd party headers /// \endcond @@ -30,17 +29,17 @@ namespace sim class Propagator { public: - Propagator(std::shared_ptr r); + Propagator(std::shared_ptr o); ~Propagator(); void setInitialState(const StateData& initialState) { - currentState = initialState; + object->setInitialState(initialState); } const StateData& getCurrentState() const { - return currentState; + return object->getCurrentState(); } void runUntilTerminate(); @@ -50,44 +49,21 @@ public: saveStates = s; } - const std::vector>& getStates() const { return states; } - - void clearStates() { states.clear(); } void setCurrentTime(double t) { currentTime = t; } void setTimeStep(double ts) { timeStep = ts; } void setSaveStats(bool s) { saveStates = s; } private: - double getMass(); - double getForceX(); - double getForceY(); - double getForceZ(); - - double getTorqueP(); // yaw - double getTorqueQ(); // pitch - double getTorqueR(); // roll - - double getIyaw() { return 1.0; } - double getIpitch() { return 1.0; } - double getIroll() { return 1.0; } std::unique_ptr> linearIntegrator; // std::unique_ptr> orientationIntegrator; std::shared_ptr object; - StateData currentState; - StateData nextState; - - Vector3 currentGravity{0.0, 0.0, 0.0}; - Vector3 currentWindSpeed{0.0, 0.0, 0.0}; - bool saveStates{true}; double currentTime{0.0}; double timeStep{0.01}; - std::vector> states; - Vector3 getCurrentGravity(); }; } // namespace sim