pax_global_header00006660000000000000000000000064120532330710014506gustar00rootroot0000000000000052 comment=b14f032352b1cbf93042fa3ff9b3fd816867a989 esc-pyoptical-d8b0edf/000077500000000000000000000000001205323307100150405ustar00rootroot00000000000000esc-pyoptical-d8b0edf/README.rst000066400000000000000000000021301205323307100165230ustar00rootroot00000000000000pyoptical ========= about -------------------- Pure python interface to CRS OptiCAL. dependencies -------------------- pyserial install -------------------- Available in Debian as ``python-pyoptical``, to install run:: apt-get install python-pyoptical Installation from source:: ./setup.py install (may need root/admin privileges) documentation -------------------- Please see the docstring for the pyoptical module. changelog -------------------- * 0.4 - 2012-11-21 * Fix long standing bug where some luminance values incorrectly caused an error because the NACK byte was present in the ADC value and not only the last byte, which contains the ack value was checked. Thanks to Ivan Prikhodko. * 0.3 - 2010-07-26 * Initial implementation * Support command line interface and module readout * Extensive documentation for use on all major OSs * Contains changes from both early alphas, 0.1. and 0.2 author -------------------- Copyright (C) 2009-2012 Valentin Haenel copying -------------------- License: MIT-X, see pyoptical.py for details. esc-pyoptical-d8b0edf/pyoptical000077500000000000000000000055031205323307100167750ustar00rootroot00000000000000#!/usr/bin/env python #coding=utf-8 # : vim set filtype=python : # Copyright (c) 2009-2012 Valentin Haenel # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. """ Command line wrapper for the pyoptical module. """ import pyoptical as pyop from optparse import OptionParser import sys import time from itertools import repeat if __name__ == "__main__": usage = "%prog [-i interval] [-n number ] [-r] com-port" version = "%prog " + pyop.__version__ error_prefix = "pyoptical -- error:" parser = OptionParser(usage=usage, version=version) parser.add_option("-i", "--interval", action="store", type="float", dest="interval", default=0.0, help="the measurement interval in ms, default is as fast as possible") parser.add_option("-n", "--number", action="store", type="int", dest="number", default=None, help="number of measurements to make, default is endless") parser.add_option("-r", "--robust", action="store_true", dest="robust", default=False, help="when encountering an error, try to continue ignoring as many exceptions as possible") opts, args = parser.parse_args() if len(args) != 1: parser.error("wrong number of arguments") sys.exit(-1) op = pyop.OptiCAL(args[0]) # this is a hack, either it executes opts.number times, or endlessly for i in repeat(None) if opts.number is None else repeat(None, opts.number): try: print op.read_luminance() time.sleep(opts.interval / 1000) except pyop.OptiCALException, e: sys.stderr.write(error_prefix + str(e) + "\n") if opts.robust: continue else: sys.exit(-1) esc-pyoptical-d8b0edf/pyoptical.py000066400000000000000000000335111205323307100174210ustar00rootroot00000000000000#!/usr/bin/env python #coding=utf-8 # Copyright (c) 2009-2012 Valentin Haenel # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. """ Python interface to the CRS 'OptiCAL' photometer. Overview ======== The 'OptiCAL' is a photometer that is produced by Cambridge Research Systems (CRS). This device is a standard tool for gamma-calibration of display devices in vision research. This package provides a free-software replacement for the Windows-software distributed by the manufacturer that allows querying an OptiCAL via a serial connection. `pyoptical` can be used as a library in third-party applications or as a standalone command line tool. This module provides the `OptiCAL` class and some supporting code. The command line wrapper for this module can be found in the 'pyoptical' script. Examples -------- Command line: ``pyoptical --number=15 --interval=750 /dev/ttyUSB0`` Make 15 measurements with an interval of 750 ms between them. usage: ``pyoptical [-i interval] [-n number ] [-r] com-port`` For more information try, ``pyoptical --help``. For more information about the ``com-port`` argument see: `Notes about the com-port`_. Library: >>> import pyoptical >>> op = pyoptical.OptiCAL('dev/dev/ttyUSB0') >>> try: >>> op.read_luminance() >>> except pyoptical.NACKException, e: >>> print e Notes about the com-port ------------------------ The com_port argument for the constructor may vary depending on both your operating system and how you connect to the OptiCAL. This code was developed using a usb-to-serial adapter that contains a PL2303 chipset manufactured by Prolific: http://www.prolific.com.tw/eng/Products.asp?ID=59. The following sections outline how to access the OptiCAL using pyoptical and a usb-to-serial adapter containing the prolific chipset. We have not tried this code using a raw serial port, but would be very interested to hear from you if you do. Linux (Ubuntu Hardy / Debian Lenny): Support for the PL2303 chipset is compiled into the kernel, and the device is automatically recognised. You could check 'dmesg' for the following output:: usb 2-1: new full speed USB device using uhci_hcd and address 4 usb 2-1: configuration #1 chosen from 1 choice pl2303 2-1:1.0: pl2303 converter detected usb 2-1: pl2303 converter now attached to ttyUSB0 In this case the com_port string is simply ``/dev/ttyUSB0`` Mac OSX (10.5.8 Leopard) Support for the PL2303 chipset is provided by the following open source driver: http://osx-pl2303.sourceforge.net/ In this case the com-port string would be something along the lines of ``/dev/tty.PL2303-xxx``, for example: ``/dev/tty.PL2303-000031FD`` Windows (XP) The manufacturer of your usb-to-serial adapter should provide you with drivers. In this case the com-port string would be something like: ``COM2``, check the device manager for the number of the COM port. Other Operating Systems and Adapters: This code has two limitations, most importantly pyserial must support your platform. Secondly, if you wish to use a usb-to-serial adapter a driver for your target operating system must be available from the manufacturer or possibly a third party (for example and open source driver). Notes about possible exceptions ------------------------------- There are three types of exceptions that can happen: - `OptiCALException` - `NACKException` - `TimeoutException` The `OptiCALException` is the base class for all exceptions in this module, and it is used as a general purpose exception to signify errors on the part of the programmer, do not quietly except these. The `NACKException` is raised when the OptiCAL responds with a ``NACK`` byte. It does this either if the command was not understood or if the command failed. If this happens during initialization, you may have to re-initialise the device. If this happens during readout it should be safe to try again instead of terminating the program. The `TimeoutException` is raised when no answer is received within the default timeout length. This might be caused by a number of issues, but essentially means that somehow the communication with the OptiCAL might be interrupted, for example because it is no longer connected to the computer. Implementation details ---------------------- The interface is implemented according to the protocol specification in the OptiCAL-User-Guide Version 4, 1995 including the following amendments: 1. To read out the ADC value, an 'L' must be sent instead of an 'R' 2. The equations to convert from ADC to meaningful units had changed. See the source code of ``read_luminance()`` for details. The full errata is available from the CSR website: http://support.crsltd.com/FileManagement/Download/9f5f62bcb3e64eb8934fe72afb937cb6 The corrected versions of the conversion formulas can also be found in the OptiCAL.py python interface available from the CRS website, written by Walter F. Bischof in 2007. However note that this code may contain a bug, see http://groups.google.com/group/psychopy-users/msg/9cb35590daf3a29b for detail. The constructor will first perform the initial calibration of the device as required by the protocol specification. Next it will read out all parameters from the eeprom and store them as private variables. And lastly it will put the device into the default mode. The initial version of the OptiCAL hardware supported two readout modes 'current' and 'voltage'. The device could be used to read luminance when in 'current' mode and 'voltage' when in voltage mode. Over the years there have been two revisions of the OptiCAL hardware, both no longer supported usage as a voltmeter, and thus the 'voltage' mode has become redundant. Since version 0.2 this interface no longer supports the 'voltage' mode, and the device will be put into 'current' mode at startup. """ __version__ = "0.4" __author__ = "Valentin Haenel " __docformat__ = "restructuredtext en" import serial class OptiCAL(object): """ Object to access the OptiCAL """ _ACK = '\x06' _NACK = '\x15' def __init__(self, com_port, timeout=5): """ initialise OptiCAL :Parameters: com_port : string name of the com-port timeout : float time in seconds to wait for a response For more information about the ``com_port`` argument see: the '``Notes about the com-port``' section in the module docstring. """ self._phot = serial.Serial(com_port, timeout=timeout) self._calibrate() self._read_ref_defs() self._read_other_defs() self._set_current_mode() def __str__(self): return "Optical found at : " + self._phot.port + "\n" + \ "Product Type : " + str(self._product_type) + "\n" + \ "Optical S/N : " + str(self._optical_serial_number) + "\n"\ "Firmware version : " + str(self._firmware_version) + "\n" \ "V_ref: " + str(self._V_ref) + "\n" + \ "Z_count: " + str(self._Z_count) + "\n" + \ "R_feed: " + str(self._R_feed) + "\n" + \ "R_gain: " + str(self._R_gain) + "\n" + \ "Probe S/N " + str(self._probe_serial_number) + "\n" +\ "K_cal: " + str(self._K_cal) + "\n" def _send_command(self, command, description): """ send a single command character and read a single response (ACK/NACK) :Parameters: command : chr the command character description : string a string describing the command """ self._phot.write(command) ret = self._phot.read() _check_return(ret, description) def _calibrate(self): """ perform initial calibration As stated in the OptiCAL user guide, this must be done after powering up the device, before any readouts are performed. """ self._send_command('C', "calibrate") def _set_current_mode(self): """ put the device into 'current' mode """ self._send_command('I', "set current mode") def _read_eeprom_single(self, address): """ read contents of eeprom at single address note: the ACK byte is truncated :Parameters: address : int address in the range 0