/* jawsd - connects to AWS datalogger over serial connection and provides
 *         weather information to the network
 * Copyright (C) 2003-2004 Jeffrey Grafton <jgrafton@tjhsst.edu>
 *
 * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. 
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <netinet/in.h>

#include "config.h"
#include "data_types.h"
#include "process.h"

void raw_to_direction (unsigned char raw, char *direction)
{
	if (raw <= 8 || raw > 248)
		strcpy(direction, "N");
	else if (raw > 8 && raw <= 24) 
		strcpy(direction, "NNE");
	else if (raw > 24 && raw <= 40)
		strcpy(direction, "NE");
	else if (raw > 40 && raw <= 56)
		strcpy(direction, "ENE");
	else if (raw > 56 && raw <= 72)
		strcpy(direction, "E");
	else if (raw > 72 && raw <= 88)
		strcpy(direction, "ESE");
	else if (raw > 88 && raw <= 104)
		strcpy(direction, "SE");
	else if (raw > 104 && raw <= 120)
		strcpy(direction, "SSE");
	else if (raw > 120 && raw <= 136)
		strcpy(direction, "S");
	else if (raw > 136 && raw <= 152)
		strcpy(direction, "SSW");
	else if (raw > 152 && raw <= 168)
		strcpy(direction, "SW");
	else if (raw > 168 && raw <= 184)
		strcpy(direction, "WSW");
	else if (raw > 184 && raw <= 200)
		strcpy(direction, "W");
	else if (raw > 200 && raw <= 216)
		strcpy(direction, "WNW");
	else if (raw > 216 && raw <= 232)
		strcpy(direction, "NW");
	else if (raw > 232 && raw <= 248)
		strcpy(direction, "NNW");
}

double jround (double in, double exp)
{
    double power;
    power = pow(10.0, exp);
    if (in < 0.0)
        return ceil(power * in - 0.5) / power;
    else
        return floor(power * in + 0.5) / power;
}

void process_data (curobs_raw *curobs, hilo_raw *hilo, data_processed *processed)
{
	struct tm raw_time;

	raw_time.tm_sec = curobs->second;
	raw_time.tm_min = curobs->minute;
	raw_time.tm_hour = curobs->hour;
	raw_time.tm_mday = curobs->day;
	raw_time.tm_mon = curobs->month - 1;
	raw_time.tm_year = 100 + curobs->year;
	
	processed->datetime = mktime(&raw_time);
	
	process_temperature(curobs->indoor_temperature, curobs->indoor_temperature_rate, &hilo->indoor_temperature, &processed->indoor_temperature);
	process_temperature(curobs->outdoor_temperature, curobs->outdoor_temperature_rate, &hilo->outdoor_temperature, &processed->outdoor_temperature);
	process_temperature(curobs->auxiliary_temperature, curobs->auxiliary_temperature_rate, &hilo->auxiliary_temperature, &processed->auxiliary_temperature);

	process_humidity(curobs->humidity, curobs->humidity_rate, &hilo->humidity, &processed->humidity);

	process_barometer(curobs->barometer, curobs->barometer_rate, &hilo->barometer, &processed->barometer);

	process_light(curobs->light, curobs->light_rate, &hilo->light, &processed->light);
	
	process_wind(curobs, hilo, &processed->wind);
	
	process_rain(curobs, hilo, &processed->rain);

	/* wind chill */
	if (processed->outdoor_temperature.current < 50.0 && processed->outdoor_temperature.current > -50.0)
	{
		if (processed->wind.average_speed > 3.0 && processed->wind.average_speed < 110.0)
			processed->wind.average_wind_chill = jround((35.74 + 0.6215 * processed->outdoor_temperature.current + (0.4275 * processed->outdoor_temperature.current - 35.75) * pow(processed->wind.average_speed, 0.16)), 1.0);
		else
			processed->wind.average_wind_chill = processed->outdoor_temperature.current;

		if (processed->wind.current_speed > 3.0 && processed->wind.current_speed < 110.0)
			processed->wind.current_wind_chill = jround((35.74 + 0.6215 * processed->outdoor_temperature.current + (0.4275 * processed->outdoor_temperature.current - 35.75) * pow(processed->wind.current_speed, 0.16)), 1.0);
		else
			processed->wind.current_wind_chill = processed->outdoor_temperature.current;
	}
	else
	{
		processed->wind.average_wind_chill = processed->outdoor_temperature.current;
		processed->wind.current_wind_chill = processed->outdoor_temperature.current;
	}

	/* dew point */
	process_dew_point(processed);
}

void process_dew_point (data_processed *processed)
{
	double dpd, x, t;
	
	t = (processed->outdoor_temperature.current - 32) * 5.0 / 9.0;
	x = (1 - 0.01 * processed->humidity.current);
	dpd = (14.55 + 0.114 * t) * x + pow(((2.5 + 0.007 * t) * x), 3) + ((15.9 + 0.117 * t) * pow(x, 14));

	processed->dew_point = jround(((9.0 / 5.0) * (t - dpd)) + 32, 1.0);
}

