Source file: /~heha/basteln/PC/Wetterstation/unwetter.zip/unwetter.cpp

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <getopt.h>
#include <fcntl.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <locale.h>

#include <iostream>
#include <iomanip>
#include <set>
#include <string>
#include <sstream>

/* global variables */
static int verbose = 0;
static int server_port = 8783; // "WS" - Wetterstation

const char* default_device = "/dev/ttyS0"; /* = COM1 */
// Status des seriellen Ports wird gespeichert
struct termios saved_settings;

/* Mindest-Abstand zwischen zwei Paketen -> Wenn Daten in groesserem
 * Abstand eintreffen, wird angenommen, dass sie zu verschiedenen Paketen
 * gehoeren.
 */
static const struct timeval packet_gap = { 5, 0 };
// Paketfrequenz von der Anlage
static const struct timeval packet_frequency = { 10, 0 };

// Wie oft (=wie lange) soll versucht werden, Pakete von der Station
// zu empfangen? (Nur bei (Re-)Synchronisierung.)
const int synchronize_retry_count = 50;

// Wir definieren fuer das Protokoll: Neue Zeile durch \n.
const std::string new_line = "\n";
const std::string next_str = "NEXT:NEXT";

static void display_help ();

static int prepare_server_socket (int sockfd);

static int run_server (int listen_fd, int serial_fd, unsigned char* first_packet);

static int open_serial_port (unsigned char* first_packet);

static int synchronize (int port_fd, unsigned char* received_packet);

static std::string evaluate_packet (unsigned char* packet);

static std::string get_current_time ();

int main (int argc, char **argv)
{
    int c;  /* return value from getopt_long */
    int *longindex = NULL;
    int listen_sockfd, serial_fd;

    /* parse command line options */
    c = EOF+1;

    while (c != EOF)
    {

	static struct option cmd_options[] = {
	    {"--help", 0, NULL, 'h' },
	    {"--verbose", 0, NULL, 'v' },
	    {"--port", 1, NULL, 'p' }
	};

	c = getopt_long (argc, argv, "hvp:", cmd_options, longindex);

	switch (c) {
	    case 'h':
		display_help ();
		exit (0);
		break;
	    case 'v':
		verbose++;
		break;
	    case 'p':
	    {
		char* endptr = 0L;

		if (strlen (optarg) <= 0)
		{
		    std::cerr << "Fehlendes Argument für -p" << std::endl;
		    display_help ();
		    exit (1);
		}

		/* convert port to number */
		server_port = strtol (optarg, &endptr, 10);

		if ((endptr == 0L) || (*endptr != 0L))
		{
		    std::cerr << "Ungültige Portnummer: " << optarg << std::endl;
		    display_help ();
		    exit (1);
		}
		break;
	    }
	    case '?':
	    case ':':
		display_help ();
		exit (1);
		break;
	}

    }	/* end of option parsing loop */

    if (optind < argc)
    {
	std::cerr << "Zu viele Parameter." << std::endl;
	exit (1);
    }

    /* open socket, Internet, TCP */
    listen_sockfd = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);

    if (listen_sockfd == -1)
    {
	std::cerr << "Fehler beim Socket Erzeugen: " << strerror (errno) << std::endl;
	return 1;
    }
    else
    {
	/* ease debugging: allow reusage of same address */
	int x = 1;

	if (setsockopt (listen_sockfd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof (x)) == -1)
	{
	    std::cerr << "setsockopt (...SO_REUSEADDR...) fehlgeschlagen: " << strerror (errno) << std::endl;
	    return 1;
	}

    }

    if (prepare_server_socket (listen_sockfd) != 0)
    {
	return 1;
    }

    // open serial port etc.
    unsigned char first_packet[32];

    if ((serial_fd = open_serial_port (first_packet)) < 0)
    {
	std::cerr << "Fehler beim Öffnen des seriellen Ports." << std::endl;
	return 1;
    }

    // Signale blockieren: SIGPIPE - wir bekommen das mit, wenn wir versuchen,
    // auf die Sockets zu schreiben und brauchen uns deshalb nicht auf das
    // gefaehrliche Gebiet der Signal-handler zu wagen.
    sigset_t sigs;

    sigemptyset (&sigs);
    sigaddset (&sigs, SIGPIPE);

    sigprocmask (SIG_BLOCK, &sigs, NULL);

    run_server (listen_sockfd, serial_fd, first_packet);
}
// end main()


