/*
EnvironmentCalculations.cpp
Copyright (C) 2016 Tyler Glenn
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 3 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, see .
Written: Dec 30 2015.
Last Updated: Dec 23 2017.
This header must be included in any derived code or copies of the code.
*/
#include "EnvironmentCalculations.h"
#include
#include
#define hi_coeff1 -42.379
#define hi_coeff2 2.04901523
#define hi_coeff3 10.14333127
#define hi_coeff4 -0.22475541
#define hi_coeff5 -0.00683783
#define hi_coeff6 -0.05481717
#define hi_coeff7 0.00122874
#define hi_coeff8 0.00085282
#define hi_coeff9 -0.00000199
/****************************************************************/
float EnvironmentCalculations::Altitude
(
float pressure,
AltitudeUnit altUnit,
float referencePressure,
float outdoorTemp,
TempUnit tempUnit
)
{
// Equation inverse to EquivalentSeaLevelPressure calculation.
float altitude = NAN;
if (!isnan(pressure) && !isnan(referencePressure) && !isnan(outdoorTemp))
{
if(tempUnit != TempUnit_Celsius)
outdoorTemp = (outdoorTemp - 32.0) * (5.0 / 9.0); /*conversion to [°C]*/
altitude = pow(referencePressure / pressure, 0.190234) - 1;
altitude *= ((outdoorTemp + 273.15) / 0.0065);
if(altUnit != AltitudeUnit_Meters) altitude *= 3.28084;
}
return altitude;
}
/****************************************************************/
float EnvironmentCalculations::AbsoluteHumidity
(
float temperature,
float humidity,
TempUnit tempUnit
)
{
//taken from https://carnotcycle.wordpress.com/2012/08/04/how-to-convert-relative-humidity-to-absolute-humidity/
//precision is about 0.1°C in range -30 to 35°C
//August-Roche-Magnus 6.1094 exp(17.625 x T)/(T + 243.04)
//Buck (1981) 6.1121 exp(17.502 x T)/(T + 240.97)
//reference https://www.eas.ualberta.ca/jdwilson/EAS372_13/Vomel_CIRES_satvpformulae.html
float temp = NAN;
const float mw = 18.01534; // molar mass of water g/mol
const float r = 8.31447215; // Universal gas constant J/mol/K
if (isnan(temperature) || isnan(humidity) )
{
return NAN;
}
if(tempUnit != TempUnit_Celsius)
{
temperature = (temperature - 32.0) * (5.0 / 9.0); /*conversion to [°C]*/
}
temp = pow(2.718281828, (17.67 * temperature) / (temperature + 243.5));
//return (6.112 * temp * humidity * 2.1674) / (273.15 + temperature); //simplified version
return (6.112 * temp * humidity * mw) / ((273.15 + temperature) * r); //long version
}
/****************************************************************/
//FYI: https://ehp.niehs.nih.gov/1206273/ in detail this flow graph: https://ehp.niehs.nih.gov/wp-content/uploads/2013/10/ehp.1206273.g003.png
float EnvironmentCalculations::HeatIndex
(
float temperature,
float humidity,
TempUnit tempUnit
)
{
float heatIndex(NAN);
if ( isnan(temperature) || isnan(humidity) )
{
return heatIndex;
}
if (tempUnit == TempUnit_Celsius)
{
temperature = (temperature * (9.0 / 5.0) + 32.0); /*conversion to [°F]*/
}
// Using both Rothfusz and Steadman's equations
// http://www.wpc.ncep.noaa.gov/html/heatindex_equation.shtml
if (temperature <= 40)
{
heatIndex = temperature; //first red block
}
else
{
heatIndex = 0.5 * (temperature + 61.0 + ((temperature - 68.0) * 1.2) + (humidity * 0.094)); //calculate A -- from the official site, not the flow graph
if (heatIndex >= 79)
{
/*
* calculate B
* the following calculation is optimized. Simply spoken, reduzed cpu-operations to minimize used ram and runtime.
* Check the correctness with the following link:
* http://www.wolframalpha.com/input/?source=nav&i=b%3D+x1+%2B+x2*T+%2B+x3*H+%2B+x4*T*H+%2B+x5*T*T+%2B+x6*H*H+%2B+x7*T*T*H+%2B+x8*T*H*H+%2B+x9*T*T*H*H
*/
heatIndex = hi_coeff1
+ (hi_coeff2 + hi_coeff4 * humidity + temperature * (hi_coeff5 + hi_coeff7 * humidity)) * temperature
+ (hi_coeff3 + humidity * (hi_coeff6 + temperature * (hi_coeff8 + hi_coeff9 * temperature))) * humidity;
//third red block
if ((humidity < 13) && (temperature >= 80.0) && (temperature <= 112.0))
{
heatIndex -= ((13.0 - humidity) * 0.25) * sqrt((17.0 - abs(temperature - 95.0)) * 0.05882);
} //fourth red block
else if ((humidity > 85.0) && (temperature >= 80.0) && (temperature <= 87.0))
{
heatIndex += (0.02 * (humidity - 85.0) * (87.0 - temperature));
}
}
}
if (tempUnit == TempUnit_Celsius)
{
return (heatIndex - 32.0) * (5.0 / 9.0); /*conversion back to [°C]*/
}
else
{
return heatIndex; //fifth red block
}
}
/****************************************************************/
float EnvironmentCalculations::EquivalentSeaLevelPressure
(
float altitude,
float temp,
float pres,
AltitudeUnit altUnit,
TempUnit tempUnit
)
{
float seaPress = NAN;
if(!isnan(altitude) && !isnan(temp) && !isnan(pres))
{
if(tempUnit != TempUnit_Celsius)
temp = (temp - 32.0) * (5.0 / 9.0); /*conversion to [°C]*/
if(altUnit != AltitudeUnit_Meters)
altitude *= 0.3048; /*conversion to meters*/
seaPress = (pres / pow(1 - ((0.0065 *altitude) / (temp + (0.0065 *altitude) + 273.15)), 5.257));
}
return seaPress;
}
/****************************************************************/
float EnvironmentCalculations::DewPoint
(
float temp,
float hum,
TempUnit tempUnit
)
{
// Equations courtesy of Brian McNoldy from http://andrew.rsmas.miami.edu;
float dewPoint = NAN;
if(!isnan(temp) && !isnan(hum))
{
if (tempUnit == TempUnit_Celsius)
{
dewPoint = 243.04 * (log(hum/100.0) + ((17.625 * temp)/(243.04 + temp)))
/(17.625 - log(hum/100.0) - ((17.625 * temp)/(243.04 + temp)));
}
else
{
float ctemp = (temp - 32.0) * 5.0/9.0;
dewPoint = 243.04 * (log(hum/100.0) + ((17.625 * ctemp)/(243.04 + ctemp)))
/(17.625 - log(hum/100.0) - ((17.625 * ctemp)/(243.04 + ctemp)));
dewPoint = dewPoint * 9.0/5.0 + 32.0;
}
}
return dewPoint;
}