void process_temperature (unsigned short curobs_raw, unsigned short rate_raw, struct hilo_raw_pack *hilo_raw, struct processed_pack *processed)
{
	/* y = (1/120 + 0.000098)x - 15.0 */
	processed->current = jround((0.0084314 * (short)ntohs(curobs_raw) - 15), 1.0);
	processed->high = jround((0.0084314 * (short)ntohs(hilo_raw->high) - 15), 1.0);
	processed->low = jround((0.0084314 * (short)ntohs(hilo_raw->low) - 15), 1.0);
	processed->rate = jround((0.0084314 * (short)ntohs(rate_raw)), 1.0);
	
	processed->high_hour = hilo_raw->high_hour;
	processed->high_minute = hilo_raw->high_minute;
	processed->low_hour = hilo_raw->low_hour;
	processed->low_minute = hilo_raw->low_minute;
}

void process_humidity (unsigned short curobs_raw, unsigned short rate_raw, struct hilo_raw_pack *hilo_raw, struct processed_pack *processed)
{
	processed->current = ((curobs_raw < 2756) ? 5.0 : jround((0.01081609 * (short)ntohs(curobs_raw) - 24.758), 1.0));
	processed->high = ((curobs_raw < 2756) ? 5.0 : jround((0.01081609 * (short)ntohs(hilo_raw->high) - 24.758), 1.0));
	processed->low = ((curobs_raw < 2756) ? 5.0 : jround((0.01081609 * (short)ntohs(hilo_raw->low) - 24.758), 1.0));
	processed->rate = jround((0.01081609 * (short)ntohs(rate_raw)), 1.0);
	
	processed->high_hour = hilo_raw->high_hour;
	processed->high_minute = hilo_raw->high_minute;
	processed->low_hour = hilo_raw->low_hour;
	processed->low_minute = hilo_raw->low_minute;
}

void process_barometer (unsigned short curobs_raw, unsigned short rate_raw, struct hilo_raw_pack *hilo_raw, struct processed_pack *processed)
{
	/* y = (1/3800 - .00000172)x + 27.999999999999 */
	processed->current = jround((0.0002614378947 * (short)ntohs(curobs_raw) + 27.999999999999999), 2.0);
	processed->high = jround((0.0002614378947 * (short)ntohs(hilo_raw->high) + 27.999999999999999), 2.0);
	processed->low = jround((0.0002614378947 * (short)ntohs(hilo_raw->low) + 27.999999999999999), 2.0);
	processed->rate = jround((0.0002614378947 * (short)ntohs(rate_raw)), 2.0);
	
	processed->high_hour = hilo_raw->high_hour;
	processed->high_minute = hilo_raw->high_minute;
	processed->low_hour = hilo_raw->low_hour;
	processed->low_minute = hilo_raw->low_minute;
}

void process_light (unsigned short curobs_raw, unsigned short rate_raw, struct hilo_raw_pack *hilo_raw, struct processed_pack *processed)
{
	/* y = (1/160 + .000286)x */
	processed->current = jround((0.006536 * (short)ntohs(curobs_raw)), 1.0);
	processed->high = jround((0.006536 * (short)ntohs(hilo_raw->high)), 1.0);
	processed->low = jround((0.006536 * (short)ntohs(hilo_raw->low)), 1.0);
	processed->rate = jround((0.006536 * (short)ntohs(rate_raw)), 1.0);
	
	processed->high_hour = hilo_raw->high_hour;
	processed->high_minute = hilo_raw->high_minute;
	processed->low_hour = hilo_raw->low_hour;
	processed->low_minute = hilo_raw->low_minute;
}

void process_wind (curobs_raw *curobs, hilo_raw *hilo, struct processed_wind_pack *processed)
{
	/* y = 55.9/255x */
	processed->current_speed = jround(((curobs->wind_speed_supplement * 256 + curobs->wind_speed) * 55.9/255), 1.0);
	raw_to_direction(curobs->wind_direction, processed->current_direction);
   processed->current_direction_degrees = jround(curobs->wind_direction * 1.40625, 2.0);
	
	processed->average_speed = jround(((curobs->average_wind_speed_supplement * 256 + curobs->average_wind_speed) * 55.9/255), 1.0);
	raw_to_direction(curobs->average_wind_direction, processed->average_direction);
   processed->average_direction_degrees = jround(curobs->average_wind_direction * 1.40625, 2.0);
	
	processed->high_speed = jround(((hilo->wind_speed_high_supplement * 256 + hilo->wind_speed_high) * 55.9/255), 1.0);
	raw_to_direction(hilo->wind_direction_high, processed->high_direction);
   processed->high_direction_degrees = jround(hilo->wind_direction_high * 1.40625, 2.0);
	
	processed->high_hour = hilo->wind_speed_high_hour;
	processed->high_minute = hilo->wind_speed_high_minute;
}

void process_rain (curobs_raw *curobs, hilo_raw *hilo, struct processed_rain_pack *processed)
{
	/* y = 1/100x */
	processed->daily = ntohs(curobs->daily_rain) / 100.0;
	processed->monthly = ntohs(hilo->monthly_rain) / 100.0;
	processed->yearly = ntohs(hilo->yearly_rain) / 100.0;
	
	/* y = 0.04x */
	processed->rate = (0.04 * curobs->rainfall_rate);
	
	processed->rate_high = (0.04 * hilo->rainfall_rate_high);
	processed->rate_high_hour = hilo->rainfall_rate_high_hour;
	processed->rate_high_minute = hilo->rainfall_rate_high_minute;
}