static void display_help ()
{
    std::cerr <<
"Syntax: l_tenki_d [-d] [-p port]  [-v]"	<< std::endl <<
						   std::endl <<
"Optionen:"					<< std::endl <<
"-d, --daemon    Daemon-Modus"			<< std::endl <<
"-p, --port      Zu verwendenden Port setzen"	<< std::endl <<
"-v, --verbose   Mehr erzählen"			<< std::endl <<
"-h, --help      Diese Hilfe"			<< std::endl;

}


static int prepare_server_socket (int sockfd)
/* sockfd is an TCP/IP-Socket ready for bind()ing */
{
    struct sockaddr_in localaddr;

    if (verbose)
    {
	std::cerr << "Schalte non-blocking I/O ein" << std::endl;
    }

    if (fcntl (sockfd, F_SETFL, fcntl (sockfd, F_GETFL, 0) | O_NONBLOCK) != 0)
    {
	std::cerr << "Fehler beim Aktivieren der non-blocking I/O auf Server Socket: " << strerror (errno) << std::endl;
	return 0;
    }

    if (verbose)
    {
	std::cerr << "bind() auf Server Socket" << std::endl;
    }

    localaddr.sin_family = AF_INET;
    localaddr.sin_port = htons (server_port);
    // wir lauschen ueberall
    localaddr.sin_addr.s_addr = htonl (INADDR_ANY);

    if (bind (sockfd, (struct sockaddr *) &localaddr, sizeof (localaddr)) != 0)
    {
	std::cerr << "Error bei bind(): " << strerror (errno) << std::endl;
	return 1;
    }

    return 0;
}

static void my_perror (const char *fmt,...) {
    // sollte reichen, auch fuer grosse Fehlermeldungen
    char error_message[2048];
    // erstmal die Fehlermeldung abholen
    va_list arguments;
    va_start (arguments, fmt);
    vsnprintf(error_message,2048,fmt,arguments);
    va_end (arguments);
    std::cerr << error_message << strerror (errno) << std::endl;
}

static int open_serial_port (unsigned char *first_packet)
{
    if (verbose)
    {
	std::cerr << "Öffne seriellen Port" << std::endl;
    }

    // Schnittstelle oeffnen: lesen+schreiben, wird nicht unser
    // "controlling tty", nicht-blockierend
    int port_fd = open (default_device, O_RDWR | O_NOCTTY | O_NONBLOCK);

    if (port_fd == -1)
    {
	my_perror ("Fehler beim Oeffnen des seriellen Ports");
	return port_fd;
    }

    // Platz fuer Einstellungen des seriellen Ports
    struct termios our_settings;

    // aktuelle Einstellungen des Ports abfragen
    if (tcgetattr (port_fd, &saved_settings) != 0)
    {
	my_perror ("Fehler beim Abfragen der Portparameter");
	return -1;
    }

    // Einstellungen kopieren
    our_settings = saved_settings;

    // Eingabe-Geschwindigkeit setzen
    cfsetispeed (&our_settings, B2400);
    // Ausgabe-Geschwindigkeit setzen (ist das notwendig?)
    cfsetospeed (&our_settings, B2400);
    // raw-Modus anschalten -> 8Bit, keine Paritaet, 1 Stopbit, keine
    // Flusskontrolle, keine Verarbeitung spezieller Zeichen
    cfmakeraw (&our_settings);
    // Einstellungen uebernehmen, jetzt!
    if (tcsetattr (port_fd, TCSANOW, &our_settings) != 0)
    {
	my_perror ("Fehler beim Setzen der Portparameter");
	return -1;
    }

    // jetzt wollen wir noch den DTR anmachen
    int port_status;

    if (ioctl (port_fd, TIOCMGET, &port_status) != 0)
    {
	my_perror ("Fehler beim Abfragen des Portstatus");
	return -1;
    }
    // DTR setzen, falls noch nicht gesetzt
    if ((port_status | TIOCM_DTR) == 0)
    {
	// FIXME: Geht nicht, ich bekomme "bad address"
	// zum Glueck ist DTR an...
	if (ioctl (port_fd, TIOCMSET, port_status|TIOCM_DTR) != 0)
	{
	    my_perror ("Fehler beim Aktivieren von DTR");
	    //cerr << hex << "alter Status: " << port_status << ", neuer: " << (port_status | TIOCM_DTR) << endl;
	    return -1;
	}
    }

    synchronize (port_fd, first_packet);

    return port_fd;
}


