-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathWeatherCalculations.cpp
169 lines (133 loc) · 7.8 KB
/
WeatherCalculations.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#include "WeatherCalculations.h"
Weather::Weather() {}
double Weather::tempCtoF(double Temperature) {
return (Temperature * 1.8) + 32.0;
};
double Weather::tempFtoC(double Temperature) {
return (Temperature - 32.0) / 1.8;
};
double Weather::getSeaLevelPressure(double AirPressure, double Altitude) {
return AirPressure / pow((1.0 - (Altitude / 44330.0)), 5.225);
};
double Weather::getAltitude(double AirPressure, double SeaLevelPressure) {
return 44330.0 * (1.0 - pow((AirPressure / SeaLevelPressure), (1.0 / 5.225)));
};
double Weather::getDewPoint(double Temperature, double Humidity) { //Temperature is in Celsius so make sure to convert Fahrenheit to Celsius!
// (1) Saturation Vapor Pressure = ESGG(T)
double RATIO = 373.15 / (273.15 + Temperature);
double RHS = -7.90298 * (RATIO - 1.0);
RHS += 5.02808 * log10(RATIO);
RHS += -1.3816e-7 * (pow(10.0, (11.344 * (1.0 - 1.0 / RATIO))) - 1.0);
RHS += 8.1328e-3 * (pow(10.0, (-3.49149 * (RATIO - 1.0))) - 1.0);
RHS += log10(1013.246);
// factor -3 is to adjust units - Vapor Pressure SVP * humidity
double VP = pow(10.0, RHS - 3.0) * Humidity;
// (2) DEWPOINT = F(Vapor Pressure)
double T = log(VP / 0.61078); // temp var
return (241.88 * T) / (17.558 - T);
};
double Weather::getHeatIndex(double Temperature, double Humidity) {
Temperature = tempCtoF(Temperature);
double Adjustment = 0;
double HeatIndex = -42.379 + (2.04901523 * Temperature) + (10.14333127 * Humidity) + (-0.22475541 * Temperature * Humidity) + (-0.00683783 * Temperature * Temperature) + (-0.05481717 * Humidity * Humidity) + (0.00122874 * Temperature * Temperature * Humidity) + (0.00085282 * Temperature * Humidity * Humidity) + (-0.00000199 * Temperature * Temperature * Humidity * Humidity);
if (Humidity < 13.0 && Temperature > 80.0 && Temperature < 112.0) {
Adjustment = ((13.0 - Humidity) * 0.25) * sqrt((17.0 - fabs(Temperature - 95.0)) / 17.0);
HeatIndex -= Adjustment;
} else if (Humidity > 85.0 && Temperature > 80.0 && Temperature < 87.0) {
Adjustment = ((Humidity - 85.0) * 0.1) * ((87.0 - Temperature) * 0.2);
HeatIndex += Adjustment;
} else if (HeatIndex < 80.0) {
HeatIndex = 0.5 * (Temperature + 61.0 + ((Temperature - 68.0) * 1.2) + (Humidity * 0.094));
}
return tempFtoC(HeatIndex);
};
double Weather::getHumidex(double Temperature, double DewPoint) {
return Temperature + (-0.5555 * ((6.11 * pow(EULER, 5417.753 * ((1.0 / 273.15) - (1.0 / (273.15 + DewPoint))))) - 10.0));
};
double Weather::getWindChill(double Temperature, double WindSpeed) {
Temperature = tempCtoF(Temperature);
WindSpeed *= 0.621371; // Converts wind speed from km/h to mph
double WindChill = 0;
if (Temperature < 50.0 && WindSpeed > 3.0) {
WindChill = tempFtoC(35.74 + (0.6215 * Temperature) - (35.75 * pow(WindSpeed, 0.16)) + (0.4275 * Temperature * pow(WindSpeed, 0.16)));
} else {
WindChill = tempFtoC(Temperature);
}
// WindChill = (13.12 + 0.6215 * Temperature - 11.37 * pow(WindSpeed, 0.16) + 0.3965 * Temperature * pow(WindSpeed, 0.16));
return WindChill;
};
Weather::Comfort Weather::getComfort(double heatIndex) {
if (heatIndex <= 23.0) return uncomfortable; //Uncomfortable
else if (heatIndex <= 26.0) return comfortable; //Comfortable
else if (heatIndex <= 29.0) return small_discomfort; //Some discomfort
else if (heatIndex <= 39.0) return medium_discomfort; //Hot feeling
else if (heatIndex <= 45.0) return great_discomfort; //Great discomfort; avoid exertion
else return dangerous; //Dangerous; probable heat stroke
}
/**
* Calculates the Air Quality Index (AQI) based on the PM2.5 and PM10 values.
* @param PM25 The PM2.5 value.
* @param PM10 The PM10 value.
* @return The calculated AQI value.
*/
uint16_t Weather::getAQI(uint16_t PM25, uint16_t PM10) {
uint16_t AQI_25 = 0, AQI_10 = 0;
//Calculate AQI for PM2.5
if (PM25 >= 0 && PM25 <= 12) AQI_25 = map(PM25, 0, 12, 0, 50);
else if (PM25 >= 13 && PM25 <= 35) AQI_25 = map(PM25, 13, 35, 51, 100);
else if (PM25 >= 36 && PM25 <= 55) AQI_25 = map(PM25, 36, 55, 101, 150);
else if (PM25 >= 56 && PM25 <= 150) AQI_25 = map(PM25, 56, 150, 151, 200);
else if (PM25 >= 151 && PM25 <= 250) AQI_25 = map(PM25, 151, 250, 201, 300);
else if (PM25 >= 251 && PM25 <= 500) AQI_25 = map(PM25, 251, 500, 301, 500);
//Calculate AQI for PM10
if (PM10 >= 0 && PM10 <= 54) AQI_10 = map(PM10, 0, 54, 0, 50);
else if (PM10 >= 55 && PM10 <= 154) AQI_10 = map(PM10, 55, 154, 51, 100);
else if (PM10 >= 155 && PM10 <= 254) AQI_10 = map(PM10, 155, 254, 101, 150);
else if (PM10 >= 255 && PM10 <= 354) AQI_10 = map(PM10, 255, 354, 151, 200);
else if (PM10 >= 355 && PM10 <= 424) AQI_10 = map(PM10, 355, 424, 201, 300);
else if (PM10 >= 425 && PM10 <= 604) AQI_10 = map(PM10, 425, 604, 301, 500);
//Return the highest AQI value
return max(AQI_25, AQI_10);
}
uint8_t Weather::getForecastSeverity(double currentPressure, const uint8_t month, WindDirection windDirection, const PressureTrend pressureTrend, const boolean hemisphere, const double highestPressureEverRecorded, const double lowestPressureEverRecorded) {
double pressureRange = highestPressureEverRecorded - lowestPressureEverRecorded;
double constant = (pressureRange / 22.0);
boolean summer = false;
if (hemisphere == true && month >= 4 && month <= 9) summer = true; // true if 'Summer'
if (hemisphere == false && month < 4 && month > 9) summer = true; // true if 'Summer'
if (hemisphere == false) { //South hemisphere
windDirection = static_cast<WindDirection>((windDirection + 8) % 16); // Adjust wind direction for Southern Hemisphere, basically creates a circle of enums and rotates it by 180 degrees and removes the period of 16 (since there are 16 enums, not considering calm (NOW) wind situation)
}
if (WindCorrectionFactors.find(windDirection) != WindCorrectionFactors.end()) {
currentPressure += WindCorrectionFactors[windDirection] * pressureRange;
}
if (summer == 1) { // if Summer
if (pressureTrend == rising) { // rising
currentPressure += 0.07 * pressureRange;
} else if (pressureTrend == falling) { // falling
currentPressure -= 0.07 * pressureRange;
}
}
if (currentPressure >= highestPressureEverRecorded) currentPressure = highestPressureEverRecorded - 1;
uint8_t forecastOption = floor((currentPressure - lowestPressureEverRecorded) / constant);
forecastOption = constrain(forecastOption, 0, 21);
uint8_t outputForecast;
if (pressureTrend == rising) { // rising
outputForecast = rise_options[forecastOption];
} else if (pressureTrend == falling) { // falling
outputForecast = fall_options[forecastOption];
} else {if (pressureTrend == steady) // must be 'steady'
outputForecast = steady_options[forecastOption];
}
return outputForecast;
}
char* Weather::getForecast(double currentPressure, const uint8_t month, WindDirection windDirection, PressureTrend pressureTrend, const boolean hemisphere, const double highestPressureEverRecorded, const double lowestPressureEverRecorded) {
static char outputForecast[57];
strcpy(outputForecast, ""); // Initialise an empty char array
uint8_t forecastOption = getForecastSeverity(currentPressure, month, windDirection, pressureTrend, hemisphere, highestPressureEverRecorded, lowestPressureEverRecorded);
// if (forecastOption == 0 || forecastOption == 21) { //Problem here is that this always works for steady weather which is option 0, but was intended to be activated only when I have lower than 0 or higher than 21, but I constain it in getForecastSeverity function
// strcpy(outputForecast, "Exceptional Weather, ");
// }
strcat(outputForecast, forecast[forecastOption]);
return outputForecast;
}