import os from datetime import datetime, timedelta import rrdtool import numpy as np from .time import to_unix_timestamp from .wind import wind_vector_degrees def dewpoint(tempC, relhum): """ Algorithm from Tom Whittaker tempC is the temperature in degrees Celsius, relhum is the relative humidity as a percentage. :param tempC: temperature in celsius :param relhum: relative humidity as a percentage """ gasconst = 461.5 latheat = 2500800.0 dp = (1.0 / (1.0 / (273.15 + tempC) - gasconst * np.log((0.0 + relhum) / 100) / (latheat - tempC * 2397.5))) return np.minimum(dp - 273.15, tempC) class RrdModel(object): keys = ('air_temp', 'rh', 'dewpoint', 'wind_speed', 'winddir_north', 'winddir_east', 'pressure', 'precip', 'accum_precip', 'solar_flux', 'altimeter') def __init__(self, filepath): self._filepath = filepath self._averages = tuple() @property def averaging_intervals(self): """Lazy load averaging intervals from database. """ if not self._averages: averages = [] info = rrdtool.info(self._filepath) for key in info.keys(): if key.startswith('rra') and key.endswith('pdp_per_row'): averages.append(int(info[key]*info['step'])) averages.sort() self._averages = tuple(averages) return self._averages def initialize(self, start=None): assert not os.path.exists(self._filepath) start = start or datetime.now() - timedelta(days=365) secs = to_unix_timestamp(start) rrdtool.create(self._filepath, '--start={}'.format(secs), '--step=5', 'DS:air_temp:GAUGE:10:-40:50', 'DS:rh:GAUGE:10:0:100', 'DS:dewpoint:GAUGE:10:0:100', 'DS:wind_speed:GAUGE:10:0:100', 'DS:winddir_north:GAUGE:10:-100:100', 'DS:winddir_east:GAUGE:10:-100:100', 'DS:pressure:GAUGE:10:0:1100', 'DS:precip:GAUGE:10:0:100', 'DS:accum_precip:GAUGE:10:0:100', 'DS:solar_flux:GAUGE:10:0:1000', 'DS:altimeter:GAUGE:10:0:100', 'RRA:AVERAGE:0.5:1:6307200', 'RRA:AVERAGE:0.5:12:525600', 'RRA:AVERAGE:0.5:60:105120', 'RRA:AVERAGE:0.5:360:17520') def _print(self, record): stamp = record.get_stamp() values = ':'.join([str(record[k]) for k in self.keys]) values = '{:d}:{}'.format(to_unix_timestamp(stamp), values) print values def add_record(self, record): pass def get_slice(self, start, end, names=None, average=5): """Get a slice of data from the database. :param start: Start time as datetime :param end: Inclusive end time as datetime :param names: Names to query for, defaults to all available, see ``keys`` :param average: Averaging interval supported by the database, see ``averaging_intervals``. """ names = names or self.keys[:] if isinstance(start, datetime): start = to_unix_timestamp(start) if isinstance(end, datetime): end = to_unix_timestamp(end) # normalize request times to averaging interval start -= start % average end -= end % average range, columns, rawdata = rrdtool.fetch(self._filepath, 'AVERAGE', '-r {:d}'.format(average), '-s {:d}'.format(start), '-e {:d}'.format(end)) src_data = np.array(rawdata) dst_data = np.zeros((src_data.shape[0], len(names))) * float('nan') # get only the columns we're interested in for dst_idx, name in enumerate(names): if name in columns: dst_data[:,dst_idx] = src_data[:,columns.index(name)] # we compute dewpoint since it wasn't always available if name == 'dewpoint': temp = src_data[:,self.keys.index('air_temp')].astype(np.float64) rh = src_data[:,self.keys.index('rh')].astype(np.float64) dst_data[:,dst_idx] = dewpoint(temp, rh) # get the wind direction in degrees from the vector components elif name == 'wind_dir': east = src_data[:,self.keys.index('winddir_east')].astype(np.float64) north = src_data[:,self.keys.index('winddir_north')].astype(np.float64) dst_data[:,dst_idx] = wind_vector_degrees(east, north) times = np.array([np.arange(start, end + average, average)]) return np.concatenate((times.T, dst_data), axis=1)