static int synchronize (int port_fd, unsigned char* received_packet)
// received_packet muss Platz fuer 32 Byte haben!
{
    if (verbose)
    {
	std::cerr << get_current_time () << ": Synchronisiere..." << std::endl;
    }

    bool synchronized = false;
    int retry_count = synchronize_retry_count;

    fd_set orgreadset;
    FD_ZERO (&orgreadset);
    FD_SET (port_fd, &orgreadset);

    if (tcflush (port_fd, TCIFLUSH) != 0)
    {
	my_perror ("tcflush() fehlgeschlagen: ");
	return -1;
    }

    // Synchronisieren wir mal mit der Station:
    // 1. alles wegwerfen
    // 2. warten auf Daten
    // 3. Bis zu 1 Sekunde warten, dass 14 Byte vollwerden
    // 4. Wenn nicht erfolgreich, gehe zu 1.
    while (! synchronized)
    {
	fd_set readset = orgreadset;

	if (verbose > 1)
	{
	    std::cerr << get_current_time () << ": Warte auf Daten..." << std::endl;
	}

	struct timeval timeout = packet_frequency;
	// schlagen wir mal 1 Sekunde drauf.
	timeout.tv_sec++;
	// erstmal auf irgendwelche Daten warten
	int select_result = select (port_fd+1, &readset, NULL, NULL, &timeout);

	if (select_result == -1)
	{
	    my_perror ("Fehler beim Synchronisieren: ");
	    return -1;
	}

	if (select_result == 0)
	{
	    retry_count--;

	    if (retry_count == 0)
	    {
		std::cerr << "Fehler beim Synchronisieren mit der Station. Abbruch nach " << synchronize_retry_count << " Versuchen." << std::endl;
		return -1;
	    }

	    continue;
	}

	// struct timeval packet_timeout = packet_gap;

	timeout = packet_gap;

	int bytes_received = 0;

	bool packet_complete = false;

	if (verbose > 1)
	{
	    std::cerr << get_current_time () << ": neues Paket?" << std::endl;
	}

	while (! packet_complete)
	{
	    readset = orgreadset;

	    int select_result = select (port_fd+1, &readset, NULL, NULL, &timeout);

	    if (select_result == -1)
	    {
		my_perror ("Fehler beim Synchronisieren: ");
		return -1;
	    }

	    if (select_result == 0)
		break;

	    int read_result = read (port_fd, &received_packet[bytes_received], 32-bytes_received);

	    if (read_result == -1)
	    {
		my_perror ("Fehler beim Synchronisieren: ");
		return -1;
	    }

	    bytes_received += read_result;

	    if (verbose > 1)
	    {
		std::cerr << get_current_time () << ": " << read_result << " Bytes gelesen (insgesamt: " << bytes_received << ")." << std::endl;
	    }

	    if (bytes_received == 14)
		packet_complete = true;

	}

	synchronized = packet_complete;
    }

    if (verbose)
    {
	std::cerr << "Synchronisation erfolgreich." << std::endl;
    }

    return 0;
}


