#! /usr/bin/python3
#
# Plots data logged by the 
# Humidity / Temperature Data Logger PCE-HT110
#
# Device Configuration:
#  - point as decimal separator
#  - temperature unit: °C (search for 'DEGREE C' in code)
#
# Some labels and texts in the script are in German.
#
# Needs python3 packages from PyPI or distribution:
#
# Python      Debian
# ------------------------------
# dateutil:   python3-dateutil
# matplotlib: python3-matplotlib
# numpy:      python3-numpy
#
import csv, locale, argparse
from dateutil.parser import parse
import matplotlib.pyplot as plt, matplotlib.dates as mdates
import numpy as np

def absolute_humidity(rh, T):
    return (6.112*np.exp((17.67*T)/(T+243.5))*rh*2.1674)/(273.15+T)

def HT100_Reader(fname):
    header = ['Position', 'Date', 'Time',
              'Ch1_Value', 'Ch1_Unit', 'Ch2_Value', 'Ch2_unit']

    with open(fname,'r') as fd:
        reader = csv.reader(fd, dialect='excel-tab')
        assert next(reader) == header
        data = []
        for r in reader:
            assert r[4].strip() == '%RH' and r[6].strip() == 'DEGREE C'
            data.append((
                int(r[0]),
                parse(r[1]+' '+r[2]),
                float(r[3]),
                float(r[5]),
                ))
    return np.rec.array(data, dtype=[
        ('nr',int),
        ('time','O'),
        ('humidity',float),
        ('temperature',float),
        ])


class Plotter:

    @staticmethod
    def quant(a, v, n):
        return a.set_ylim(n*np.floor(v.min()/n), n*np.ceil(v.max()/n))

    @staticmethod
    def make_format(current, other):
        # current and other are axes
        def format_coord(x, y):
            # x, y are data coordinates
            # convert to display coords
            display_coord = current.transData.transform((x,y))
            inv = other.transData.inverted()
            # convert back to data coords with respect to ax
            x1, y1 = inv.transform(display_coord)
            xs = mdates.DateFormatter('%d.%m.%Y %H:%M:%S')(x)
            y2s = locale.format('%.1f', y)
            y1s = locale.format('%.1f', y1)
            return '{}: {} / {}'.format(xs, y1s, y2s)
        return format_coord

    def __init__(self, data, axes, marker):
        fig, ax1 = plt.subplots(figsize=(10, 6))
        fig.autofmt_xdate()

        # Feuchte
        if axes == 'trh':
            h = data.humidity
            l1 = 'Feuchte %'
            q1 = 10
        else:
            h = absolute_humidity(data.humidity, data.temperature)
            l1 = 'Feuchte g/m³'
            q1 = 5
        if marker:
            h_marker = '-b1'
        else:
            h_marker = '-b'
        p1, = ax1.plot(data.time, h, h_marker, linewidth=0.2, label=l1)
        color = p1.get_color()
        ax1.set_ylabel(l1)
        ax1.yaxis.label.set_color(color)
        ax1.tick_params(axis='y', colors=color)
        ax1.ticklabel_format(axis='y',useLocale=True)
        self.quant(ax1, h, q1)

        # Temperatur
        ax2 = ax1.twinx()
        if axes in ('trh', 'tah'):
            t = data.temperature
            l2 = 'Temperatur'
            q2 = 10
        else:
            t = data.humidity
            l2 = 'Feuchte %'
            q2 = 10
        if marker:
            t_marker = '-r2'
        else:
            t_marker = '-r'
        p2, = ax2.plot(data.time, t, t_marker, linewidth=0.2, label=l2)
        color = p2.get_color()
        ax2.set_ylabel(l2)
        ax2.yaxis.label.set_color(color)
        ax2.tick_params(axis='y', colors=color)
        ax1.ticklabel_format(axis='y',useLocale=True)
        self.quant(ax2, t, q2)

        # Legende und interaktive Koordinaten-Anzeige
        ax2.format_coord = self.make_format(ax2, ax1)
        ax2.xaxis.set_major_formatter(mdates.DateFormatter('%d.%m %H:%M'))
        lines = [p1, p2]
        ax1.legend(lines, [l.get_label() for l in lines])


def main():
    locale.setlocale(locale.LC_ALL, '')
    parser = argparse.ArgumentParser(description='Messdaten-Plotter für HT100')
    parser.add_argument('datei', help='"XLS"-Datei von der SD-Karte')
    parser.add_argument(
        '-a','--axes', choices=('trh','tah','rhah'), default='trh',
        help=("trh, tah oder rhah (t für Temperatur, rh relative und"
              " ah absolute Luftfeuchtigkeit"))
    parser.add_argument(
        '-m','--marker', action='store_true', help='Zeige Messpunkt-Marker')
    args = parser.parse_args()
    p = Plotter(HT100_Reader(args.datei), args.axes, args.marker)
    plt.show()

if __name__ == '__main__':
    main()
