mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-13 16:18:42 +00:00
added a new air density (rho) estimator dialog box, hooked it into the Tools menu, and updated src.pro to add compilation dependencies for it
This commit is contained in:
committed by
Mark Liversedge
parent
c0ba84cdbc
commit
5c8beee46d
@@ -50,6 +50,7 @@
|
||||
#include "RideCalendar.h"
|
||||
#include "DatePickerDialog.h"
|
||||
#include "ToolsDialog.h"
|
||||
#include "ToolsRhoEstimator.h"
|
||||
#include "MetricAggregator.h"
|
||||
#include "SplitActivityWizard.h"
|
||||
#include "BatchExportDialog.h"
|
||||
@@ -610,6 +611,7 @@ MainWindow::MainWindow(const QDir &home) :
|
||||
QMenu *optionsMenu = menuBar()->addMenu(tr("&Tools"));
|
||||
optionsMenu->addAction(tr("&Options..."), this, SLOT(showOptions()), tr("Ctrl+O"));
|
||||
optionsMenu->addAction(tr("Critical Power Calculator..."), this, SLOT(showTools()));
|
||||
optionsMenu->addAction(tr("Air Density (Rho) Estimator..."), this, SLOT(showRhoEstimator()));
|
||||
|
||||
optionsMenu->addSeparator();
|
||||
optionsMenu->addAction(tr("Workout Wizard"), this, SLOT(showWorkoutWizard()));
|
||||
@@ -1075,6 +1077,12 @@ void MainWindow::showTools()
|
||||
td->show();
|
||||
}
|
||||
|
||||
void MainWindow::showRhoEstimator()
|
||||
{
|
||||
ToolsRhoEstimator *tre = new ToolsRhoEstimator();
|
||||
tre->show();
|
||||
}
|
||||
|
||||
void MainWindow::showWorkoutWizard()
|
||||
{
|
||||
WorkoutWizard *ww = new WorkoutWizard(this);
|
||||
|
||||
@@ -245,6 +245,7 @@ class MainWindow : public QMainWindow
|
||||
bool saveRideExitDialog(); // save dirty rides on exit dialog
|
||||
void showOptions();
|
||||
void showTools();
|
||||
void showRhoEstimator();
|
||||
void toggleSidebar();
|
||||
void showSidebar(bool want);
|
||||
void showToolbar(bool want);
|
||||
|
||||
288
src/ToolsRhoEstimator.cpp
Normal file
288
src/ToolsRhoEstimator.cpp
Normal file
@@ -0,0 +1,288 @@
|
||||
/*
|
||||
* Copyright (c) Steven Gribble (gribble [at] gmail [dot] com)
|
||||
* http://www.gribble.org/
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "ToolsRhoEstimator.h"
|
||||
#include "Settings.h"
|
||||
#include "Units.h"
|
||||
#include <QtGui>
|
||||
#include <sstream>
|
||||
#include <cmath>
|
||||
|
||||
typedef QDoubleSpinBox* QDoubleSpinBoxPtr;
|
||||
|
||||
ToolsRhoEstimator::ToolsRhoEstimator(QWidget *parent) : QDialog(parent) {
|
||||
// Does the user prefer metric or imperial? Set the initial radio
|
||||
// button and field settings to their GoldenCheetah preference.
|
||||
QVariant unit = appsettings->value(NULL, GC_UNIT);
|
||||
useMetricUnits = (unit.toString() == "Metric");
|
||||
|
||||
// Set the main window title.
|
||||
setWindowTitle(tr("Air Density (Rho) Estimator"));
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
// Create the main layout box.
|
||||
QVBoxLayout *mainVBox = new QVBoxLayout(this);
|
||||
|
||||
// Set up the instructions field.
|
||||
mainVBox->addWidget(new QLabel(
|
||||
tr("Enter measured values for the following:")));
|
||||
mainVBox->addSpacing(5);
|
||||
|
||||
// "metric vs. imperial" radio buttons. (Makes it easier than
|
||||
// forcing them to change their preference in the preferences menu.)
|
||||
QHBoxLayout *rads = new QHBoxLayout;
|
||||
metBut = new QRadioButton(tr("Metric"));
|
||||
metBut->setChecked(useMetricUnits);
|
||||
rads->addWidget(metBut);
|
||||
impBut = new QRadioButton(tr("Imperial"));
|
||||
impBut->setChecked(!useMetricUnits);
|
||||
// note that we only need to connect one of the radio button
|
||||
// signals, since changing one also changes the other.
|
||||
connect(impBut, SIGNAL(toggled(bool)),
|
||||
this, SLOT(on_radio_toggled(bool)));
|
||||
rads->addWidget(impBut);
|
||||
mainVBox->addLayout(rads);
|
||||
|
||||
// The temperature box.
|
||||
QHBoxLayout *thl = new QHBoxLayout;
|
||||
tempSpinBox = new QDoubleSpinBox(this);
|
||||
tempSpinBox->setDecimals(2);
|
||||
tempSpinBox->setRange(-200, 200);
|
||||
if (useMetricUnits) {
|
||||
tempLabel = new QLabel("Temperature (C):");
|
||||
thl->addWidget(tempLabel);
|
||||
tempSpinBox->setValue(15);
|
||||
} else {
|
||||
tempLabel = new QLabel("Temperature (F):");
|
||||
thl->addWidget(tempLabel);
|
||||
tempSpinBox->setValue(59);
|
||||
}
|
||||
tempSpinBox->setWrapping(false);
|
||||
tempSpinBox->setAlignment(Qt::AlignRight);
|
||||
connect(tempSpinBox, SIGNAL(valueChanged(double)),
|
||||
this, SLOT(on_valueChanged(double)));
|
||||
thl->addWidget(tempSpinBox);
|
||||
mainVBox->addLayout(thl);
|
||||
|
||||
// The air pressure box.
|
||||
QHBoxLayout *phl = new QHBoxLayout;
|
||||
pressSpinBox = new QDoubleSpinBox(this);
|
||||
pressSpinBox->setDecimals(2);
|
||||
pressSpinBox->setRange(0, 2000);
|
||||
if (useMetricUnits) {
|
||||
pressLabel = new QLabel("Air Pressure (hPa):");
|
||||
phl->addWidget(pressLabel);
|
||||
pressSpinBox->setValue(1018);
|
||||
} else {
|
||||
pressLabel = new QLabel("Air Pressure (inHg):");
|
||||
phl->addWidget(pressLabel);
|
||||
pressSpinBox->setValue(30.06);
|
||||
}
|
||||
pressSpinBox->setWrapping(false);
|
||||
pressSpinBox->setAlignment(Qt::AlignRight);
|
||||
connect(pressSpinBox, SIGNAL(valueChanged(double)),
|
||||
this, SLOT(on_valueChanged(double)));
|
||||
phl->addWidget(pressSpinBox);
|
||||
mainVBox->addLayout(phl);
|
||||
|
||||
// The dewpoint box.
|
||||
QHBoxLayout *dhl = new QHBoxLayout;
|
||||
dewpSpinBox = new QDoubleSpinBox(this);
|
||||
dewpSpinBox->setDecimals(2);
|
||||
dewpSpinBox->setRange(-200, 200);
|
||||
if (useMetricUnits) {
|
||||
dewpLabel = new QLabel("Dewpoint (C):");
|
||||
dhl->addWidget(dewpLabel);
|
||||
dewpSpinBox->setValue(7.5);
|
||||
} else {
|
||||
dewpLabel = new QLabel("Dewpoint (F):");
|
||||
dhl->addWidget(dewpLabel);
|
||||
dewpSpinBox->setValue(45.5);
|
||||
}
|
||||
dewpSpinBox->setWrapping(false);
|
||||
dewpSpinBox->setAlignment(Qt::AlignRight);
|
||||
connect(dewpSpinBox, SIGNAL(valueChanged(double)),
|
||||
this, SLOT(on_valueChanged(double)));
|
||||
dhl->addWidget(dewpSpinBox);
|
||||
mainVBox->addLayout(dhl);
|
||||
|
||||
// Label for the computed air density.
|
||||
mainVBox->addSpacing(15);
|
||||
mainVBox->addWidget(new QLabel(
|
||||
tr("Estimated air density (rho):")));
|
||||
|
||||
// Estimated Rho (metric units).
|
||||
QHBoxLayout *rhoMetHBox = new QHBoxLayout;
|
||||
txtRhoMet = new QLineEdit(this);
|
||||
txtRhoMet->setAlignment(Qt::AlignRight);
|
||||
txtRhoMet->setReadOnly(true);
|
||||
rhoMetHBox->addWidget(txtRhoMet);
|
||||
rhoMetHBox->addWidget(new QLabel(tr("(kg/m^3)")));
|
||||
mainVBox->addLayout(rhoMetHBox);
|
||||
|
||||
// Estimated Rho (imperial units).
|
||||
QHBoxLayout *rhoImpHBox = new QHBoxLayout;
|
||||
txtRhoImp = new QLineEdit(this);
|
||||
txtRhoImp->setAlignment(Qt::AlignRight);
|
||||
txtRhoImp->setReadOnly(true);
|
||||
rhoImpHBox->addWidget(txtRhoImp);
|
||||
rhoImpHBox->addWidget(new QLabel(tr("(lb/ft^3)")));
|
||||
mainVBox->addLayout(rhoImpHBox);
|
||||
|
||||
// "Done" button.
|
||||
mainVBox->addSpacing(15);
|
||||
QHBoxLayout *buttonHBox = new QHBoxLayout;
|
||||
btnOK = new QPushButton(this);
|
||||
btnOK->setText(tr("Done"));
|
||||
buttonHBox->addWidget(btnOK);
|
||||
mainVBox->addLayout(buttonHBox);
|
||||
connect(btnOK, SIGNAL(clicked()), this, SLOT(on_btnOK_clicked()));
|
||||
|
||||
// Force the initial "rho" calculation when the dialog box comes up
|
||||
// initially.
|
||||
this->on_valueChanged(0.0);
|
||||
}
|
||||
|
||||
void ToolsRhoEstimator::on_radio_toggled(bool checked) {
|
||||
checked = true; // hack to avoid the "unused parameter" g++ warning
|
||||
|
||||
if (impBut->isChecked()) {
|
||||
// we just changed from metric --> imperial, so relabel and do the
|
||||
// field conversions for the user.
|
||||
tempSpinBox->setValue(celsius_to_fahrenheit(tempSpinBox->value()));
|
||||
tempLabel->setText("Temperature (F):");
|
||||
pressSpinBox->setValue(
|
||||
hectopascals_to_inchesmercury(pressSpinBox->value()));
|
||||
pressLabel->setText("Air Pressure (inHg):");
|
||||
dewpSpinBox->setValue(celsius_to_fahrenheit(dewpSpinBox->value()));
|
||||
dewpLabel->setText("Dewpoint (F):");
|
||||
} else {
|
||||
// we just changed from imperial --> metric, so relabel and do the
|
||||
// field conversions for the user.
|
||||
tempSpinBox->setValue(fahrenheit_to_celsius(tempSpinBox->value()));
|
||||
tempLabel->setText("Temperature (C):");
|
||||
pressSpinBox->setValue(
|
||||
inchesmercury_to_hectopascals(pressSpinBox->value()));
|
||||
pressLabel->setText("Air Pressure (hPa):");
|
||||
dewpSpinBox->setValue(fahrenheit_to_celsius(dewpSpinBox->value()));
|
||||
dewpLabel->setText("Dewpoint (C):");
|
||||
}
|
||||
|
||||
// Relay the "something has changed" signal to
|
||||
// on_valueChanged(double).
|
||||
this->on_valueChanged(0.0);
|
||||
}
|
||||
|
||||
void ToolsRhoEstimator::on_btnOK_clicked() {
|
||||
// all done!
|
||||
accept();
|
||||
}
|
||||
|
||||
void ToolsRhoEstimator::on_valueChanged(double newval) {
|
||||
newval++; // hack to avoid the "unused parameter" g++ warning.
|
||||
|
||||
// scrape the field values, convert to metric if needed.
|
||||
double temp = tempSpinBox->value();
|
||||
double press = pressSpinBox->value();
|
||||
double dewp = dewpSpinBox->value();
|
||||
if (impBut->isChecked()) {
|
||||
// yup, convert to metric.
|
||||
temp = fahrenheit_to_celsius(temp);
|
||||
press = inchesmercury_to_hectopascals(press);
|
||||
dewp = fahrenheit_to_celsius(dewp);
|
||||
}
|
||||
|
||||
// calculate rho, in (kg/m^3)
|
||||
double rho_met = calculate_rho(temp, press, dewp);
|
||||
// calculate rho in imperial units.
|
||||
double rho_imp = rho_met_to_imp(rho_met);
|
||||
|
||||
// display the calculated Rho's.
|
||||
std::stringstream ss_met, ss_imp;
|
||||
ss_met.precision(6);
|
||||
ss_met << rho_met;
|
||||
txtRhoMet->setText(tr(ss_met.str().c_str()));
|
||||
ss_imp.precision(6);
|
||||
ss_imp << rho_imp;
|
||||
txtRhoImp->setText(tr(ss_imp.str().c_str()));
|
||||
}
|
||||
|
||||
double ToolsRhoEstimator::fahrenheit_to_celsius(double f) {
|
||||
return (f - 32.0) * (5.0/9.0);
|
||||
}
|
||||
|
||||
double ToolsRhoEstimator::celsius_to_fahrenheit(double c) {
|
||||
return (c * 9.0 / 5.0) + 32.0;
|
||||
}
|
||||
|
||||
double ToolsRhoEstimator::hectopascals_to_inchesmercury(double hpa) {
|
||||
return (hpa / 1000.0) * 29.53;
|
||||
}
|
||||
|
||||
double ToolsRhoEstimator::inchesmercury_to_hectopascals(double inhg) {
|
||||
return (inhg * 1000.0) / 29.53;
|
||||
}
|
||||
|
||||
double ToolsRhoEstimator::rho_met_to_imp(double rho) {
|
||||
return rho * ((0.30480061 * 0.30480061 * 0.30480061) / 0.45359237);
|
||||
}
|
||||
|
||||
double ToolsRhoEstimator::calculate_rho(double temp,
|
||||
double press,
|
||||
double dewp) {
|
||||
// Step 1: calculate the saturation water pressure at the dew point,
|
||||
// i.e., the vapor pressure in the air. Use Herman Wobus' equation.
|
||||
double c0 = 0.99999683;
|
||||
double c1 = -0.90826951E-02;
|
||||
double c2 = 0.78736169E-04;
|
||||
double c3 = -0.61117958E-06;
|
||||
double c4 = 0.43884187E-08;
|
||||
double c5 = -0.29883885E-10;
|
||||
double c6 = 0.21874425E-12;
|
||||
double c7 = -0.17892321E-14;
|
||||
double c8 = 0.11112018E-16;
|
||||
double c9 = -0.30994571E-19;
|
||||
double p = c0 +
|
||||
dewp*(c1 +
|
||||
dewp*(c2 +
|
||||
dewp*(c3 +
|
||||
dewp*(c4 +
|
||||
dewp*(c5 +
|
||||
dewp*(c6 +
|
||||
dewp*(c7 +
|
||||
dewp*(c8 +
|
||||
dewp*(c9)))))))));
|
||||
double psat_mbar = 6.1078 / std::pow(p, 8);
|
||||
|
||||
// Step 2: calculate the vapor pressure.
|
||||
double pv_pascals = psat_mbar * 100.0;
|
||||
|
||||
// Step 3: calculate the pressure of dry air, given the vapor
|
||||
// pressure and actual pressure.
|
||||
double pd_pascals = (press*100) - pv_pascals;
|
||||
|
||||
// Step 4: calculate the air density, using the equation for
|
||||
// the density of a mixture of dry air and water vapor.
|
||||
double density =
|
||||
(pd_pascals / (287.0531 * (temp + 273.15))) +
|
||||
(pv_pascals / (461.4964 * (temp + 273.15)));
|
||||
|
||||
return density;
|
||||
}
|
||||
60
src/ToolsRhoEstimator.h
Normal file
60
src/ToolsRhoEstimator.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) 2012 Steven Gribble (gribble [at] gmail [dot] com)
|
||||
* http://www.gribble.org/
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "GoldenCheetah.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QtGui>
|
||||
|
||||
// This class implements a dialog box containing a tool for
|
||||
// helping the user estimate rho, the air density, given
|
||||
// temperature, pressure, and dew point as inputs.
|
||||
class ToolsRhoEstimator : public QDialog {
|
||||
Q_OBJECT
|
||||
G_OBJECT
|
||||
|
||||
public:
|
||||
ToolsRhoEstimator(QWidget *parent = 0);
|
||||
|
||||
private:
|
||||
bool useMetricUnits;
|
||||
QRadioButton *metBut;
|
||||
QRadioButton *impBut;
|
||||
QPushButton *btnOK;
|
||||
QLineEdit *txtRhoImp;
|
||||
QLineEdit *txtRhoMet;
|
||||
QLabel *tempLabel;
|
||||
QLabel *pressLabel;
|
||||
QLabel *dewpLabel;
|
||||
QDoubleSpinBox *tempSpinBox;
|
||||
QDoubleSpinBox *pressSpinBox;
|
||||
QDoubleSpinBox *dewpSpinBox;
|
||||
double fahrenheit_to_celsius(double f);
|
||||
double celsius_to_fahrenheit(double c);
|
||||
double hectopascals_to_inchesmercury(double hpa);
|
||||
double inchesmercury_to_hectopascals(double inhg);
|
||||
double rho_met_to_imp(double rho);
|
||||
double calculate_rho(double temp, double press, double dewp);
|
||||
|
||||
private slots:
|
||||
void on_radio_toggled(bool checked);
|
||||
void on_btnOK_clicked();
|
||||
void on_valueChanged(double newval);
|
||||
};
|
||||
@@ -328,6 +328,7 @@ HEADERS += \
|
||||
TxtRideFile.h \
|
||||
TimeUtils.h \
|
||||
ToolsDialog.h \
|
||||
ToolsRhoEstimator.h \
|
||||
TrainTool.h \
|
||||
TreeMapWindow.h \
|
||||
TreeMapPlot.h \
|
||||
@@ -506,6 +507,7 @@ SOURCES += \
|
||||
TimeInZone.cpp \
|
||||
TimeUtils.cpp \
|
||||
ToolsDialog.cpp \
|
||||
ToolsRhoEstimator.cpp \
|
||||
TrainTool.cpp \
|
||||
TreeMapWindow.cpp \
|
||||
TreeMapPlot.cpp \
|
||||
|
||||
Reference in New Issue
Block a user