static int run_server (int server_fd, int serial_fd, unsigned char* first_packet)
{
    // auf eingehende Verbindungen warten
    if (verbose)
    {
	std::cerr << "Server bereit." << std::endl;
    }

    // eingehende Verbindungen werden am Socket listen_fd signalisiert durch
    // "readable" - siehe accept(2)
    if (listen (server_fd, 0) != 0)
    {
	std::cerr << "Fehler bei listen(): " << strerror (errno) << std::endl;
	return 1;
    }

    fd_set orgrset;
    int highest_fd = 0;

    FD_ZERO (&orgrset);
    FD_SET (server_fd, &orgrset);
    if (server_fd > highest_fd) highest_fd = server_fd;
    // wir warten auch auf Daten am seriellen Port
    FD_SET (serial_fd, &orgrset);
    if (serial_fd > highest_fd) highest_fd = serial_fd;

    std::set<int> connected_clients;

    std::string last_dataset;

    last_dataset = evaluate_packet (first_packet) + next_str + new_line;
    //last_dataset = "FEHLER:Noch keine Daten empfangen" + new_line + next_str + new_line;

    unsigned char incoming_packet[32];
    struct timeval packet_start_time, current_time;

    int invalid_packet_count = 0;
    int packet_pos = 0;

    while (1)
    {
	// select modifiziert readset dann um anzuzeigen, wo sich was tut
	fd_set readset = orgrset;

	// wir warten, bis irgendwas passiert:
	// - neue Daten
	// - neue Verbindung geht ein
	int select_result = select (highest_fd+1, &readset, NULL, NULL, NULL);

	if (verbose > 1)
	{
	    std::cerr << "select() lieferte " << select_result << std::endl;
	}

	if (select_result == -1)
	{
	    if (errno != EINTR)
	    {
		std::cerr << "Fehler bei select(): " << strerror (errno) << std::endl;
		return 1;
	    }

	    // FIXME: sollen wir bei EINTR (z.B. bei CTRL-C) stoppen oder ignorieren?
	    if (verbose)
	    {
		std::cerr << "Ende." << std::endl;
	    }

	    break;
	}

	// was auch immer da los war - wir probieren's nochmal
	if (select_result == 0)
	{
	    std::cerr << "select() lieferte 0" << std::endl;
	    continue;
	}

	// Was zum Lesen da?
	if (FD_ISSET (serial_fd, &readset))
	{
	    gettimeofday (&current_time, NULL);

	    if (packet_pos == 0) // neues Paket?
	    {
		// Startzeit des Pakets merken
		packet_start_time.tv_sec = current_time.tv_sec;
		packet_start_time.tv_usec = current_time.tv_usec;
	    }
	    else
	    {
		// Nachsehen, ob mehr als 9 Sekunden vergangen seit Paketstart
		long sekunden = current_time.tv_sec - packet_start_time.tv_sec;

		if (sekunden >= 9)
		{
		    std::cerr << get_current_time() << ": letztes Paket unvollstaendig (nur " << packet_pos << " Bytes empfangen)." << std::endl;
		    packet_pos = 0;
		    packet_start_time.tv_sec = current_time.tv_sec;
		    packet_start_time.tv_usec = current_time.tv_usec;
#if 0
		    invalid_packet_count++;

		    if (invalid_packet_count > 3)
		    {
			if (synchronize (serial_fd, incoming_packet) == 0)
			{
			    last_dataset = evaluate_packet (incoming_packet) + next_str + new_line;
			}

			invalid_packet_count = 0;
			continue;
		    }
#endif

		}

	    }

	    // wir lesen u.U. mehr als notwendig
	    ssize_t read_result = read (serial_fd, &incoming_packet[packet_pos], 32-packet_pos);

	    if (read_result == -1)
	    {
		std::cerr << "Fehler beim Lesen der Daten: " << strerror (errno) << std::endl;
		continue;
	    }

	    packet_pos += read_result;

	    if (verbose)
	    {
		std::cerr << get_current_time() << ": " << read_result << " Byte gelesen. Paketgroesse: " << packet_pos << std::endl;
	    }

	    if (packet_pos > 14)
	    {
		if (verbose > 1)
		    std::cerr << "Mehr als 14 Byte gelesen: insgesamt " << read_result + packet_pos << std::endl;

		tcflush (serial_fd, TCIFLUSH);
		// einfach alles wegwerfen, + 1 Sekunde warten
		// und da auch alles wegwerfen
		fd_set orgwait_readset;
		FD_ZERO (&orgwait_readset);
		FD_SET (serial_fd, &orgwait_readset);
		struct timeval timeout = packet_gap;

		int r = 1, bytes_skipped = packet_pos;

		while (r != 0)
		{
			fd_set wait_readset = orgwait_readset;
			r = select (highest_fd+1, &wait_readset, NULL, NULL, &timeout);
			if (r == -1)
			{
				std::cerr << "Fehler bei select(): " << strerror (errno) << std::endl;
				break;
			}

			if (r != 0)
			{
				char buf;
				bytes_skipped += read (serial_fd, &buf, 1);
			}

		}

		if (verbose)
		{
			std::cerr << bytes_skipped << " Bytes verworfen." << std::endl;
		}

		packet_pos = 0;
	    }

	    if (packet_pos == 14)
	    {
		invalid_packet_count = 0;
		packet_pos = 0;

		last_dataset = evaluate_packet (incoming_packet) + next_str + new_line;

		if (verbose > 2)
		{
		    std::cerr << "Neuer Datensatz:" << std::endl << last_dataset;
		}

		const char* last_dataset_buf = last_dataset.c_str();
		const int last_dataset_size = last_dataset.size();

		std::set<int> still_connected_clients;

		// alle Clients abklappern und Daten hinschicken
		for (std::set<int>::const_iterator it = connected_clients.begin (),
			end_it = connected_clients.end ();
		     it != end_it;
		     ++it)
		{
		    ssize_t write_result = write (*it, last_dataset_buf, last_dataset_size);

		    // wir ignorieren genau 2 Fehler: EAGAIN und EINTR
		    // FIXME: teilweise geschriebene Daten noch nachschieben?
		    // -> gibt Probleme: Zeugs kann sich stauen...
		    if ((write_result >= 0) || ((write_result == -1) && ((errno == EAGAIN) || (errno == EINTR))))
		    {
			if (verbose > 1)
			    std::cerr << "Schreiben auf FD " << *it << " erfolgreich: " << write_result << " Bytes" << std::endl;

			still_connected_clients.insert (*it);
		    }
		    else
		    {
			if (verbose > 1)
			{
			    std::cerr << "Schliesse FD " << *it << " wegen: " << strerror (errno) << std::endl;
			}

			// -> diesen FD schliessen
			close (*it);
		    } // Fehler beim Schreiben zu Client

		} // Clients mit Daten versorgen

		connected_clients = still_connected_clients;
	    } // Paket war vollstaendig

	} // neue Daten gelesen

	// neue Verbindungsanfrage?
	if (FD_ISSET (server_fd, &readset))
	{
	    struct sockaddr_in remoteaddr;
	    socklen_t remoteaddrlen = sizeof (remoteaddr);

	    // Verbindung annehmen -> neuer Socket
	    int new_fd = accept (server_fd, (sockaddr*)&remoteaddr, &remoteaddrlen);

	    if (new_fd < 0) // Fehler
	    {
		// EAGAIN und EWOULDBLOCK stufen wir mal als nicht-kritisch ein
		if ((errno != EAGAIN) && (errno != EWOULDBLOCK))
		{
		    if (verbose)
			std::cerr << "accept() lieferte: " << strerror (errno) << std::endl;
		}
		else
		{
		    std::cerr << "Fehler bei accept(): " << strerror (errno) << std::endl;
		    break;
		}

	    }
	    else
	    {
		// Verbindung ging gut
		if (verbose)
		{ // mal anzeigen, wer da ist
		    std::cerr << "Verbindungsanahme von " << inet_ntoa (remoteaddr.sin_addr) << ':' << ntohs (remoteaddr.sin_port) << " auf FD " << new_fd << std::endl;
		}

		if (fcntl (new_fd, F_SETFL, fcntl (new_fd, F_GETFL, 0) | O_NONBLOCK) != 0)
		{
		    std::cerr << "Fehler beim Setzen von non-blocking I/O: " << strerror (errno) << std::endl;
		}
		else
		{
		    connected_clients.insert (new_fd);

		    if (verbose)
		    {
			std::cerr << "Schicke letzten Datensatz nach FD " << new_fd << std::endl;
		    }

		    // einfach Daten rausschreiben und vergessen
		    // FIXME: evtl. noch Fehlerbehandlung?
		    if (write (new_fd, last_dataset.c_str(), last_dataset.size()) < 0)
		    {
			std::cerr << "Fehler beim Schreiben auf FD " << new_fd << ": " << strerror (errno) << std::endl;
			// wir ignorieren das einfach mal...
			// beim naechsten Schreiben fliegt der fd dann wieder weg
		    }

		}

	    }

	} // neue Verbindung angenommen

    } // while(1)

    // TODO: Aufraeumen
    return 0;
}

