mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-13 16:18:42 +00:00
All are harmless but for the sake of clarity have fixed them all bar a couple; - Lucene grumbles about signed/unsigned conversion which is/isn't valid depending upon the version of CLucene you compile with. Either way it is harmless. - QxtScheduleView has a bunch of issues, but since it is a third party widget its better to leave it unchanged.
288 lines
9.6 KiB
C++
288 lines
9.6 KiB
C++
/*
|
|
* 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 "MainWindow.h"
|
|
#include "Units.h"
|
|
#include <QtGui>
|
|
#include <sstream>
|
|
#include <cmath>
|
|
|
|
typedef QDoubleSpinBox* QDoubleSpinBoxPtr;
|
|
|
|
ToolsRhoEstimator::ToolsRhoEstimator(MainWindow *mainWindow, QWidget *parent) : QDialog(parent), mainWindow(mainWindow) {
|
|
// Does the user prefer metric or imperial? Set the initial radio
|
|
// button and field settings to their GoldenCheetah preference.
|
|
useMetricUnits = mainWindow->useMetricUnits;
|
|
|
|
// 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(tr("Temperature (C):"));
|
|
thl->addWidget(tempLabel);
|
|
tempSpinBox->setValue(15);
|
|
} else {
|
|
tempLabel = new QLabel(tr("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(tr("Air Pressure (hPa):"));
|
|
phl->addWidget(pressLabel);
|
|
pressSpinBox->setValue(1018);
|
|
} else {
|
|
pressLabel = new QLabel(tr("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(tr("Dewpoint (C):"));
|
|
dhl->addWidget(dewpLabel);
|
|
dewpSpinBox->setValue(7.5);
|
|
} else {
|
|
dewpLabel = new QLabel(tr("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 */) {
|
|
|
|
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(tr("Temperature (F):"));
|
|
pressSpinBox->setValue(
|
|
hectopascals_to_inchesmercury(pressSpinBox->value()));
|
|
pressLabel->setText(tr("Air Pressure (inHg):"));
|
|
dewpSpinBox->setValue(celsius_to_fahrenheit(dewpSpinBox->value()));
|
|
dewpLabel->setText(tr("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(tr("Temperature (C):"));
|
|
pressSpinBox->setValue(
|
|
inchesmercury_to_hectopascals(pressSpinBox->value()));
|
|
pressLabel->setText(tr("Air Pressure (hPa):"));
|
|
dewpSpinBox->setValue(fahrenheit_to_celsius(dewpSpinBox->value()));
|
|
dewpLabel->setText(tr("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;
|
|
}
|