adding mass calculations to MotorModel
This commit is contained in:
parent
0acec0829a
commit
cd95c112d3
@ -146,8 +146,8 @@ void MainWindow::on_loadRSE_button_clicked()
|
|||||||
const std::vector<model::MotorModel>& motors = loader.getMotors();
|
const std::vector<model::MotorModel>& motors = loader.getMotors();
|
||||||
for(const auto& motor : motors)
|
for(const auto& motor : motors)
|
||||||
{
|
{
|
||||||
std::cout << "Adding: " << motor.commonName << std::endl;
|
std::cout << "Adding: " << motor.data.commonName << std::endl;
|
||||||
engineSelector->addItem(QString(motor.commonName.c_str()));
|
engineSelector->addItem(QString(motor.data.commonName.c_str()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ void ThrustCurveMotorSelector::on_searchButton_clicked()
|
|||||||
|
|
||||||
for(const auto& i : motors)
|
for(const auto& i : motors)
|
||||||
{
|
{
|
||||||
ui->motorSelection->addItem(QString::fromStdString(i.commonName));
|
ui->motorSelection->addItem(QString::fromStdString(i.data.commonName));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -87,7 +87,7 @@ void ThrustCurveMotorSelector::on_setMotor_clicked()
|
|||||||
std::end(motorModels),
|
std::end(motorModels),
|
||||||
[&commonName](const auto& item)
|
[&commonName](const auto& item)
|
||||||
{
|
{
|
||||||
return item.commonName == commonName;
|
return item.data.commonName == commonName;
|
||||||
});
|
});
|
||||||
|
|
||||||
QtRocket::getInstance()->getRocket()->setMotorModel(mm);
|
QtRocket::getInstance()->getRocket()->setMotorModel(mm);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include "model/MotorModel.h"
|
#include "model/MotorModel.h"
|
||||||
|
#include "utils/math/Constants.h"
|
||||||
|
|
||||||
namespace model
|
namespace model
|
||||||
{
|
{
|
||||||
@ -13,4 +14,87 @@ MotorModel::~MotorModel()
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double MotorModel::getMass(double simTime) const
|
||||||
|
{
|
||||||
|
// the current mass is the emptyMass + the current prop mass
|
||||||
|
|
||||||
|
// If ignition hasn't occurred, return the totalMass
|
||||||
|
if(!ignitionOccurred)
|
||||||
|
{
|
||||||
|
return data.totalWeight;
|
||||||
|
}
|
||||||
|
else if(simTime - ignitionTime <= data.burnTime)
|
||||||
|
{
|
||||||
|
double thrustTime = simTime - ignitionTime;
|
||||||
|
// Find the right interval in the massCurve
|
||||||
|
auto i = massCurve.cbegin();
|
||||||
|
while(i->first <= thrustTime)
|
||||||
|
{
|
||||||
|
// If thrustTime is equal to a data point that we have, then just return
|
||||||
|
// the mass at that time. Otherwise it fell between two points and we
|
||||||
|
// will interpolate
|
||||||
|
if(i->first == thrustTime)
|
||||||
|
{
|
||||||
|
// return empty mass + remaining propellant mass
|
||||||
|
return emptyMass + i->second;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// linearly interpolate the propellant mass. Then return the empty mass + remaining prop mass
|
||||||
|
double tStart = std::prev(i)->first;
|
||||||
|
double tEnd = i->first;
|
||||||
|
double propMassStart = std::prev(i)->second;
|
||||||
|
double propMassEnd = i->second;
|
||||||
|
double slope = (propMassEnd - propMassStart) / (tEnd - tStart);
|
||||||
|
double currentMass = emptyMass + propMassStart + (thrustTime - tStart) * slope;
|
||||||
|
return currentMass;
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return emptyMass;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MotorModel::setMetaData(const MetaData& md)
|
||||||
|
{
|
||||||
|
data = md;
|
||||||
|
emptyMass = data.totalWeight - data.propWeight;
|
||||||
|
|
||||||
|
// Calculate the Isp for the motor, as we'll need this for the computing the mass flow rate.
|
||||||
|
// This will be the total impulse in Newton-seconds over
|
||||||
|
// the propellant weight. The prop mass is in grams, hence the division by 1000.0 to get kg
|
||||||
|
isp = data.totalImpulse / (utils::math::Constants::g0 * data.propWeight / 1000.0);
|
||||||
|
|
||||||
|
// Precompute the mass curve. Having this precomputed will ensure multiple calls to getMass()
|
||||||
|
// or getThrust() during the same time step don't accidentally decrement the mass multiple times.
|
||||||
|
// Having a lookup table will ensure consistent mass values, as well as speed up the simulation,
|
||||||
|
// just at the cost of some extra space
|
||||||
|
|
||||||
|
// Most motor data in the RASP format has a limitation of 32 data points. We're not going to
|
||||||
|
// match that, so we can pick whatever we want and just interpolate values. We can have 128
|
||||||
|
// for example
|
||||||
|
|
||||||
|
massCurve.reserve(128);
|
||||||
|
double timeStep = data.burnTime / 127.0;
|
||||||
|
double t = 0.0;
|
||||||
|
double propMass{data.propWeight};
|
||||||
|
for(std::size_t i = 0; i < 127; ++i)
|
||||||
|
{
|
||||||
|
massCurve.push_back(std::make_pair(t + i*timeStep, propMass));
|
||||||
|
propMass -= thrust.getThrust(t + i*timeStep) * timeStep * data.propWeight / data.totalImpulse;
|
||||||
|
}
|
||||||
|
massCurve.push_back(std::make_pair(data.burnTime, 0.0));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void MotorModel::moveMetaData(MetaData&& md)
|
||||||
|
{
|
||||||
|
data = std::move(md);
|
||||||
|
}
|
||||||
} // namespace model
|
} // namespace model
|
||||||
|
@ -255,11 +255,12 @@ public:
|
|||||||
if(name == "SU" ||
|
if(name == "SU" ||
|
||||||
name == "Single Use")
|
name == "Single Use")
|
||||||
return MOTORTYPE::SU;
|
return MOTORTYPE::SU;
|
||||||
else if(name == "Hybrid")
|
|
||||||
return MOTORTYPE::HYBRID;
|
|
||||||
else if(name == "reload" ||
|
else if(name == "reload" ||
|
||||||
name == "Reload")
|
name == "Reload")
|
||||||
return MOTORTYPE::RELOAD;
|
return MOTORTYPE::RELOAD;
|
||||||
|
else // It's a hybrid
|
||||||
|
return MOTORTYPE::HYBRID;
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -349,35 +350,64 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/// TODO: make these MotorModel members private. Public just for testing
|
/// TODO: make these MotorModel members private. Public just for testing
|
||||||
//private:
|
//private:
|
||||||
|
|
||||||
MotorAvailability availability{AVAILABILITY::REGULAR}; /// Motor Availability
|
struct MetaData
|
||||||
double avgThrust{0.0}; /// Average thrust in Newtons
|
{
|
||||||
double burnTime{0.0}; /// Burn time in seconds
|
MetaData() = default;
|
||||||
CertOrg certOrg{CERTORG::UNC}; /// The certification organization, defaults to Uncertified
|
~MetaData() = default;
|
||||||
std::string commonName{""}; /// Common name, e.g. A8 or J615
|
MetaData(const MetaData&) = default;
|
||||||
// int dataFiles
|
MetaData(MetaData&&) = default;
|
||||||
std::vector<int> delays; /// 1000 delay means no ejection charge
|
MetaData& operator=(const MetaData&) = default;
|
||||||
std::string designation{""}; /// Other name, usually includes prop type, e.g. H110W
|
MetaData& operator=(MetaData&&) = default;
|
||||||
double diameter{0}; /// motor diameter in mm
|
|
||||||
std::string impulseClass; /// Motor letter, e.g. 'A', 'B', '1/2A', 'M', etc
|
MotorAvailability availability{AVAILABILITY::REGULAR}; /// Motor Availability
|
||||||
std::string infoUrl{""}; /// TODO: ???
|
double avgThrust{0.0}; /// Average thrust in Newtons
|
||||||
double length{0.0}; /// motor length in mm
|
double burnTime{0.0}; /// Burn time in seconds
|
||||||
MotorManufacturer manufacturer{MOTORMANUFACTURER::UNKNOWN}; /// Motor Manufacturer
|
CertOrg certOrg{CERTORG::UNC}; /// The certification organization, defaults to Uncertified
|
||||||
|
std::string commonName{""}; /// Common name, e.g. A8 or J615
|
||||||
|
// int dataFiles
|
||||||
|
std::vector<int> delays; /// 1000 delay means no ejection charge
|
||||||
|
std::string designation{""}; /// Other name, usually includes prop type, e.g. H110W
|
||||||
|
double diameter{0}; /// motor diameter in mm
|
||||||
|
std::string impulseClass; /// Motor letter, e.g. 'A', 'B', '1/2A', 'M', etc
|
||||||
|
std::string infoUrl{""}; /// TODO: ???
|
||||||
|
double length{0.0}; /// motor length in mm
|
||||||
|
MotorManufacturer manufacturer{MOTORMANUFACTURER::UNKNOWN}; /// Motor Manufacturer
|
||||||
|
|
||||||
|
double maxThrust{0.0}; /// Max thrust in Newtons
|
||||||
|
std::string motorIdTC{""}; /// 24 character hex string used by thrustcurve.org to ID a motor
|
||||||
|
std::string propType{""}; /// Propellant type, e.g. black powder
|
||||||
|
double propWeight{0.0}; /// Propellant weight in grams
|
||||||
|
bool sparky{false}; /// true if the motor is "sparky", false otherwise
|
||||||
|
double totalImpulse{0.0}; /// Total impulse in Newton-seconds
|
||||||
|
double totalWeight{0.0}; /// Total weight in grams
|
||||||
|
MotorType type{MOTORTYPE::SU}; /// Motor type, e.g. single-use, reload, or hybrid
|
||||||
|
std::string lastUpdated{""}; /// Date last updated on ThrustCurve.org
|
||||||
|
};
|
||||||
|
|
||||||
|
double getMass(double simTime) const;
|
||||||
|
double getThrust(double simTime);
|
||||||
|
|
||||||
|
void setMetaData(const MetaData& md);
|
||||||
|
void moveMetaData(MetaData&& md);
|
||||||
|
|
||||||
|
void startMotor(double startTime) { ignitionOccurred = true; ignitionTime = startTime; }
|
||||||
|
|
||||||
double maxThrust{0.0}; /// Max thrust in Newtons
|
|
||||||
std::string motorIdTC{""}; /// 24 character hex string used by thrustcurve.org to ID a motor
|
|
||||||
std::string propType{""}; /// Propellant type, e.g. black powder
|
|
||||||
double propWeight{0.0}; /// Propellant weight in grams
|
|
||||||
bool sparky{false}; /// true if the motor is "sparky", false otherwise
|
|
||||||
double totalImpulse{0.0}; /// Total impulse in Newton-seconds
|
|
||||||
double totalWeight{0.0}; /// Total weight in grams
|
|
||||||
MotorType type{MOTORTYPE::SU}; /// Motor type, e.g. single-use, reload, or hybrid
|
|
||||||
std::string lastUpdated{""}; /// Date last updated on ThrustCurve.org
|
|
||||||
|
|
||||||
// Thrust parameters
|
// Thrust parameters
|
||||||
|
MetaData data;
|
||||||
|
private:
|
||||||
|
bool ignitionOccurred{false};
|
||||||
|
double emptyMass;
|
||||||
|
double isp;
|
||||||
|
double maxTime;
|
||||||
|
double ignitionTime;
|
||||||
ThrustCurve thrust; /// The measured motor thrust curve
|
ThrustCurve thrust; /// The measured motor thrust curve
|
||||||
|
|
||||||
|
std::vector<std::pair<double, double>> massCurve;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -7,7 +7,8 @@
|
|||||||
// 3rd party headers
|
// 3rd party headers
|
||||||
/// \endcond
|
/// \endcond
|
||||||
|
|
||||||
#include "ThrustCurve.h"
|
#include "model/ThrustCurve.h"
|
||||||
|
#include "utils/Logger.h"
|
||||||
|
|
||||||
|
|
||||||
ThrustCurve::ThrustCurve(std::vector<std::pair<double, double>>& tc)
|
ThrustCurve::ThrustCurve(std::vector<std::pair<double, double>>& tc)
|
||||||
@ -54,6 +55,12 @@ void ThrustCurve::setIgnitionTime(double t)
|
|||||||
double ThrustCurve::getThrust(double t)
|
double ThrustCurve::getThrust(double t)
|
||||||
{
|
{
|
||||||
// calculate t relative to the start time of the motor
|
// calculate t relative to the start time of the motor
|
||||||
|
static bool burnout{false};
|
||||||
|
if(!burnout && t >= (maxTime + ignitionTime))
|
||||||
|
{
|
||||||
|
burnout = true;
|
||||||
|
utils::Logger::getInstance()->info("Motor burnout at time: " + std::to_string(t));
|
||||||
|
}
|
||||||
t -= ignitionTime;
|
t -= ignitionTime;
|
||||||
if(t < 0.0 || t > maxTime)
|
if(t < 0.0 || t > maxTime)
|
||||||
{
|
{
|
||||||
|
@ -41,6 +41,8 @@ public:
|
|||||||
|
|
||||||
void setIgnitionTime(double t);
|
void setIgnitionTime(double t);
|
||||||
|
|
||||||
|
double getMaxTime() const { return maxTime; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: Get rid of this. This is for temporary testing
|
* TODO: Get rid of this. This is for temporary testing
|
||||||
*/
|
*/
|
||||||
|
@ -37,6 +37,7 @@ SOURCES += \
|
|||||||
utils/ThreadPool.cpp \
|
utils/ThreadPool.cpp \
|
||||||
utils/ThrustCurveAPI.cpp \
|
utils/ThrustCurveAPI.cpp \
|
||||||
utils/math/Quaternion.cpp \
|
utils/math/Quaternion.cpp \
|
||||||
|
utils/math/UtilityMathFunctions.cpp \
|
||||||
utils/math/Vector3.cpp
|
utils/math/Vector3.cpp
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
@ -75,6 +76,7 @@ HEADERS += \
|
|||||||
utils/Triplet.h \
|
utils/Triplet.h \
|
||||||
utils/math/Constants.h \
|
utils/math/Constants.h \
|
||||||
utils/math/Quaternion.h \
|
utils/math/Quaternion.h \
|
||||||
|
utils/math/UtilityMathFunctions.h \
|
||||||
utils/math/Vector3.h
|
utils/math/Vector3.h
|
||||||
|
|
||||||
FORMS += \
|
FORMS += \
|
||||||
|
@ -47,9 +47,9 @@ Propagator::Propagator(Rocket* r)
|
|||||||
/* dpitch/dt */ [](double, const std::vector<double>& s) -> double { return s[9]; },
|
/* dpitch/dt */ [](double, const std::vector<double>& s) -> double { return s[9]; },
|
||||||
/* dyaw/dt */ [](double, const std::vector<double>& s) -> double { return s[10]; },
|
/* dyaw/dt */ [](double, const std::vector<double>& s) -> double { return s[10]; },
|
||||||
/* droll/dt */ [](double, const std::vector<double>& s) -> double { return s[11]; },
|
/* droll/dt */ [](double, const std::vector<double>& s) -> double { return s[11]; },
|
||||||
/* dpitchRate/dt */ [this](double, const std::vector<double>& s) -> double { (getTorqueP() - s[7] * s[8] * (getIroll() - getIyaw())) / getIpitch(); },
|
/* dpitchRate/dt */ [this](double, const std::vector<double>& s) -> double { return (getTorqueP() - s[7] * s[8] * (getIroll() - getIyaw())) / getIpitch(); },
|
||||||
/* dyawRate/dt */ [this](double, const std::vector<double>& s) -> double { (getTorqueQ() - s[6] * s[9] * (getIpitch() - getIroll())) / getIyaw(); },
|
/* dyawRate/dt */ [this](double, const std::vector<double>& s) -> double { return (getTorqueQ() - s[6] * s[9] * (getIpitch() - getIroll())) / getIyaw(); },
|
||||||
/* drollRate/dt */ [this](double, const std::vector<double>& s) -> double { (getTorqueR() - s[6] * s[7] * (getIyaw() - getIpitch())) / getIroll(); }));
|
/* drollRate/dt */ [this](double, const std::vector<double>& s) -> double { return (getTorqueR() - s[6] * s[7] * (getIyaw() - getIpitch())) / getIroll(); }));
|
||||||
|
|
||||||
|
|
||||||
integrator->setTimeStep(timeStep);
|
integrator->setTimeStep(timeStep);
|
||||||
|
@ -35,7 +35,7 @@ RSEDatabaseLoader::~RSEDatabaseLoader()
|
|||||||
|
|
||||||
void RSEDatabaseLoader::buildAndAppendMotorModel(boost::property_tree::ptree& v)
|
void RSEDatabaseLoader::buildAndAppendMotorModel(boost::property_tree::ptree& v)
|
||||||
{
|
{
|
||||||
model::MotorModel mm;
|
model::MotorModel::MetaData mm;
|
||||||
mm.availability = model::MotorModel::MotorAvailability(model::MotorModel::AVAILABILITY::REGULAR);
|
mm.availability = model::MotorModel::MotorAvailability(model::MotorModel::AVAILABILITY::REGULAR);
|
||||||
mm.avgThrust = v.get<double>("<xmlattr>.avgThrust", 0.0);
|
mm.avgThrust = v.get<double>("<xmlattr>.avgThrust", 0.0);
|
||||||
mm.burnTime = v.get<double>("<xmlattr>.burn-time", 0.0);
|
mm.burnTime = v.get<double>("<xmlattr>.burn-time", 0.0);
|
||||||
@ -70,7 +70,9 @@ void RSEDatabaseLoader::buildAndAppendMotorModel(boost::property_tree::ptree& v)
|
|||||||
thrustData.push_back(std::make_pair(tdata, fdata));
|
thrustData.push_back(std::make_pair(tdata, fdata));
|
||||||
}
|
}
|
||||||
|
|
||||||
motors.emplace_back(std::move(mm));
|
model::MotorModel motorModel;
|
||||||
|
motorModel.moveMetaData(std::move(mm));
|
||||||
|
motors.emplace_back(std::move(motorModel));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -148,7 +148,8 @@ std::vector<model::MotorModel> ThrustCurveAPI::searchMotors(const SearchCriteria
|
|||||||
iter != jsonResult["results"].end();
|
iter != jsonResult["results"].end();
|
||||||
++iter)
|
++iter)
|
||||||
{
|
{
|
||||||
model::MotorModel mm;
|
model::MotorModel motorModel;
|
||||||
|
model::MotorModel::MetaData mm;
|
||||||
mm.commonName = (*iter)["commonName"].asString();
|
mm.commonName = (*iter)["commonName"].asString();
|
||||||
|
|
||||||
std::string availability = (*iter)["availability"].asString();
|
std::string availability = (*iter)["availability"].asString();
|
||||||
@ -185,7 +186,8 @@ std::vector<model::MotorModel> ThrustCurveAPI::searchMotors(const SearchCriteria
|
|||||||
else
|
else
|
||||||
mm.type = model::MotorModel::MotorType(model::MotorModel::MOTORTYPE::HYBRID);
|
mm.type = model::MotorModel::MotorType(model::MotorModel::MOTORTYPE::HYBRID);
|
||||||
|
|
||||||
retVal.push_back(mm);
|
motorModel.moveMetaData(std::move(mm));
|
||||||
|
retVal.push_back(motorModel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch(const std::exception& e)
|
catch(const std::exception& e)
|
||||||
|
15
utils/math/UtilityMathFunctions.cpp
Normal file
15
utils/math/UtilityMathFunctions.cpp
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
|
||||||
|
/// \cond
|
||||||
|
// C headers
|
||||||
|
// C++ headers
|
||||||
|
// 3rd party headers
|
||||||
|
/// \endcond
|
||||||
|
|
||||||
|
|
||||||
|
namespace utils
|
||||||
|
{
|
||||||
|
namespace math
|
||||||
|
{
|
||||||
|
|
||||||
|
} // namespace math
|
||||||
|
} // namespace utils
|
40
utils/math/UtilityMathFunctions.h
Normal file
40
utils/math/UtilityMathFunctions.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#ifndef UTILITYMATHFUNCTIONS_H
|
||||||
|
#define UTILITYMATHFUNCTIONS_H
|
||||||
|
|
||||||
|
/// \cond
|
||||||
|
// C headers
|
||||||
|
// C++ headers
|
||||||
|
#include <cmath>
|
||||||
|
#include <limits>
|
||||||
|
// 3rd party headers
|
||||||
|
/// \endcond
|
||||||
|
|
||||||
|
namespace utils
|
||||||
|
{
|
||||||
|
namespace math
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief doublesEqual compares two doubles for equality, using a value for number of smallest
|
||||||
|
* places to ignore
|
||||||
|
*
|
||||||
|
* This is derived from the example on cppreference.com: https://en.cppreference.com/w/cpp/types/numeric_limits/epsilon
|
||||||
|
* The default ulp of 8 with a numeric_limits<double>::epsilon() of ~2e-16, so ulp of 8 yields 12
|
||||||
|
* significant figures. A ulp of 10 yields 6 significant figures.
|
||||||
|
* @param a the first double to compare
|
||||||
|
* @param b the second double to compare
|
||||||
|
* @param ulp number of smallest decimal places to ignore
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
bool floatingPointEqual(T a, T b, int ulp = 4)
|
||||||
|
{
|
||||||
|
return std::fabs(a - b) <= std::numeric_limits<T>::epsilon() * std::fabs(a + b) * ulp
|
||||||
|
// unless the result is subnormal
|
||||||
|
|| std::fabs(a - b) < std::numeric_limits<T>::min();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace math
|
||||||
|
} // namespace utils
|
||||||
|
#endif // UTILITYMATHFUNCTIONS_H
|
Loading…
x
Reference in New Issue
Block a user