static std::string get_current_time ()
{
    struct timeval tv;

    gettimeofday (&tv, NULL);

    std::stringstream result;

    result << tv.tv_sec << "." << tv.tv_usec;
    return result.str();
}


extern const float kty[256], hyg[256], lux[256], baro[256];

static std::string evaluate_packet (unsigned char* incoming)
{
    std::stringstream ergebnis;
    int windgeschw;
    static int letzte_windgeschw = 0;
    int h,m,s;	// vom Zeit-Diagramm

    const static char *richtung[16]=
    {	//Norden=0°, Uhrzeiger-Richtung
	"Nord","Nord-Nordost","Nordost","Ost-Nordost",
	"Ost","Ost-Südost","Südost","Süd-Südost",
	"Süd","Süd-Südwest","Südwest","West-Südwest",
	"West","West-Nordwest","Nordwest","Nord-Nordwest"
    };

    // Windgeschwindigkeit vorberechnen:
    // Leider hinterläßt der Sender bei stehendem Flügelrad
    // den letzten gemessenen Wert konstant, deshalb hier Extrawurst braten
    windgeschw = (((int)incoming[9]) << 8) + incoming[10];

    if ((windgeschw == letzte_windgeschw) && (windgeschw < 0x30))
    {
	windgeschw=0;
    }

    letzte_windgeschw=windgeschw;

    //Uhr des Senders auswerten:
    if (incoming[12] & 0x80)
    {	//Stunden-Minuten-Sekunden
	h=incoming[11];
	m=incoming[12]&0x7F;
	s=incoming[13];
    }
    else
    {		    //Minuten-Sekunden-Hundertstel
	h=0;
	m=incoming[11];
	s=incoming[12];
    }

    // Ergebnisse ausgeben
    setlocale (LC_TIME, "de_DE");
    char timestr[256];
    time_t cur_time = time (0L);
    struct tm* local_time = localtime(&cur_time);
    strftime (timestr, 256, "%a, %d. %B, %X", local_time);

    ergebnis << "Uhrzeit (lokal): " << timestr << new_line;

    ios::fmtflags old_flags = cout.flags();
    ergebnis.setf (ios::fixed);

    ergebnis << "Regen (letzte Stunde) in mm/h:" << (int)incoming[1] << new_line;
    ergebnis << "Windgeschwindigkeit in m/s:" << setprecision (2) << windgeschw*0.0179 << new_line;
    ergebnis << "Windrichtung (Grad):";

    if (windgeschw != 0)
    {
	ergebnis << (int)incoming[2]*360/256 << " " << richtung[(incoming[2]+8) >> 4] << new_line;
    }
    else
    {
	ergebnis << "unbekannt (zu wenig Wind)" << new_line;
    }
    ergebnis << "Luftdruck in hPa:" << setprecision (0) << baro[incoming[3]] << new_line;
    ergebnis << "Radioaktivität in µSv/h:" << setprecision (2) << incoming[4]*(2.5/255) << new_line;
    ergebnis << "Lichtintensität in Lux:" << setprecision (0) << lux[incoming[5]] << new_line;

    ergebnis << "Luftfeuchte in %:" << setprecision (0) << hyg[incoming[6]] << new_line;

    ergebnis << "Temperatur in °C:" << setprecision (1) << kty[incoming[7]] << new_line;
    ergebnis << "Laufzeit:" << setw(2) << setfill ('0') << h << "." << m << ":" << s << new_line;

    ergebnis.flags (old_flags);

    return ergebnis.str();
}
// Tabellen-Variablen
const float kty[256] =	{
    -27.5, -27.0, -26.0, -25.5, -24.5, -24.0, -23.5, -22.5,
    -22.0, -21.5, -20.5, -20.0, -19.5, -18.5, -18.0, -17.5,
    -17.0, -16.0, -15.5, -15.0, -14.0, -13.5, -13.0, -12.5,
    -11.5, -11.0, -10.5, -9.5, -9.0, -8.5, -8.0, -7.5,
    -6.5, -6.0, -5.5, -5.0, -4.0, -3.5, -3.0, -2.5,
    -2.0, -1.0, -0.5, 0.0, 0.5, 1.0, 2.0, 2.5,
    3.0, 3.5, 4.0, 4.5, 5.5, 6.0, 6.5, 7.0,
    7.5, 8.0, 8.5, 9.5, 10.0, 10.5, 11.0, 11.5,
    12.0, 12.5, 13.0, 14.0, 14.5, 15.0, 15.5, 16.0,
    16.5, 17.0, 17.5, 18.0, 18.5, 19.0, 20.0, 20.5,
    21.0, 21.5, 22.0, 22.5, 23.0, 23.5, 24.0, 24.5,
    25.0, 25.5, 26.0, 26.5, 27.0, 27.5, 28.0, 28.5,
    29.5, 30.0, 30.5, 31.0, 31.5, 32.0, 32.5, 33.0,
    33.5, 34.0, 34.5, 35.0, 35.5, 36.0, 36.5, 37.0,
    37.5, 38.0, 38.5, 39.0, 39.5, 40.0, 40.5, 41.0,
    41.5, 42.0, 42.5, 43.0, 43.5, 43.5, 44.0, 44.5,
    45.0, 45.5, 46.0, 46.5, 47.0, 47.5, 48.0, 48.5,
    49.0, 49.5, 50.0, 50.5, 51.0, 51.5, 52.0, 52.5,
    53.0, 53.5, 53.5, 54.0, 54.5, 55.0, 55.5, 56.0,
    56.5, 57.0, 57.5, 58.0, 58.5, 59.0, 59.0, 59.5,
    60.0, 60.5, 61.0, 61.5, 62.0, 62.5, 63.0, 63.5,
    64.0, 64.0, 64.5, 65.0, 65.5, 66.0, 66.5, 67.0,
    67.5, 68.0, 68.0, 68.5, 69.0, 69.5, 70.0, 70.5,
    71.0, 71.0, 71.5, 72.0, 72.5, 73.0, 73.5, 74.0,
    74.5, 74.5, 75.0, 75.5, 76.0, 76.5, 77.0, 77.5,
    77.5, 78.0, 78.5, 79.0, 79.5, 80.0, 80.0, 80.5,
    81.0, 81.5, 82.0, 82.5, 82.5, 83.0, 83.5, 84.0,
    84.5, 85.0, 85.0, 85.5, 86.0, 86.5, 87.0, 87.0,
    87.5, 88.0, 88.5, 89.0, 89.5, 89.5, 90.0, 90.5,
    91.0, 91.5, 91.5, 92.0, 92.5, 93.0, 93.5, 93.5,
    94.0, 94.5, 95.0, 95.5, 95.5, 96.0, 96.5, 97.0,
    97.5, 97.5, 98.0, 98.5, 99.0, 99.0, 99.5, 100.0
};


