/* 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; }