Files
ilt1000/ilt1000.py

375 lines
8.2 KiB
Python
Raw Normal View History

2016-04-07 22:19:40 -07:00
#!/usr/bin/python3
import datetime
import serial
import time
2016-04-07 22:19:40 -07:00
class Error(Exception):
pass
class UnsupportedCommand(Error):
pass
class RequiredValueNotSet(Error):
pass
class OutOfRange(Error):
pass
class Saturated(Error):
pass
2016-04-08 13:10:42 -07:00
class CommandError(Error):
pass
2016-04-08 10:59:37 -07:00
# TODO commands:
2016-04-23 15:02:09 -07:00
# captureflash (SPEC ERROR: doc description missing)
# getflash
# clearambientlevel
# getambientlevel
# setambientlevel
# getirrthresholdlow
# setirrthresholdlow
# setirrdatapoint
# eraseirrdata
2016-04-08 10:59:37 -07:00
# setsimpleirrcal
2016-04-23 15:02:09 -07:00
# storeirrdata
# setcurrentloop
2016-04-08 10:59:37 -07:00
# setuserdark
2016-04-23 15:02:09 -07:00
# setfactorydark
2016-04-08 10:59:37 -07:00
# startlogdata
# stoplogdata
2016-04-23 15:02:09 -07:00
# eraselogdata
2016-04-08 10:59:37 -07:00
# usecalfactor
# erasecalfactor
# getcalfactor
# setcalfactor
2016-04-23 15:02:09 -07:00
# usecalfactortemp
2016-04-23 14:44:38 -07:00
# setsamplecount
2016-04-23 15:02:09 -07:00
# getsampletime
# setsampletime
# setsampletimetemp
# getapiversion
# getbias
# getecal
# getecaldate
# setecal
# getfeedbackresnumber
# usefeedbackrestemp
# setfeedbackres
# getfriendlyname
# setfriendlyname
# getintegrate
# startintegrate
# topintegrate
# getpeak
# startpeak
# gettriggerin
# settriggerout
# getvoltagestage
# getvagc3
# getvped
# getvped
# getvx1
# getvx17
# set0vbias
# set5vbias
# setwireless
# configbackup
# configrestore
# setmodelname
# setwflisten
2016-04-08 10:59:37 -07:00
2016-04-07 22:19:40 -07:00
class ILT1000(object):
2016-04-08 11:24:02 -07:00
DARK_NONE = 0
DARK_FACTORY = 1
DARK_USER = 2
DARK_NAMES = {
0: 'None',
1: 'Factory',
2: 'User',
}
2016-04-08 13:01:49 -07:00
FEEDBACK_RES_AUTO = 0
FEEDBACK_RES_LOW = 1
FEEDBACK_RES_MEDIUM = 2
FEEDBACK_RES_HIGH = 3
2016-04-08 13:10:42 -07:00
AVERAGING_AUTO = 0
AVERAGING_LOW = 1 # 5 ㎐
AVERAGING_MEDIUM = 2 # 2 ㎐
AVERAGING_HIGH = 3 # 0.5 ㎐
2016-04-08 14:02:56 -07:00
LOG_OPTICAL_DENSITY = 1 << 0
LOG_TRANSMISSION_PERCENT = 1 << 1
LOG_SENSOR_CURRENT = 1 << 2
LOG_SENSOR_VOLTAGE = 1 << 3
LOG_CONTROLLER_TEMP = 1 << 4
LOG_IRRADIANCE = 1 << 5
LOG_REALTIME = 1 << 7
2016-04-08 10:59:37 -07:00
# ILT1000 presents two FTDI serial devices, which become ttyUSB0 and ttyUSB1
# if nothing else is attached. ttyUSB0 seems to be completely non-responsive.
# We default to ttyUSB1
def __init__(self, device='/dev/ttyUSB1', set_time=True):
self._dev = serial.Serial(device, baudrate=115200)
self._Clear()
2016-04-08 13:10:42 -07:00
self._SendCommandOrDie('echooff')
2016-04-07 22:19:40 -07:00
if set_time:
self.SetDateTime()
def _Clear(self):
self._dev.timeout = 0.1
2016-04-23 14:44:38 -07:00
self._dev.write(b'\r')
2016-04-08 14:02:56 -07:00
self._dev.read(2 ** 16)
self._dev.timeout = None
2016-04-08 14:02:56 -07:00
def _GetLine(self):
return self._dev.readline().rstrip().decode('ascii')
2016-04-07 22:19:40 -07:00
def _SendCommand(self, command):
2016-04-23 14:44:38 -07:00
encoded = command.encode('ascii') + b'\r'
self._dev.write(encoded[:1])
time.sleep(0.05)
self._dev.write(encoded[1:])
2016-04-08 14:02:56 -07:00
ret = self._GetLine()
2016-04-07 22:19:40 -07:00
if ret == '-999':
raise UnsupportedCommand(command)
if ret == '-500':
raise RequiredValueNotSet(command)
if ret == '-501':
raise OutOfRange(command)
if ret == '-502':
raise Saturated(command)
return ret
2016-04-08 13:10:42 -07:00
def _SendCommandOrDie(self, command):
ret = self._SendCommand(command)
if int(ret) != 0:
raise CommandError
2016-04-07 22:19:40 -07:00
def GetModelName(self):
return self._SendCommand('getmodelname')
def GetGeneration(self):
return int(self._SendCommand('getgeneration'))
def GetFirmwareVersion(self):
return self._SendCommand('getfwversion')
def GetSerialNumber(self):
return self._SendCommand('getserialnumber')
2016-04-08 11:17:43 -07:00
def GetAuxSerialNumber(self):
return self._SendCommand('getauxserialno')
def SetAuxSerialNumber(self, serial):
2016-04-08 13:10:42 -07:00
self._SendCommandOrDie('setauxserialno %s' % serial)
2016-04-07 22:19:40 -07:00
def GetControllerTempF(self):
return int(self._SendCommand('gettemp'))
def GetAmbientTempF(self):
return int(self._SendCommand('getambienttemp'))
def GetDateTime(self):
ret = self._SendCommand('getdatetime')
return datetime.datetime.fromtimestamp(int(ret.split()[2]))
def SetDateTime(self, now=None):
now = now or datetime.datetime.utcnow()
timestr = now.strftime('%m/%d/%Y %H:%M:%S')
2016-04-08 13:10:42 -07:00
self._SendCommandOrDie('setdatetime ' + timestr)
2016-04-07 22:19:40 -07:00
2016-04-08 10:59:37 -07:00
def GetSensorCurrent(self):
2016-04-07 22:19:40 -07:00
ret = self._SendCommand('getcurrent')
return float(ret)
2016-04-08 10:59:37 -07:00
def GetSensorVoltage(self):
2016-04-07 22:19:40 -07:00
ret = self._SendCommand('getvoltage')
2016-04-08 14:09:34 -07:00
return float(ret)
2016-04-07 22:19:40 -07:00
def GetTransmissionPercent(self):
ret = self._SendCommand('gettrans')
return float(ret) / 10
2016-04-23 14:35:18 -07:00
def Get100PercentCurrent(self):
2016-04-08 11:09:38 -07:00
ret = self._SendCommand('get100perc')
2016-04-08 11:11:34 -07:00
return float(ret)
2016-04-08 11:24:02 -07:00
2016-04-23 14:35:18 -07:00
def Set100PercentCurrent(self):
2016-04-08 12:56:58 -07:00
ret = self._SendCommand('set100perc')
return float(ret)
2016-04-08 13:16:45 -07:00
def GetOpticalDensity(self):
ret = self._SendCommand('getod')
return float(ret) / 100
2016-04-08 13:16:05 -07:00
def GetIrradiance(self):
ret = self._SendCommand('getirradiance')
2016-04-23 15:02:09 -07:00
return float(ret)
2016-04-08 13:16:05 -07:00
2016-04-08 11:24:02 -07:00
def GetDarkMode(self):
return int(self._SendCommand('getdarkmode'))
2016-04-08 11:28:52 -07:00
2016-04-08 12:51:14 -07:00
_DARK_MODE_COMMANDS = {
DARK_NONE: 'usenodark',
DARK_FACTORY: 'usefactorydark',
DARK_USER: 'useuserdark',
}
2016-04-08 13:14:52 -07:00
def SetDarkMode(self, mode=DARK_FACTORY):
2016-04-08 13:10:42 -07:00
self._SendCommandOrDie(self._DARK_MODE_COMMANDS[mode])
2016-04-08 12:51:14 -07:00
2016-04-08 12:46:22 -07:00
def GetFactoryDarkVoltages(self):
ret = self._SendCommand('getfactorydark')
2016-04-23 15:07:35 -07:00
values = ret.split(' ')
return [
[float(values[4 * r + i + 1]) / 1000000 for i in range(3)]
for r in range(3)]
2016-04-08 12:46:22 -07:00
2016-04-08 12:51:14 -07:00
def GetUserDarkVoltages(self):
2016-04-23 15:07:35 -07:00
ret = self._SendCommand('getuserdark')
values = ret.split(' ')
return [
[float(values[4 * r + i + 1]) / 1000000 for i in range(3)]
for r in range(3)]
2016-04-08 12:51:14 -07:00
2016-04-08 11:42:37 -07:00
def GetClockFrequencyHz(self):
ret = self._SendCommand('getclockfreq')
return float(ret) / 100
2016-04-08 12:25:02 -07:00
def GetFeedbackResistanceOhm(self):
ret = self._SendCommand('getfeedbackres')
return float(ret) * 100
2016-04-08 13:01:49 -07:00
2016-04-08 13:14:52 -07:00
def SetFeedbackResistor(self, resistor=FEEDBACK_RES_AUTO):
2016-04-08 13:10:42 -07:00
self._SendCommandOrDie('usefeedbackres %d' % resistor)
_AVERAGING_COMMANDS = {
AVERAGING_AUTO: 'setautaveraging',
AVERAGING_LOW: 'setlowaveraging',
AVERAGING_MEDIUM: 'setmedaveraging',
AVERAGING_HIGH: 'sethiaveraging',
}
2016-04-08 13:14:52 -07:00
def SetAveraging(self, averaging=AVERAGING_AUTO):
2016-04-08 13:10:42 -07:00
self._SendCommandOrDie(self._AVERAGING_COMMANDS[averaging])
2016-04-08 13:14:52 -07:00
2016-04-08 14:02:56 -07:00
def StartLogging(self, mask, period_seconds):
2016-04-23 14:35:18 -07:00
self._SendCommandOrDie('startlogdata %d %d %d' % (mask, period_seconds * 100, time.time()))
2016-04-08 14:02:56 -07:00
def StopLogging(self):
self._SendCommandOrDie('stoplogdata')
def EraseLogData(self):
self._SendCommandOrDie('eraselogdata')
def GetLogData(self):
samples = int(self._SendCommand('getlogdata'))
mask = int(self._GetLine())
period = float(self._GetLine()) / 10
ret = {
'period_seconds': period,
'samples': [],
}
fields = [
'recorded',
]
if mask & self.LOG_OPTICAL_DENSITY:
fields.append('optical_density')
if mask & self.LOG_TRANSMISSION_PERCENT:
fields.append('transmission_percent')
if mask & self.LOG_SENSOR_CURRENT:
fields.append('sensor_current')
if mask & self.LOG_SENSOR_VOLTAGE:
fields.append('sensor_voltage')
if mask & self.LOG_CONTROLLER_TEMP:
fields.append('controller_temp')
if mask & self.LOG_IRRADIANCE:
fields.append('irradiance')
for _ in range(samples):
row = self._GetLine()
values = row.split(',')
sample = [
datetime.datetime.fromtimestamp(int(values[0])),
]
index = 1
if mask & self.LOG_OPTICAL_DENSITY:
sample.append(float(values[index]) / 100)
index += 1
if mask & self.LOG_TRANSMISSION_PERCENT:
sample.append(float(values[index]) / 10)
index += 1
if mask & self.LOG_SENSOR_CURRENT:
sample.append(float(values[index]))
index += 1
if mask & self.LOG_SENSOR_VOLTAGE:
2016-04-08 14:09:34 -07:00
sample.append(float(values[index]))
2016-04-08 14:02:56 -07:00
index += 1
if mask & self.LOG_CONTROLLER_TEMP:
sample.append(float(values[index]))
index += 1
if mask & self.LOG_IRRADIANCE:
2016-04-23 15:02:09 -07:00
sample.append(float(values[index]))
2016-04-08 14:02:56 -07:00
index += 1
ret['samples'].append(_Row(fields, sample))
return ret
2016-04-23 15:11:15 -07:00
def GetInfo(self):
# SPEC ERROR: There doesn't seem to be a good way to, other than a timing
# hack, to find the end of the getinfo response.
raise UnsupportedCommand
2016-04-08 14:02:56 -07:00
class _Row(object):
def __init__(self, fields, values):
self._fields = fields
self._values = values
def __getitem__(self, key):
return self._values[self._fields.index(key)]
def __str__(self):
return str(self.AsDict())
def __repr__(self):
return repr(self.AsDict())
def AsDict(self):
return dict(zip(self._fields, self._values))