const float hyg[256] = {
     100, 100, 100, 100, 100, 100, 100, 100,
     100, 100, 100, 100, 100, 100, 100, 100,
     100, 100, 100, 100, 100, 100, 100, 100,
     100, 100, 100, 100, 100, 100, 100, 100,
     100, 99, 99, 99, 98, 98, 98, 97,
     97, 96, 96, 96, 95, 95, 95, 94,
     94, 94, 93, 93, 92, 92, 92, 91,
     91, 91, 90, 90, 89, 89, 89, 88,
     88, 88, 87, 87, 86, 86, 86, 85,
     85, 85, 84, 84, 83, 83, 83, 82,
     82, 81, 81, 81, 80, 80, 79, 79,
     79, 78, 78, 77, 77, 77, 76, 76,
     75, 75, 75, 74, 74, 73, 73, 72,
     72, 72, 71, 71, 70, 70, 69, 69,
     69, 68, 68, 67, 67, 66, 66, 66,
     65, 65, 64, 64, 63, 63, 62, 62,
     62, 61, 61, 60, 60, 59, 59, 58,
     58, 57, 57, 57, 56, 56, 55, 55,
     54, 54, 53, 53, 52, 52, 51, 51,
     50, 50, 49, 49, 48, 48, 47, 47,
     46, 46, 45, 45, 44, 44, 43, 43,
     42, 42, 41, 41, 40, 40, 39, 39,
     38, 37, 37, 36, 36, 35, 35, 34,
     34, 33, 32, 32, 31, 31, 30, 30,
     29, 28, 28, 27, 27, 26, 25, 25,
     24, 24, 23, 22, 22, 21, 20, 20,
     19, 18, 18, 17, 16, 16, 15, 15,
     14, 13, 12, 12, 11, 10, 10, 9,
     8, 7, 7, 6, 5, 5, 4, 3,
     2, 1, 1, 0, 0, 0, 0, 0,
     0, 0, 0, 0, 0, 0, 0, 0,
     0, 0, 0, 0, 0, 0, 0, 0
};

