diff --git a/.gitignore b/.gitignore index 72e12ea..7b027ba 100644 --- a/.gitignore +++ b/.gitignore @@ -38,7 +38,7 @@ build/ docs/doxygen/* # IDE -.vscode/qtrocket.pro.user +.vscode/ qtrocket.pro.user .qmake.stash diff --git a/CMakeLists.txt b/CMakeLists.txt index d01e33c..ccc9f99 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -99,6 +99,7 @@ set(PROJECT_SOURCES if(${QT_VERSION_MAJOR} GREATER_EQUAL 6) qt_add_executable(qtrocket + qtrocket.qrc MANUAL_FINALIZATION ${PROJECT_SOURCES} ) diff --git a/CMakeLists.txt.user b/CMakeLists.txt.user index 81f852a..5fc28a1 100644 --- a/CMakeLists.txt.user +++ b/CMakeLists.txt.user @@ -1,6 +1,6 @@ - + EnvironmentId diff --git a/QtRocket.h b/QtRocket.h index 61816c9..5d07f75 100644 --- a/QtRocket.h +++ b/QtRocket.h @@ -45,6 +45,8 @@ public: void addMotorModels(std::vector& m); + const std::vector& getMotorModels() const { return motorModels; } + void addRocket(std::shared_ptr r) { rocket = r; } void setSimulationOptions(std::shared_ptr options) { simOptions = options; } diff --git a/gui/AnalysisWindow.cpp b/gui/AnalysisWindow.cpp index 9f7afe5..2976858 100644 --- a/gui/AnalysisWindow.cpp +++ b/gui/AnalysisWindow.cpp @@ -2,6 +2,8 @@ #include "ui_AnalysisWindow.h" #include "QtRocket.h" +#include "model/MotorModel.h" +#include "model/ThrustCurve.h" AnalysisWindow::AnalysisWindow(QWidget *parent) : QDialog(parent), @@ -12,10 +14,25 @@ AnalysisWindow::AnalysisWindow(QWidget *parent) : this->hide(); this->show(); + connect(ui->plotAltitudeBtn, SIGNAL(clicked()), this, SLOT(plotAltitude())); + //connect(ui->plotAtmosphereBtn, SIGNAL(clicked()), this, SLOT(plotAtmosphere())); + connect(ui->plotVelocityBtn, SIGNAL(clicked()), this, SLOT(plotVelocity())); + connect(ui->plotMotorCurveBtn, SIGNAL(clicked()), this, SLOT(plotMotorCurveBtn())); + +} + +AnalysisWindow::~AnalysisWindow() +{ + delete ui; +} + +void AnalysisWindow::plotAltitude() +{ std::shared_ptr rocket = QtRocket::getInstance()->getRocket(); const std::vector>>& res = rocket->getStates(); auto& plot = ui->plotWidget; + plot->clearGraphs(); plot->setInteraction(QCP::iRangeDrag, true); plot->setInteraction(QCP::iRangeZoom, true); // generate some data: @@ -37,7 +54,66 @@ AnalysisWindow::AnalysisWindow(QWidget *parent) : plot->replot(); } -AnalysisWindow::~AnalysisWindow() +void AnalysisWindow::plotVelocity() { - delete ui; + std::shared_ptr rocket = QtRocket::getInstance()->getRocket(); + const std::vector>>& res = rocket->getStates(); + auto& plot = ui->plotWidget; + plot->clearGraphs(); + plot->setInteraction(QCP::iRangeDrag, true); + plot->setInteraction(QCP::iRangeZoom, true); + + // generate some data: + QVector tData(res.size()), zData(res.size()); + for (int i = 0; i < tData.size(); ++i) + { + tData[i] = res[i].first; + zData[i] = res[i].second[5]; + } + // create graph and assign data to it: + plot->addGraph(); + plot->graph(0)->setData(tData, zData); + // give the axes some labels: + plot->xAxis->setLabel("time"); + plot->yAxis->setLabel("Z Velocity"); + // set axes ranges, so we see all data: + plot->xAxis->setRange(*std::min_element(std::begin(tData), std::end(tData)), *std::max_element(std::begin(tData), std::end(tData))); + plot->yAxis->setRange(*std::min_element(std::begin(zData), std::end(zData)), *std::max_element(std::begin(zData), std::end(zData))); + plot->replot(); + } + +void AnalysisWindow::plotMotorCurveBtn() +{ + std::shared_ptr rocket = QtRocket::getInstance()->getRocket(); + model::MotorModel motor = rocket->getCurrentMotorModel(); + ThrustCurve tc = motor.getThrustCurve(); + + + const std::vector>& res = tc.getThrustCurveData(); + auto& plot = ui->plotWidget; + plot->clearGraphs(); + plot->setInteraction(QCP::iRangeDrag, true); + plot->setInteraction(QCP::iRangeZoom, true); + + // generate some data: + QVector tData(res.size()); + QVector fData(res.size()); + for (int i = 0; i < tData.size(); ++i) + { + tData[i] = res[i].first; + fData[i] = res[i].second; + } + // create graph and assign data to it: + plot->addGraph(); + plot->graph(0)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, 5)); + plot->graph(0)->setData(tData, fData); + // give the axes some labels: + plot->xAxis->setLabel("time"); + plot->yAxis->setLabel("Thrust (N)"); + // set axes ranges, so we see all data: + plot->xAxis->setRange(*std::min_element(std::begin(tData), std::end(tData)), *std::max_element(std::begin(tData), std::end(tData))); + 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/gui/AnalysisWindow.h b/gui/AnalysisWindow.h index 7ff9411..7c0747c 100644 --- a/gui/AnalysisWindow.h +++ b/gui/AnalysisWindow.h @@ -36,6 +36,13 @@ public: explicit AnalysisWindow(QWidget *parent = nullptr); ~AnalysisWindow(); +private slots: + + void plotAltitude(); + //void plotAtmosphere(); + void plotVelocity(); + void plotMotorCurveBtn(); + private: Ui::AnalysisWindow *ui; }; diff --git a/gui/AnalysisWindow.ui b/gui/AnalysisWindow.ui index f9af7ec..673bde8 100644 --- a/gui/AnalysisWindow.ui +++ b/gui/AnalysisWindow.ui @@ -16,13 +16,68 @@ - 19 + 149 29 - 961 - 671 + 831 + 541 + + + + 20 + 50 + 121 + 36 + + + + Plot Altitude + + + + + + 20 + 110 + 121 + 36 + + + + Plot Velocity + + + + + false + + + + 20 + 170 + 121 + 36 + + + + Plot Atmosphere + + + + + + 20 + 230 + 121 + 36 + + + + Motor Curve + + @@ -33,5 +88,25 @@ - + + + plotAltitudeBtn + clicked() + AnalysisWindow + plotAltitude() + + + 78 + 61 + + + 88 + 17 + + + + + + plotAltitude() + diff --git a/gui/MainWindow.cpp b/gui/MainWindow.cpp index 69015e1..c64a5e1 100644 --- a/gui/MainWindow.cpp +++ b/gui/MainWindow.cpp @@ -151,7 +151,7 @@ void MainWindow::on_setMotor_clicked() QString motorName = ui->engineSelectorComboBox->currentText(); model::MotorModel mm = rseDatabase->getMotorModelByName(motorName.toStdString()); QtRocket::getInstance()->getRocket()->setMotorModel(mm); - + QtRocket::getInstance()->addMotorModels(rseDatabase->getMotors()); } diff --git a/gui/MainWindow.ui b/gui/MainWindow.ui index b7562a9..4e8bd15 100644 --- a/gui/MainWindow.ui +++ b/gui/MainWindow.ui @@ -62,6 +62,9 @@ + + false + 30 @@ -83,8 +86,8 @@ - 30 - 70 + 240 + 440 191 25 @@ -96,7 +99,7 @@ - Calculate Ballistic Trajectory + Calculate Trajectory @@ -105,12 +108,15 @@ 260 80 161 - 151 + 196 + + false + 90.0 @@ -143,7 +149,7 @@ - 50.0 + 5.0 @@ -164,7 +170,7 @@ - Velocity + Initial Velocity @@ -188,7 +194,7 @@ 90 - 250 + 290 421 80 @@ -224,7 +230,7 @@ 240 - 360 + 390 201 25 @@ -244,7 +250,7 @@ 0 0 1031 - 22 + 32 diff --git a/gui/ThrustCurveMotorSelector.cpp b/gui/ThrustCurveMotorSelector.cpp index d19228b..71740f8 100644 --- a/gui/ThrustCurveMotorSelector.cpp +++ b/gui/ThrustCurveMotorSelector.cpp @@ -90,6 +90,34 @@ void ThrustCurveMotorSelector::on_setMotor_clicked() return item.data.commonName == commonName; }); + ThrustCurve tc = tcApi->getMotorData(mm.data.motorIdTC).getThrustCurve(); + mm.addThrustCurve(tc); QtRocket::getInstance()->getRocket()->setMotorModel(mm); + + const std::vector>& res = tc.getThrustCurveData(); + auto& plot = ui->plot; + plot->clearGraphs(); + plot->setInteraction(QCP::iRangeDrag, true); + plot->setInteraction(QCP::iRangeZoom, true); + + // generate some data: + QVector tData(res.size()); + QVector fData(res.size()); + for (int i = 0; i < tData.size(); ++i) + { + tData[i] = res[i].first; + fData[i] = res[i].second; + } + // create graph and assign data to it: + plot->addGraph(); + plot->graph(0)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, 5)); + plot->graph(0)->setData(tData, fData); + // give the axes some labels: + plot->xAxis->setLabel("time"); + plot->yAxis->setLabel("Thrust (N)"); + // set axes ranges, so we see all data: + plot->xAxis->setRange(*std::min_element(std::begin(tData), std::end(tData)), *std::max_element(std::begin(tData), std::end(tData))); + plot->yAxis->setRange(*std::min_element(std::begin(fData), std::end(fData)), *std::max_element(std::begin(fData), std::end(fData))); + plot->replot(); } diff --git a/model/MotorModel.h b/model/MotorModel.h index 39729d9..4f719d3 100644 --- a/model/MotorModel.h +++ b/model/MotorModel.h @@ -398,6 +398,8 @@ public: void addThrustCurve(const ThrustCurve& tc) { thrust = tc; } + const ThrustCurve& getThrustCurve() const { return thrust; } + // Thrust parameters MetaData data; private: diff --git a/model/Rocket.h b/model/Rocket.h index 67ea9a2..da1459a 100644 --- a/model/Rocket.h +++ b/model/Rocket.h @@ -81,19 +81,18 @@ public: */ double getThrust(double t); - /** - * @brief setThrustCurve sets the current thrust curve - * @param curve - * @todo Remove this - */ - void setThrustCurve(const ThrustCurve& curve); - /** * @brief setMotorModel * @param motor */ void setMotorModel(const model::MotorModel& motor); + /** + * @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 diff --git a/model/ThrustCurve.h b/model/ThrustCurve.h index c50c064..06ea047 100644 --- a/model/ThrustCurve.h +++ b/model/ThrustCurve.h @@ -48,6 +48,8 @@ public: */ void setThrustCurveVector(const std::vector>& v); + const std::vector> getThrustCurveData() const { return thrustCurve; } + private: std::vector> thrustCurve; double maxTime{0.0}; diff --git a/utils/ThrustCurveAPI.cpp b/utils/ThrustCurveAPI.cpp index 5039c6e..de3ff83 100644 --- a/utils/ThrustCurveAPI.cpp +++ b/utils/ThrustCurveAPI.cpp @@ -14,7 +14,7 @@ namespace utils { ThrustCurveAPI::ThrustCurveAPI() - : hostname("https://www.thrustcurve.org/api/v1/"), + : hostname("https://www.thrustcurve.org/"), curlConnection() { @@ -29,13 +29,53 @@ ThrustCurveAPI::~ThrustCurveAPI() model::MotorModel ThrustCurveAPI::getMotorData(const std::string& motorId) { std::stringstream endpoint; - endpoint << hostname << "download.json?motorId=" << motorId << "&data=samples"; + endpoint << hostname << "api/v1/download.json?motorId=" << motorId << "&data=samples"; std::vector extraHeaders = {}; std::string res = curlConnection.get(endpoint.str(), extraHeaders); - - /// TODO: fix this 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())); + + } + } + ThrustCurve tc(samples); + mm.addThrustCurve(tc); + } + 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 mm; } @@ -43,7 +83,7 @@ ThrustcurveMetadata ThrustCurveAPI::getMetadata() { std::string endpoint = hostname; - endpoint += "metadata.json"; + endpoint += "api/v1/metadata.json"; std::string result = curlConnection.get(endpoint, extraHeaders); ThrustcurveMetadata ret; @@ -123,7 +163,7 @@ std::vector ThrustCurveAPI::searchMotors(const SearchCriteria { std::vector retVal; std::string endpoint = hostname; - endpoint += "search.json?"; + endpoint += "api/v1/search.json?"; for(const auto& i : c.criteria) { endpoint += i.first;