2016-04-07 22:19:40 -07:00
|
|
|
#!/usr/bin/python3
|
|
|
|
|
|
|
|
|
|
import datetime
|
|
|
|
|
import serial
|
2016-04-08 12:04:17 -07:00
|
|
|
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:
|
|
|
|
|
# eraselogdata
|
|
|
|
|
# getlogdata
|
|
|
|
|
# setcurrentloop
|
|
|
|
|
# setsimpleirrcal
|
|
|
|
|
# setuserdark
|
|
|
|
|
# startlogdata
|
|
|
|
|
# stoplogdata
|
|
|
|
|
# usecalfactor
|
|
|
|
|
# erasecalfactor
|
|
|
|
|
# getcalfactor
|
|
|
|
|
# setcalfactor
|
|
|
|
|
|
|
|
|
|
|
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 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):
|
2016-04-08 12:04:17 -07:00
|
|
|
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()
|
|
|
|
|
|
2016-04-08 12:04:17 -07:00
|
|
|
def _Clear(self):
|
|
|
|
|
self._dev.timeout = 0.1
|
|
|
|
|
self._dev.write(b'\r\n')
|
|
|
|
|
self._dev.read(128)
|
|
|
|
|
self._dev.timeout = None
|
|
|
|
|
|
2016-04-07 22:19:40 -07:00
|
|
|
def _SendCommand(self, command):
|
|
|
|
|
self._dev.write(command.encode('ascii') + b'\r\n')
|
|
|
|
|
ret = self._dev.readline().rstrip().decode('ascii')
|
|
|
|
|
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')
|
|
|
|
|
|
2016-04-08 12:04:17 -07:00
|
|
|
def SetAuxSerialNumber(self, serial):
|
|
|
|
|
# SPEC ERROR
|
|
|
|
|
# This is undocumented.
|
2016-04-08 13:10:42 -07:00
|
|
|
self._SendCommandOrDie('setauxserialno %s' % serial)
|
2016-04-08 12:04:17 -07:00
|
|
|
|
2016-04-07 22:19:40 -07:00
|
|
|
def GetControllerTempF(self):
|
|
|
|
|
return int(self._SendCommand('gettemp'))
|
|
|
|
|
|
|
|
|
|
def GetAmbientTempF(self):
|
2016-04-08 10:59:37 -07:00
|
|
|
# SPEC ERROR
|
2016-04-07 22:19:40 -07:00
|
|
|
# Protocol doc indicates that this is degrees F * 100, but actual values
|
|
|
|
|
# look like just degrees F
|
|
|
|
|
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):
|
|
|
|
|
# SPEC ERROR
|
2016-04-08 11:11:34 -07:00
|
|
|
# Protocol doc indicates that this is in pA, but actual values are in
|
2016-04-08 10:59:37 -07:00
|
|
|
# scientific notation and appear to be A. They are also suspiciously
|
|
|
|
|
# similar to getvoltage return values.
|
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')
|
|
|
|
|
return float(ret) / 1000000
|
|
|
|
|
|
|
|
|
|
def GetTransmissionPercent(self):
|
|
|
|
|
ret = self._SendCommand('gettrans')
|
|
|
|
|
return float(ret) / 10
|
|
|
|
|
|
|
|
|
|
def GetOpticalDensity(self):
|
|
|
|
|
ret = self._SendCommand('getod')
|
|
|
|
|
return float(ret) / 100
|
2016-04-08 11:09:38 -07:00
|
|
|
|
|
|
|
|
def Get100PercentVoltage(self):
|
2016-04-08 11:11:34 -07:00
|
|
|
# SPEC ERROR
|
|
|
|
|
# Spec says microvolts, but actual values appear to be in volts.
|
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-08 12:56:58 -07:00
|
|
|
def Set100PercentVoltage(self):
|
|
|
|
|
# SPEC ERROR
|
|
|
|
|
# Spec says microvolts, but actual values appear to be in volts.
|
|
|
|
|
ret = self._SendCommand('set100perc')
|
|
|
|
|
return float(ret)
|
|
|
|
|
|
2016-04-08 13:16:05 -07:00
|
|
|
def GetIrradiance(self):
|
|
|
|
|
ret = self._SendCommand('getirradiance')
|
|
|
|
|
return float(ret) / 1000
|
|
|
|
|
|
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):
|
|
|
|
|
# SPEC ERROR
|
|
|
|
|
# Actual return value sample:
|
|
|
|
|
# R1 12149 9733 9251 R2 12476 10080 9604 R3 13940 11894 11435
|
|
|
|
|
ret = self._SendCommand('getfactorydark')
|
|
|
|
|
return [float(x) / 1000000 for x in ret.split()]
|
|
|
|
|
|
2016-04-08 12:51:14 -07:00
|
|
|
def GetUserDarkVoltages(self):
|
|
|
|
|
# SPEC ERROR
|
|
|
|
|
# Actual return value sample:
|
|
|
|
|
# R1 12149 9733 9251 R2 12476 10080 9604 R3 13940 11894 11435
|
|
|
|
|
ret = self._SendCommand('getfactorydark')
|
|
|
|
|
return [float(x) / 1000000 for x in ret.split()]
|
|
|
|
|
|
2016-04-08 11:42:37 -07:00
|
|
|
def GetClockFrequencyHz(self):
|
|
|
|
|
ret = self._SendCommand('getclockfreq')
|
|
|
|
|
return float(ret) / 100
|
2016-04-08 12:04:17 -07:00
|
|
|
|
|
|
|
|
def SetClockFrequency(self):
|
|
|
|
|
# SPEC ERROR
|
2016-04-08 13:14:52 -07:00
|
|
|
# Command returns -999 on my ILT1000-V02 3.0.7.7. Implementation below is
|
|
|
|
|
# untested and likely wrong.
|
2016-04-08 13:10:42 -07:00
|
|
|
self._SendCommandOrDie('setclockfreq')
|
2016-04-08 12:04:17 -07:00
|
|
|
self._dev.write(b'A')
|
|
|
|
|
time.sleep(60.0)
|
|
|
|
|
self._dev.write(b'B')
|
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
|
|
|
# SPEC WARNING
|
|
|
|
|
# There does not appear to be a way to read this back.
|
|
|
|
|
self._SendCommandOrDie(self._AVERAGING_COMMANDS[averaging])
|
2016-04-08 13:14:52 -07:00
|
|
|
|
|
|
|
|
def SetSampleCount(self, sample_count=200):
|
|
|
|
|
# SPEC WARNING
|
|
|
|
|
# There does not appear to be a way to read this back.
|
|
|
|
|
# SPEC ERROR
|
|
|
|
|
# Returns -999 on my ILT1000-V02 3.0.7.7.
|
|
|
|
|
self._SendCommandOrDie('setsamplecount %d' % sample_count)
|