const float lux[256] = {
      0, 0, 10, 30, 50, 80, 100, 100,
     100, 100, 110, 110, 110, 120, 130, 140,
     150, 160, 170, 180, 190, 200, 200, 220,
     230, 240, 250, 270, 290, 300, 330, 350,
     380, 400, 430, 450, 470, 490, 510, 530,
     550, 570, 590, 610, 630, 700, 750, 850,
     980, 1150, 1300, 1500, 1700, 1900, 2600, 3200,
     4100, 4500, 4700, 5200, 5700, 6400, 7300, 8000,
     8400, 8900, 9300, 9700, 10000, 10500, 10800, 11000,
     11200, 11400, 11700, 11900, 12300, 12400, 12600, 12800,
     13100, 13700, 14300, 14900, 15300, 15900, 16600, 17000,
     17500, 18000, 18500, 19000, 19600, 20100, 20800, 21400,
     22000, 22700, 23300, 23900, 24500, 25100, 25900, 26500,
    27000, 27600, 28200, 28800, 29400, 30000, 30600, 31200,
    31800, 32500, 33100, 33700, 34300, 35000, 35600, 36200,
    36800, 37500, 38100, 38700, 39300, 40000, 40500, 41000,
    41500, 42000, 42500, 43000, 43500, 44000, 44500, 45000,
    45500, 46000, 46500, 47000, 47500, 48000, 48500, 49000,
    49500, 50000, 50300, 50600, 50900, 51200, 51500, 51800,
    52100, 52400, 52700, 53000, 53300, 53600, 53900, 54200,
    54500, 54800, 55100, 55400, 55700, 56000, 56300, 56600,
    56900, 57200, 57500, 57800, 58100, 58400, 58700, 59000,
    59300, 59600, 59900, 60200, 60500, 60800, 61100, 61400,
    61700, 62000, 62300, 62600, 62900, 63200, 63500, 63800,
    64100, 64400, 64700, 65000, 65300, 65600, 65900, 66200,
    66500, 66800, 67100, 67400, 67700, 68000, 68300, 68600,
    68900, 69200, 69500, 69800, 70100, 70400, 70700, 71000,
    71300, 71600, 71600, 71900, 72200, 72500, 72800, 73100,
    73400, 73700, 74000, 74000, 74300, 74600, 74900, 75200,
    75500, 75800, 76100, 76400, 76700, 77000, 77300, 77600,
    77900, 78200, 78500, 78800, 79100, 79400, 79700, 80000,
    80300, 80600, 80900, 81200, 81500, 81800, 82100, 85000
};

