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/*
# IDE
.vscode/qtrocket.pro.user
.vscode/
qtrocket.pro.user
.qmake.stash

View File

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

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!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>
<data>
<variable>EnvironmentId</variable>

View File

@ -45,6 +45,8 @@ public:
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 setSimulationOptions(std::shared_ptr<sim::SimulationOptions> options) { simOptions = options; }

View File

@ -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> 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:
@ -37,7 +54,66 @@ AnalysisWindow::AnalysisWindow(QWidget *parent) :
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);
~AnalysisWindow();
private slots:
void plotAltitude();
//void plotAtmosphere();
void plotVelocity();
void plotMotorCurveBtn();
private:
Ui::AnalysisWindow *ui;
};

View File

@ -16,13 +16,68 @@
<widget class="QCustomPlot" name="plotWidget" native="true">
<property name="geometry">
<rect>
<x>19</x>
<x>149</x>
<y>29</y>
<width>961</width>
<height>671</height>
<width>831</width>
<height>541</height>
</rect>
</property>
</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>
<customwidgets>
<customwidget>
@ -33,5 +88,25 @@
</customwidget>
</customwidgets>
<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>

View File

@ -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());
}

View File

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

View File

@ -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<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; }
const ThrustCurve& getThrustCurve() const { return thrust; }
// Thrust parameters
MetaData data;
private:

View File

@ -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

View File

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

View File

@ -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<std::string> 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<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;
}
@ -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<model::MotorModel> ThrustCurveAPI::searchMotors(const SearchCriteria
{
std::vector<model::MotorModel> retVal;
std::string endpoint = hostname;
endpoint += "search.json?";
endpoint += "api/v1/search.json?";
for(const auto& i : c.criteria)
{
endpoint += i.first;