Skip to content
Snippets Groups Projects
model.py 4.04 KiB
Newer Older
from collections import OrderedDict
from datetime import datetime, timedelta

import rrdtool
import numpy as np

from .time import to_unix_timestamp
from .wind import mean_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

    def initialize(self, start=None):
        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 get_slice(self, start, end, names=None, average=5):

        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] = mean_wind_vector_degrees(east, north)

        times = np.array([np.arange(start, end + average, average)])
        return np.concatenate((times.T, dst_data), axis=1)