Now with ThrustCurve.org integration

This commit is contained in:
Travis Hunter 2023-04-27 12:03:09 -06:00
parent eeea751fbc
commit fb6f221137
14 changed files with 269 additions and 31 deletions

2
.gitignore vendored
View File

@ -38,7 +38,7 @@ build/
docs/doxygen/* docs/doxygen/*
# IDE # IDE
.vscode/qtrocket.pro.user .vscode/
qtrocket.pro.user qtrocket.pro.user
.qmake.stash .qmake.stash

View File

@ -99,6 +99,7 @@ set(PROJECT_SOURCES
if(${QT_VERSION_MAJOR} GREATER_EQUAL 6) if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
qt_add_executable(qtrocket qt_add_executable(qtrocket
qtrocket.qrc
MANUAL_FINALIZATION MANUAL_FINALIZATION
${PROJECT_SOURCES} ${PROJECT_SOURCES}
) )

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject> <!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 10.0.0, 2023-04-26T15:16:57. --> <!-- Written by QtCreator 10.0.0, 2023-04-26T18:22:10. -->
<qtcreator> <qtcreator>
<data> <data>
<variable>EnvironmentId</variable> <variable>EnvironmentId</variable>

View File

@ -45,6 +45,8 @@ public:
void addMotorModels(std::vector<model::MotorModel>& m); void addMotorModels(std::vector<model::MotorModel>& m);
const std::vector<model::MotorModel>& getMotorModels() const { return motorModels; }
void addRocket(std::shared_ptr<Rocket> r) { rocket = r; } void addRocket(std::shared_ptr<Rocket> r) { rocket = r; }
void setSimulationOptions(std::shared_ptr<sim::SimulationOptions> options) { simOptions = options; } void setSimulationOptions(std::shared_ptr<sim::SimulationOptions> options) { simOptions = options; }

View File

@ -2,6 +2,8 @@
#include "ui_AnalysisWindow.h" #include "ui_AnalysisWindow.h"
#include "QtRocket.h" #include "QtRocket.h"
#include "model/MotorModel.h"
#include "model/ThrustCurve.h"
AnalysisWindow::AnalysisWindow(QWidget *parent) : AnalysisWindow::AnalysisWindow(QWidget *parent) :
QDialog(parent), QDialog(parent),
@ -12,10 +14,25 @@ AnalysisWindow::AnalysisWindow(QWidget *parent) :
this->hide(); this->hide();
this->show(); 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> rocket = QtRocket::getInstance()->getRocket(); std::shared_ptr<Rocket> rocket = QtRocket::getInstance()->getRocket();
const std::vector<std::pair<double, std::vector<double>>>& res = rocket->getStates(); const std::vector<std::pair<double, std::vector<double>>>& res = rocket->getStates();
auto& plot = ui->plotWidget; auto& plot = ui->plotWidget;
plot->clearGraphs();
plot->setInteraction(QCP::iRangeDrag, true); plot->setInteraction(QCP::iRangeDrag, true);
plot->setInteraction(QCP::iRangeZoom, true); plot->setInteraction(QCP::iRangeZoom, true);
// generate some data: // generate some data:
@ -37,7 +54,66 @@ AnalysisWindow::AnalysisWindow(QWidget *parent) :
plot->replot(); plot->replot();
} }
AnalysisWindow::~AnalysisWindow() void AnalysisWindow::plotVelocity()
{ {
delete ui; std::shared_ptr<Rocket> rocket = QtRocket::getInstance()->getRocket();
const std::vector<std::pair<double, std::vector<double>>>& res = rocket->getStates();
auto& plot = ui->plotWidget;
plot->clearGraphs();
plot->setInteraction(QCP::iRangeDrag, true);
plot->setInteraction(QCP::iRangeZoom, true);
// generate some data:
QVector<double> 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> rocket = QtRocket::getInstance()->getRocket();
model::MotorModel motor = rocket->getCurrentMotorModel();
ThrustCurve tc = motor.getThrustCurve();
const std::vector<std::pair<double, double>>& res = tc.getThrustCurveData();
auto& plot = ui->plotWidget;
plot->clearGraphs();
plot->setInteraction(QCP::iRangeDrag, true);
plot->setInteraction(QCP::iRangeZoom, true);
// generate some data:
QVector<double> tData(res.size());
QVector<double> 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();
} }

View File

@ -36,6 +36,13 @@ public:
explicit AnalysisWindow(QWidget *parent = nullptr); explicit AnalysisWindow(QWidget *parent = nullptr);
~AnalysisWindow(); ~AnalysisWindow();
private slots:
void plotAltitude();
//void plotAtmosphere();
void plotVelocity();
void plotMotorCurveBtn();
private: private:
Ui::AnalysisWindow *ui; Ui::AnalysisWindow *ui;
}; };

View File

@ -16,13 +16,68 @@
<widget class="QCustomPlot" name="plotWidget" native="true"> <widget class="QCustomPlot" name="plotWidget" native="true">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>19</x> <x>149</x>
<y>29</y> <y>29</y>
<width>961</width> <width>831</width>
<height>671</height> <height>541</height>
</rect> </rect>
</property> </property>
</widget> </widget>
<widget class="QPushButton" name="plotAltitudeBtn">
<property name="geometry">
<rect>
<x>20</x>
<y>50</y>
<width>121</width>
<height>36</height>
</rect>
</property>
<property name="text">
<string>Plot Altitude</string>
</property>
</widget>
<widget class="QPushButton" name="plotVelocityBtn">
<property name="geometry">
<rect>
<x>20</x>
<y>110</y>
<width>121</width>
<height>36</height>
</rect>
</property>
<property name="text">
<string>Plot Velocity</string>
</property>
</widget>
<widget class="QPushButton" name="plotAtmosphereBtn">
<property name="enabled">
<bool>false</bool>
</property>
<property name="geometry">
<rect>
<x>20</x>
<y>170</y>
<width>121</width>
<height>36</height>
</rect>
</property>
<property name="text">
<string>Plot Atmosphere</string>
</property>
</widget>
<widget class="QPushButton" name="plotMotorCurveBtn">
<property name="geometry">
<rect>
<x>20</x>
<y>230</y>
<width>121</width>
<height>36</height>
</rect>
</property>
<property name="text">
<string>Motor Curve</string>
</property>
</widget>
</widget> </widget>
<customwidgets> <customwidgets>
<customwidget> <customwidget>
@ -33,5 +88,25 @@
</customwidget> </customwidget>
</customwidgets> </customwidgets>
<resources/> <resources/>
<connections/> <connections>
<connection>
<sender>plotAltitudeBtn</sender>
<signal>clicked()</signal>
<receiver>AnalysisWindow</receiver>
<slot>plotAltitude()</slot>
<hints>
<hint type="sourcelabel">
<x>78</x>
<y>61</y>
</hint>
<hint type="destinationlabel">
<x>88</x>
<y>17</y>
</hint>
</hints>
</connection>
</connections>
<slots>
<slot>plotAltitude()</slot>
</slots>
</ui> </ui>

View File

@ -151,7 +151,7 @@ void MainWindow::on_setMotor_clicked()
QString motorName = ui->engineSelectorComboBox->currentText(); QString motorName = ui->engineSelectorComboBox->currentText();
model::MotorModel mm = rseDatabase->getMotorModelByName(motorName.toStdString()); model::MotorModel mm = rseDatabase->getMotorModelByName(motorName.toStdString());
QtRocket::getInstance()->getRocket()->setMotorModel(mm); QtRocket::getInstance()->getRocket()->setMotorModel(mm);
QtRocket::getInstance()->addMotorModels(rseDatabase->getMotors());
} }

View File

@ -62,6 +62,9 @@
</sizepolicy> </sizepolicy>
</property> </property>
<widget class="QPushButton" name="testButton1"> <widget class="QPushButton" name="testButton1">
<property name="enabled">
<bool>false</bool>
</property>
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>30</x> <x>30</x>
@ -83,8 +86,8 @@
<widget class="QPushButton" name="testButton2"> <widget class="QPushButton" name="testButton2">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>30</x> <x>240</x>
<y>70</y> <y>440</y>
<width>191</width> <width>191</width>
<height>25</height> <height>25</height>
</rect> </rect>
@ -96,7 +99,7 @@
</sizepolicy> </sizepolicy>
</property> </property>
<property name="text"> <property name="text">
<string>Calculate Ballistic Trajectory</string> <string>Calculate Trajectory</string>
</property> </property>
</widget> </widget>
<widget class="QWidget" name="layoutWidget"> <widget class="QWidget" name="layoutWidget">
@ -105,12 +108,15 @@
<x>260</x> <x>260</x>
<y>80</y> <y>80</y>
<width>161</width> <width>161</width>
<height>151</height> <height>196</height>
</rect> </rect>
</property> </property>
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<item row="1" column="1"> <item row="1" column="1">
<widget class="QLineEdit" name="initialAngle"> <widget class="QLineEdit" name="initialAngle">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text"> <property name="text">
<string>90.0</string> <string>90.0</string>
</property> </property>
@ -143,7 +149,7 @@
<item row="0" column="1"> <item row="0" column="1">
<widget class="QLineEdit" name="initialVelocity"> <widget class="QLineEdit" name="initialVelocity">
<property name="text"> <property name="text">
<string>50.0</string> <string>5.0</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -164,7 +170,7 @@
<item row="0" column="0"> <item row="0" column="0">
<widget class="QLabel" name="label"> <widget class="QLabel" name="label">
<property name="text"> <property name="text">
<string>Velocity</string> <string>Initial Velocity</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -188,7 +194,7 @@
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>90</x> <x>90</x>
<y>250</y> <y>290</y>
<width>421</width> <width>421</width>
<height>80</height> <height>80</height>
</rect> </rect>
@ -224,7 +230,7 @@
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>240</x> <x>240</x>
<y>360</y> <y>390</y>
<width>201</width> <width>201</width>
<height>25</height> <height>25</height>
</rect> </rect>
@ -244,7 +250,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>1031</width> <width>1031</width>
<height>22</height> <height>32</height>
</rect> </rect>
</property> </property>
<widget class="QMenu" name="menuFile"> <widget class="QMenu" name="menuFile">

View File

@ -90,6 +90,34 @@ void ThrustCurveMotorSelector::on_setMotor_clicked()
return item.data.commonName == commonName; return item.data.commonName == commonName;
}); });
ThrustCurve tc = tcApi->getMotorData(mm.data.motorIdTC).getThrustCurve();
mm.addThrustCurve(tc);
QtRocket::getInstance()->getRocket()->setMotorModel(mm); QtRocket::getInstance()->getRocket()->setMotorModel(mm);
const std::vector<std::pair<double, double>>& res = tc.getThrustCurveData();
auto& plot = ui->plot;
plot->clearGraphs();
plot->setInteraction(QCP::iRangeDrag, true);
plot->setInteraction(QCP::iRangeZoom, true);
// generate some data:
QVector<double> tData(res.size());
QVector<double> 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();
} }

View File

@ -398,6 +398,8 @@ public:
void addThrustCurve(const ThrustCurve& tc) { thrust = tc; } void addThrustCurve(const ThrustCurve& tc) { thrust = tc; }
const ThrustCurve& getThrustCurve() const { return thrust; }
// Thrust parameters // Thrust parameters
MetaData data; MetaData data;
private: private:

View File

@ -81,19 +81,18 @@ public:
*/ */
double getThrust(double t); double getThrust(double t);
/**
* @brief setThrustCurve sets the current thrust curve
* @param curve
* @todo Remove this
*/
void setThrustCurve(const ThrustCurve& curve);
/** /**
* @brief setMotorModel * @brief setMotorModel
* @param motor * @param motor
*/ */
void setMotorModel(const model::MotorModel& 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 * @brief terminateCondition returns true or false, whether the passed-in time/state matches the terminate condition
* @param cond time/state pair * @param cond time/state pair

View File

@ -48,6 +48,8 @@ public:
*/ */
void setThrustCurveVector(const std::vector<std::pair<double, double>>& v); void setThrustCurveVector(const std::vector<std::pair<double, double>>& v);
const std::vector<std::pair<double, double>> getThrustCurveData() const { return thrustCurve; }
private: private:
std::vector<std::pair<double, double>> thrustCurve; std::vector<std::pair<double, double>> thrustCurve;
double maxTime{0.0}; double maxTime{0.0};

View File

@ -14,7 +14,7 @@ namespace utils
{ {
ThrustCurveAPI::ThrustCurveAPI() ThrustCurveAPI::ThrustCurveAPI()
: hostname("https://www.thrustcurve.org/api/v1/"), : hostname("https://www.thrustcurve.org/"),
curlConnection() curlConnection()
{ {
@ -29,13 +29,53 @@ ThrustCurveAPI::~ThrustCurveAPI()
model::MotorModel ThrustCurveAPI::getMotorData(const std::string& motorId) model::MotorModel ThrustCurveAPI::getMotorData(const std::string& motorId)
{ {
std::stringstream endpoint; std::stringstream endpoint;
endpoint << hostname << "download.json?motorId=" << motorId << "&data=samples"; endpoint << hostname << "api/v1/download.json?motorId=" << motorId << "&data=samples";
std::vector<std::string> extraHeaders = {}; std::vector<std::string> extraHeaders = {};
std::string res = curlConnection.get(endpoint.str(), extraHeaders); std::string res = curlConnection.get(endpoint.str(), extraHeaders);
/// TODO: fix this
model::MotorModel mm; model::MotorModel mm;
if(!res.empty())
{
try
{
Json::Reader reader;
Json::Value jsonResult;
reader.parse(res, jsonResult);
std::vector<std::pair<double, double>> 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; return mm;
} }
@ -43,7 +83,7 @@ ThrustcurveMetadata ThrustCurveAPI::getMetadata()
{ {
std::string endpoint = hostname; std::string endpoint = hostname;
endpoint += "metadata.json"; endpoint += "api/v1/metadata.json";
std::string result = curlConnection.get(endpoint, extraHeaders); std::string result = curlConnection.get(endpoint, extraHeaders);
ThrustcurveMetadata ret; ThrustcurveMetadata ret;
@ -123,7 +163,7 @@ std::vector<model::MotorModel> ThrustCurveAPI::searchMotors(const SearchCriteria
{ {
std::vector<model::MotorModel> retVal; std::vector<model::MotorModel> retVal;
std::string endpoint = hostname; std::string endpoint = hostname;
endpoint += "search.json?"; endpoint += "api/v1/search.json?";
for(const auto& i : c.criteria) for(const auto& i : c.criteria)
{ {
endpoint += i.first; endpoint += i.first;