From 5c9b91de6a3b4312bcc4a1328e0dfbe29c17051a Mon Sep 17 00:00:00 2001 From: Philipp Klaus <klaus@physik.uni-frankfurt.de> Date: Wed, 31 May 2017 17:36:11 +0200 Subject: [PATCH] pt100 board: python package for users added --- .../pt100_board/__init__.py | 31 ++++++ .../pt100_board/pt100_csv_reader.py | 73 ++++++++++++++ .../pt100_board/pt100_logfile_reader.py | 97 +++++++++++++++++++ .../pt100_board/pt100_reader.py | 81 ++++++++++++++++ sensors/pt100-board-python-package/setup.py | 21 ++++ 5 files changed, 303 insertions(+) create mode 100755 sensors/pt100-board-python-package/pt100_board/__init__.py create mode 100755 sensors/pt100-board-python-package/pt100_board/pt100_csv_reader.py create mode 100755 sensors/pt100-board-python-package/pt100_board/pt100_logfile_reader.py create mode 100755 sensors/pt100-board-python-package/pt100_board/pt100_reader.py create mode 100644 sensors/pt100-board-python-package/setup.py diff --git a/sensors/pt100-board-python-package/pt100_board/__init__.py b/sensors/pt100-board-python-package/pt100_board/__init__.py new file mode 100755 index 0000000..5c2d473 --- /dev/null +++ b/sensors/pt100-board-python-package/pt100_board/__init__.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python + +class PT100BoardDatagram(object): + + SENDERS = {b'A': 'Answer', b'W': 'Write'} + KINDS = {b'T': 'temperature', b'E': 'EEPROM', b'R': 'reload configuration', b'I': 'board information'} + TERMINATOR = b'\n' + + def __init__(self, data): + """ + data: bytes + """ + assert len(data) == 11 + assert data[-1:] == self.TERMINATOR + self.data = data + + self.sender = self.SENDERS[data[0:1]] + self.kind = self.KINDS[data[1:2]] + + self.board = int(data[2:3], 16) + self.sensor = int(data[3:4], 16) + + if self.kind == 'temperature': + self.temperature = None + self.connected = int(data[4:5], 16) == 0 + if self.connected: + value = int(data[5:10], 16) + if value & 0x80000: value -= 0x100000 + value /= 1000. + self.temperature = value + diff --git a/sensors/pt100-board-python-package/pt100_board/pt100_csv_reader.py b/sensors/pt100-board-python-package/pt100_board/pt100_csv_reader.py new file mode 100755 index 0000000..9c0f3bc --- /dev/null +++ b/sensors/pt100-board-python-package/pt100_board/pt100_csv_reader.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python + +import argparse +import sys +import time +from datetime import datetime as dt + +from pt100_board import PT100BoardDatagram + +try: + import serial +except ImportError: + sys.stderr.write("Could not import the PySerial module. Please install it first.\n") + sys.exit(1) + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('device', help='The serial port to open') + parser.add_argument('--timestamp', choices=('none', 'relative', 'absolute'), default='relative', help='Type of timestamps') + parser.add_argument('--headline', action='store_true', help='Output column headers in first line of output') + args = parser.parse_args() + + ser = serial.Serial(args.device, baudrate=38400, timeout=0.1) + + if args.headline: + if args.timestamp == 'relative': + headers = ['reltime'] + elif args.timestamp == 'absolute': + headers = ['abstime'] + else: + headers = [] + headers += ['boardid'] + headers += ['sensor%d' % i for i in range(8)] + print(' '.join(headers)) + + start = time.time() + sensor_values = {} + try: + while True: + line = ser.readline() + if not line: continue + line = line.strip(b'\x00') + try: + dg = PT100BoardDatagram(line) + except: + sys.stderr.write("Could not interpret this line: {}\n".format(repr(line))) + continue + if dg.kind == 'temperature': + if dg.connected: + sensor_values['{}.{}'.format(dg.board, dg.sensor)] = dg.temperature + else: + sensor_values['{}.{}'.format(dg.board, dg.sensor)] = float('nan') + if dg.sensor == 0x7: + try: + data = [] + if args.timestamp == 'relative': + data += ['%.3f' % (time.time() - start)] + if args.timestamp == 'absolute': + data += [dt.now().isoformat()] + data += [str(dg.board)] + data += ['%.3f' % sensor_values['{}.{}'.format(dg.board, sensor)] for sensor in range(8)] + sys.stdout.write(' '.join(data) + '\n') + sys.stdout.flush() + except KeyError: + sys.stderr.write('not enough values yet for board %d.\n' % dg.board) + else: + continue + + except KeyboardInterrupt: + sys.stderr.write("Ctrl-C pressed, exiting...\n") + sys.exit(1) + +if __name__ == "__main__": main() diff --git a/sensors/pt100-board-python-package/pt100_board/pt100_logfile_reader.py b/sensors/pt100-board-python-package/pt100_board/pt100_logfile_reader.py new file mode 100755 index 0000000..fc90161 --- /dev/null +++ b/sensors/pt100-board-python-package/pt100_board/pt100_logfile_reader.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python + +import argparse +import sys +import pandas as pd +from IPython import embed +from datetime import datetime as dt, timedelta +from matplotlib import pyplot as plt + +from pt100_board import PT100BoardDatagram + +parser = argparse.ArgumentParser() +parser.add_argument('--config', help='A config file (with names for the sensors)') +parser.add_argument('logfile', help='The serial port to open') +args = parser.parse_args() + +config = {} +if args.config: + with open(args.config, 'r') as configfile: + for line in configfile: + if line.startswith('#'): continue + line = line.rstrip('\n') + if not line: continue + parts = line.split() + if len(parts) == 4: parts = parts + [''] + if len(parts) != 5: continue + board, sensor, y, x, name = parts + board, sensor, y, x = (int(val) for val in (board, sensor, y, x)) + sensor_id = '{}.{}'.format(board, sensor) + if not name: name = sensor_id + config[sensor_id] = dict(name=name, x=x, y=y) + +timestamp = None +#last_time = 0 +#log_n_plot_interval = 1.0 +last_time = dt(1900,1,1) +log_n_plot_interval = timedelta(seconds=1) +data = {'timestamps': [], 'sensor_values': {}} +sensor_values = {} +with open(args.logfile, 'rb') as logfile: + for line in logfile: + line = line.strip(b'\x00') + if not line: continue + if line.startswith(b'#'): + try: + #timestamp = int(line[1:]) + timestamp = dt.utcfromtimestamp(int(line[1:])) + except: + sys.stderr.write('could not read this timestamp: {}\n'.format(line)) + continue + try: + dg = PT100BoardDatagram(line) + except: + sys.stderr.write("Could not interpret this line: {}\n".format(repr(line))) + continue + if dg.kind == 'temperature': + if dg.connected: + sensor_id = '{}.{}'.format(dg.board, dg.sensor) + if config: sensor_name = config[sensor_id]['name'] + sensor_values[sensor_name] = dg.temperature + else: + continue + else: + continue + + if timestamp and timestamp >= last_time + log_n_plot_interval: + data['timestamps'].append(timestamp) + for sensor in sensor_values: + if sensor not in data['sensor_values']: + data['sensor_values'][sensor] = [float('nan')] * (len(data['timestamps']) - 1) + data['sensor_values'][sensor].append(sensor_values[sensor]) + for sensor in data['sensor_values']: + if sensor not in sensor_values: + data['sensor_values'][sensor].append(float('nan')) + sensor_values = {} + last_time = timestamp + +df = pd.DataFrame(data['sensor_values']) +df.index = data['timestamps'] + +print(""" +df.plot() +plt.show() + +# removing spikes with 1st derivative +threshold = 0.3 +for col in df.columns: + df[col][df[col].diff().abs()>threshold] = float('nan') + +# removing spikes with 2nd derivative +threshold = .7 +for col in df.columns: + df[col][df[col].diff().diff().abs()>threshold] = float('nan') +""") + +embed() + diff --git a/sensors/pt100-board-python-package/pt100_board/pt100_reader.py b/sensors/pt100-board-python-package/pt100_board/pt100_reader.py new file mode 100755 index 0000000..c6533f2 --- /dev/null +++ b/sensors/pt100-board-python-package/pt100_board/pt100_reader.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python + +import argparse +import sys +import time +from datetime import datetime as dt + +from pt100_board import PT100BoardDatagram + +try: + import serial +except ImportError: + sys.stderr.write("Could not import the PySerial module. Please install it first.\n") + sys.exit(1) + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('--plot', action='store_true', help='Real time plot of the temperatures') + parser.add_argument('device', help='The serial port to open') + args = parser.parse_args() + + if args.plot: + import matplotlib + matplotlib.use('TKAgg') + #matplotlib.use('GTKAgg') + from matplotlib import pyplot as plt + import random + + ser = serial.Serial(args.device, baudrate=38400, timeout=0.1) + + log_n_plot_interval = 2. + last_time = time.time() + data = {'timestamps': [], 'sensor_values': {}} + sensor_values = {} + try: + while True: + line = ser.readline() + if not line: continue + line = line.strip(b'\x00') + try: + dg = PT100BoardDatagram(line) + except: + sys.stderr.write("Could not interpret this line: {}\n".format(repr(line))) + continue + if dg.kind == 'temperature': + if dg.connected: + line = '{}.{} {}\n'.format(dg.board, dg.sensor, dg.temperature) + sensor_values['{}.{}'.format(dg.board, dg.sensor)] = dg.temperature + else: + continue + else: + continue + sys.stdout.write(line) + sys.stdout.flush() + + now = time.time() + if now >= last_time + log_n_plot_interval: + last_time += log_n_plot_interval + data['timestamps'].append(dt.now().replace(microsecond=0)) + for sensor in sensor_values: + if sensor not in data['sensor_values']: + data['sensor_values'][sensor] = [float('nan')] * (len(data['timestamps']) - 1) + data['sensor_values'][sensor].append(sensor_values[sensor]) + for sensor in data['sensor_values']: + if sensor not in sensor_values: + data['sensor_values'][sensor].append(float('nan')) + sensor_values = {} + + if args.plot: + plt.clf() + for sensor in data['sensor_values']: + plt.plot(data['timestamps'], data['sensor_values'][sensor], label=sensor) + plt.legend() + plt.draw() + plt.pause(0.0001) + + except KeyboardInterrupt: + sys.stderr.write("Ctrl-C pressed, exiting...\n") + sys.exit(1) + +if __name__ == "__main__": main() diff --git a/sensors/pt100-board-python-package/setup.py b/sensors/pt100-board-python-package/setup.py new file mode 100644 index 0000000..200604c --- /dev/null +++ b/sensors/pt100-board-python-package/setup.py @@ -0,0 +1,21 @@ +try: + from setuptools import setup +except ImportError: + from distutils.core import setup + +setup(name='pt100_board', + version='0.2', + description='Utilities to work with the Pt100 Reader Board', + url='', + author='Philipp Klaus', + author_email='klaus@physik.uni-frankfurt.de', + packages=['pt100_board'], + install_requires=['PySerial'], + entry_points = { + 'console_scripts': [ + 'pt100_reader = pt100_board.pt100_reader:main', + 'pt100_csv_reader = pt100_board.pt100_csv_reader:main', + 'pt100_logfile_reader = pt100_board.pt100_logfile_reader:main', + ], + }, + zip_safe=True) -- 2.43.0