Source code for psychopy.hardware

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys
import glob
from itertools import chain
from psychopy import logging
from .manager import DeviceManager, deviceManager
from .base import BaseDevice, BaseResponse, BaseResponseDevice

try:
    from collections.abc import Iterable
except ImportError:
    from collections import Iterable

__all__ = [
    'forp',
    'cedrus',
    'minolta',
    'gammasci',
    'pr',
    'crs',
    'iolab',
    'eyetracker',
    'deviceManager',
    'listener'
]


def getSerialPorts():
    """Finds the names of all (virtual) serial ports present on the system

    Returns
    -------
    list
        Iterable with all the serial ports.

    """
    if sys.platform == "darwin":
        ports = [
            '/dev/tty.USA*',  # keyspan twin adapter is usually USA28X13P1.1
            '/dev/tty.Key*',  # some are Keyspan.1 or Keyserial.1
            '/dev/tty.modem*',
            '/dev/cu.usbmodem*',  # for PR650
            '/dev/tty.usbserial*',  # for the 'Plugable' converter,
                                    # according to Tushar Chauhan
        ]
    elif sys.platform.startswith("linux"):
        ports = [
            "/dev/ttyACM?",  # USB CDC devices (virtual serial ports)
            "/dev/ttyUSB?",  # USB to serial adapters using the
                             # usb-serial kernel module
            "/dev/ttyS?",   # genuine serial ports usually
                            # /dev/ttyS0 or /dev/ttyS1
        ]
    elif sys.platform == "cygwin":
        # I don't think anyone has actually tried this
        # Cygwin maps the windows serial ports like this
        ports = ["/dev/ttyS?", ]
    elif sys.platform == "win32":
        # While PsychoPy does support using numeric values to specify
        # which serial port to use, it is better in this case to
        # provide a cannoncial name.
        return map("COM{0}".format, range(11))  # COM0-10
    else:
        logging.error("We don't support serial ports on {0} yet!"
                      .format(sys.platform))
        return []

    # This creates an iterator for each glob expression. The glob
    # expressions are then chained together. This is more efficient
    # because it means we don't perform the lookups before we actually
    # need to.
    return chain.from_iterable(map(glob.iglob, ports))


def getAllPhotometers():
    """Gets all available photometers.

    The returned photometers may vary depending on which drivers are installed.
    Standalone PsychoPy ships with libraries for all supported photometers.

    Returns
    -------
    list
        A list of all photometer classes.

    """
    from .photometer import getAllPhotometerClasses

    # need values returned as a list for now
    return getAllPhotometerClasses()


def getPhotometerByName(name):
    """Gets a Photometer class by name.

    You can use either short names like 'pr650' or a long name like 'CRS
    ColorCAL'.

    Parameters
    ----------
    name : str
        The name of the device.

    Returns
    -------
    object
        Returns the photometer matching the passed in device name or `None` if
        we were unable to find it.

    """
    for thisName, photom in getAllPhotometers().items():
        # longName is used from the GUI and driverFor is for coders
        if name.lower() == thisName:
            return photom


[docs] def findPhotometer(ports=None, device=None): """Try to find a connected photometer/photospectrometer! PsychoPy will sweep a series of serial ports trying to open them. If a port successfully opens then it will try to issue a command to the device. If it responds with one of the expected values then it is assumed to be the appropriate device. Parameters ---------- ports : list A list of ports to search. Each port can be a string (e.g. 'COM1', '/dev/tty.Keyspan1.1') or a number (for win32 comports only). If `None` is provided then PsychoPy will sweep COM0-10 on Win32 and search known likely port names on MacOS and Linux. device : str String giving expected device (e.g. 'PR650', 'PR655', 'CS100A', 'LS100', 'LS110', 'S470'). If this is not given then an attempt will be made to find a device of any type, but this often fails. Returns ------- object or None An object representing the first photometer found, `None` if the ports didn't yield a valid response. `None` if there were not even any valid ports (suggesting a driver not being installed.) Examples -------- Sweeps ports 0 to 10 searching for a PR655:: photom = findPhotometer(device='PR655') print(photom.getLum()) if hasattr(photom, 'getSpectrum'): # can retrieve spectrum (e.g. a PR650) print(photom.getSpectrum()) """ from .photometer import getAllPhotometerClasses # try each port if isinstance(ports, str) or ports is None: ports = [ports] for port in ports: # get all available devices for cls, profiles in DeviceManager.getAvailableDevices( list(getAllPhotometerClasses().values()) ).items(): # iterate through each for profile in profiles: # if port matches, or device matches and no port given, initialise from this profile if ( port is None and device == cls.__name__ ) or ( port and "port" in profile and profile['port'] in port ): return DeviceManager.addDevice(**profile)
if __name__ == "__main__": pass

Back to top