frappy/frappy_psi/sensirion.py

96 lines
3.3 KiB
Python

# *****************************************************************************
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# Module authors:
# Markus Zolliker <markus.zolliker@psi.ch>
#
# *****************************************************************************
"""sensirion flow sensor,
onnected via an Arduio Nano on a serial connection
shared with the trinamic hepump valve motor
"""
import math
from frappy.core import Parameter, Readable, IntRange, FloatRange, BoolType, BytesIO, HasIO
from frappy.errors import ProgrammingError
class FlowSensor(HasIO, Readable):
value = Parameter(unit='ln/min')
stddev = Parameter('std dev.', FloatRange(unit='ln/min'))
nsamples = Parameter('number of samples for averaging', IntRange(1,1024), default=160)
offset = Parameter('offset correction', FloatRange(unit='ln/min'), readonly=False, default=0)
scale = Parameter('scale factor', FloatRange(), readonly=False, default=2.3)
saved = Parameter('is the current value saved?', BoolType(), readonly=False)
pollinterval = Parameter(default=0.2)
ioClass = BytesIO
_saved = None
def command(self, cmd, nvalues=1):
if len(cmd) == 1: # its a query
command = f'{cmd}\n'
else:
if len(cmd) > 7:
raise ProgrammingError('number does not fit into 6 characters')
command = f'{cmd[0]}{cmd[1:].ljust(6)}\n'
reply = self.io.communicate(command.encode('ascii'), max(1, nvalues * 9))
if nvalues == 1:
return float(reply)
if nvalues:
return tuple(float(s) for s in reply.split())
return None
def doPoll(self):
flow, stddev = self.command('?', nvalues=2)
stddev = stddev / math.sqrt(self.nsamples)
if (flow, stddev) != (self.value, self.stddev):
self.value, self.stddev = flow, stddev
# TODO: treat status (e.g. when reading 0 always)
def read_value(self):
self.doPoll()
return self.value
def read_nsamples(self):
return self.command('n')
def write_nsamples(self, nsamples):
return self.command(f'n{nsamples}')
def read_offset(self):
return self.command('o')
def write_offset(self, offset):
return self.command(f'o{offset:.2f}')
def read_scale(self):
return self.command('g')
def write_scale(self, scale):
return self.command(f'g{scale:.4f}')
def read_saved(self):
if self._saved is None:
self._saved = self.read_scale(), self.read_offset()
return True
return self._saved == (self.scale, self.offset)
def write_saved(self, target):
if target:
self.command('s', nvalues=0)