const float baro[256] = {
     815, 816, 817, 818, 819, 820, 821, 822,
     823, 824, 825, 826, 827, 828, 829, 830,
     831, 832, 833, 834, 835, 836, 837, 838,
     839, 840, 841, 842, 843, 844, 845, 846,
     847, 848, 849, 850, 851, 852, 853, 854,
     855, 856, 857, 858, 859, 860, 861, 862,
     863, 864, 865, 866, 867, 868, 869, 870,
     871, 872, 873, 874, 875, 876, 877, 878,
     879, 880, 881, 882, 883, 884, 885, 886,
     887, 888, 889, 890, 891, 892, 893, 894,
     895, 896, 897, 898, 899, 900, 901, 902,
     903, 904, 905, 906, 907, 908, 909, 910,
     911, 912, 913, 914, 915, 916, 917, 918,
     919, 920, 921, 922, 923, 924, 925, 926,
     927, 928, 929, 930, 931, 932, 933, 934,
     935, 936, 937, 938, 939, 940, 941, 942,
     943, 944, 945, 946, 947, 948, 949, 950,
     951, 952, 953, 954, 955, 956, 957, 958,
     959, 960, 961, 962, 963, 964, 965, 966,
     967, 968, 969, 970, 971, 972, 973, 974,
     975, 976, 977, 978, 979, 980, 981, 982,
     983, 984, 985, 986, 987, 988, 989, 990,
     991, 992, 993, 994, 995, 996, 997, 998,
     999, 1000, 1001, 1002, 1003, 1004, 1005, 1006,
     1007, 1008, 1009, 1010, 1011, 1012, 1013, 1014,
     1015, 1016, 1017, 1018, 1019, 1020, 1021, 1022,
     1023, 1024, 1025, 1026, 1027, 1028, 1029, 1030,
     1031, 1032, 1033, 1034, 1035, 1036, 1037, 1038,
     1039, 1040, 1041, 1042, 1043, 1044, 1045, 1046,
     1047, 1048, 1049, 1050, 1051, 1052, 1053, 1054,
     1055, 1056, 1057, 1058, 1059, 1060, 1061, 1062,
     1063, 1064, 1065, 1066, 1067, 1068, 1069, 1070
};

/* vim:smartindent:ts=4
*/
Detected encoding: ANSI (CP1252)4
Wrong umlauts? -