Esempio n. 1
0
    def __init__(self, serialNumber=None, connect=True):
        """Load DLLs."""
        self.handle = None
        if platform.system() == 'Linux':
            from ctypes import cdll
            # ok I don't know what is wrong with my installer,
            # but I need to include .so.2
            self.lib = cdll.LoadLibrary("lib" + self.LIBNAME + ".so.2")
        elif platform.system() == 'Darwin':
            from picoscope.darwin_utils import LoadLibraryDarwin
            self.lib = LoadLibraryDarwin("lib" + self.LIBNAME + ".dylib")
        else:
            from ctypes import windll
            self.lib = windll.LoadLibrary(str(self.LIBNAME + ".dll"))

        super(PS4000, self).__init__(serialNumber, connect)
        # check to see which model we have and use special functions if needed

        unit_number = self.getUnitInfo('VariantInfo')
        if unit_number == '4262':
            self.getTimestepFromTimebase = self._getTimestepFromTimebase4262
            self.getTimeBaseNum = self._getTimeBaseNum4262
        elif unit_number in {'4223', '4224', '4423', '4424'}:
            self.getTimestepFromTimebase = self._getTimestepFromTimebase4223
            self.getTimeBaseNum = self._getTimeBaseNum4223
        else:
            raise NotImplementedError('Timebase functions have not been '
                                      'written for the ' + unit_number)
Esempio n. 2
0
    def __init__(self, serialNumber=None, connect=True):
        """Load DLL etc."""
        if platform.system() == 'Linux':
            from ctypes import cdll
            self.lib = cdll.LoadLibrary("lib" + self.LIBNAME + ".so")
        elif platform.system() == 'Darwin':
            from picoscope.darwin_utils import LoadLibraryDarwin
            self.lib = LoadLibraryDarwin("lib" + self.LIBNAME + ".dylib")
        else:
            from ctypes import windll
            self.lib = windll.LoadLibrary(str(self.LIBNAME + ".dll"))

        super(PS3000, self).__init__(serialNumber, connect)
Esempio n. 3
0
    def __init__(self, serialNumber=None, connect=True):
        """Load DLLs."""
        if platform.system() == 'Linux':
            from ctypes import cdll
            # ok I don't know what is wrong with my installer,
            # but I need to include .so.2
            self.lib = cdll.LoadLibrary("lib" + self.LIBNAME + ".so.2")
        elif platform.system() == 'Darwin':
            from picoscope.darwin_utils import LoadLibraryDarwin
            self.lib = LoadLibraryDarwin("lib" + self.LIBNAME + ".dylib")
        else:
            from ctypes import windll
            self.lib = windll.LoadLibrary(str(self.LIBNAME + ".dll"))

        super(PS6000, self).__init__(serialNumber, connect)
Esempio n. 4
0
    def __init__(self, serialNumber=None, connect=True):
        """Load DLLs."""
        self.handle = None
        if platform.system() == 'Linux':
            from ctypes import cdll
            # ok I don't know what is wrong with my installer,
            # but I need to include .so.2
            self.lib = cdll.LoadLibrary("lib" + self.LIBNAME + ".so.2")
        elif platform.system() == 'Darwin':
            from picoscope.darwin_utils import LoadLibraryDarwin
            self.lib = LoadLibraryDarwin("lib" + self.LIBNAME + ".dylib")
        else:
            from ctypes import windll
            self.lib = windll.LoadLibrary(str(self.LIBNAME + ".dll"))

        super(PS4000, self).__init__(serialNumber, connect)
        # check to see which model we have and use special functions if needed

        unit_number = self.getUnitInfo('VariantInfo')
        if unit_number == '4262':
            self.getTimestepFromTimebase = self._getTimestepFromTimebase4262
            self.getTimeBaseNum = self._getTimeBaseNum4262
        elif unit_number in {'4223', '4224', '4423', '4424'}:
            self.getTimestepFromTimebase = self._getTimestepFromTimebase4223
            self.getTimeBaseNum = self._getTimeBaseNum4223
        else:
            raise NotImplementedError('Timebase functions have not been '
                                      'written for the ' + unit_number)
Esempio n. 5
0
    def __init__(self, serialNumber=None, connect=True):
        """Load DLL etc."""
        if platform.system() == 'Linux':
            from ctypes import cdll
            self.lib = cdll.LoadLibrary("lib" + self.LIBNAME + ".so")
        elif platform.system() == 'Darwin':
            from picoscope.darwin_utils import LoadLibraryDarwin
            self.lib = LoadLibraryDarwin("lib" + self.LIBNAME + ".dylib")
        else:
            from ctypes import windll
            from ctypes.util import find_library
            self.lib = windll.LoadLibrary(
                find_library(str(self.LIBNAME + ".dll"))
            )

        self.resolution = self.ADC_RESOLUTIONS["8"]
        super(PS5000a, self).__init__(serialNumber, connect)
Esempio n. 6
0
    def __init__(self, serialNumber=None, connect=True):
        """Load DLLs."""
        self.handle = None

        if platform.system() == 'Linux':
            from ctypes import cdll
            # ok I don't know what is wrong with my installer,
            # but I need to include .so.2
            self.lib = cdll.LoadLibrary("lib" + self.LIBNAME + ".so.2")
        elif platform.system() == 'Darwin':
            from picoscope.darwin_utils import LoadLibraryDarwin
            self.lib = LoadLibraryDarwin("lib" + self.LIBNAME + ".dylib")
        else:
            from ctypes import windll
            from ctypes.util import find_library
            self.lib = windll.LoadLibrary(
                find_library(str(self.LIBNAME + ".dll")))

        self.resolution = self.ADC_RESOLUTIONS["12"]

        super(PS4000a, self).__init__(serialNumber, connect)
Esempio n. 7
0
    def __init__(self, serialNumber=None, connect=True):
        """Load DLL etc."""
        if platform.system() == 'Linux':
            from ctypes import cdll
            self.lib = cdll.LoadLibrary("lib" + self.LIBNAME + ".so")
        elif platform.system() == 'Darwin':
            from picoscope.darwin_utils import LoadLibraryDarwin
            self.lib = LoadLibraryDarwin("lib" + self.LIBNAME + ".dylib")
        else:
            from ctypes import windll
            self.lib = windll.LoadLibrary(str(self.LIBNAME + ".dll"))

        super(PS3000, self).__init__(serialNumber, connect)
Esempio n. 8
0
    def __init__(self, serialNumber=None, connect=True):
        """Load DLLs."""
        if platform.system() == 'Linux':
            from ctypes import cdll
            # ok I don't know what is wrong with my installer,
            # but I need to include .so.2
            self.lib = cdll.LoadLibrary("lib" + self.LIBNAME + ".so.2")
        elif platform.system() == 'Darwin':
            from picoscope.darwin_utils import LoadLibraryDarwin
            self.lib = LoadLibraryDarwin("lib" + self.LIBNAME + ".dylib")
        else:
            from ctypes import windll
            self.lib = windll.LoadLibrary(str(self.LIBNAME + ".dll"))

        super(PS6000, self).__init__(serialNumber, connect)
Esempio n. 9
0
class PS4000a(_PicoscopeBase):
    """The following are low-level functions for the PS4000A."""

    LIBNAME = "ps4000a"

    MAX_VALUE = 32764
    MIN_VALUE = -32764

    # EXT/AUX seems to have an input impedance of 50 ohm (PS6403B)
    EXT_MAX_VALUE = 32767
    EXT_MIN_VALUE = -32767
    EXT_RANGE_VOLTS = 1

    # I don't think that the 50V range is allowed, but I left it there anyway
    # The 10V and 20V ranges are only allowed in high impedence modes
    CHANNEL_RANGE = [{"rangeV": 20E-3, "apivalue": 1, "rangeStr": "20 mV"},
                     {"rangeV": 50E-3, "apivalue": 2, "rangeStr": "50 mV"},
                     {"rangeV": 100E-3, "apivalue": 3, "rangeStr": "100 mV"},
                     {"rangeV": 200E-3, "apivalue": 4, "rangeStr": "200 mV"},
                     {"rangeV": 500E-3, "apivalue": 5, "rangeStr": "500 mV"},
                     {"rangeV": 1.0, "apivalue": 6, "rangeStr": "1 V"},
                     {"rangeV": 2.0, "apivalue": 7, "rangeStr": "2 V"},
                     {"rangeV": 5.0, "apivalue": 8, "rangeStr": "5 V"},
                     {"rangeV": 10.0, "apivalue": 9, "rangeStr": "10 V"},
                     {"rangeV": 20.0, "apivalue": 10, "rangeStr": "20 V"},
                     {"rangeV": 50.0, "apivalue": 11, "rangeStr": "50 V"},
                     ]

    NUM_CHANNELS = 8
    CHANNELS = {"A": 0, "B": 1, "C": 2, "D": 3, "E": 4, "F": 5, "G": 6,
                "H": 7,  "MaxChannels": 8}

    ADC_RESOLUTIONS = {"8": 0, "12": 1, "14": 2, "15": 3, "16": 4}

    CHANNEL_COUPLINGS = {"DC50": 2, "DC": 1, "AC": 0}

    WAVE_TYPES = {"Sine": 0, "Square": 1, "Triangle": 2,
                  "RampUp": 3, "RampDown": 4,
                  "Sinc": 5, "Gaussian": 6, "HalfSine": 7, "DCVoltage": 8,
                  "WhiteNoise": 9}

    SWEEP_TYPES = {"Up": 0, "Down": 1, "UpDown": 2, "DownUp": 3}

    TIME_UNITS = {"femtoseconds": 0,
                  "picoseconds": 1,
                  "nanoseconds": 2,
                  "microseconds": 3,
                  "milliseconds": 4,
                  "seconds": 5}

    def __init__(self, serialNumber=None, connect=True):
        """Load DLLs."""
        self.handle = None

        if platform.system() == 'Linux':
            from ctypes import cdll
            # ok I don't know what is wrong with my installer,
            # but I need to include .so.2
            self.lib = cdll.LoadLibrary("lib" + self.LIBNAME + ".so.2")
        elif platform.system() == 'Darwin':
            from picoscope.darwin_utils import LoadLibraryDarwin
            self.lib = LoadLibraryDarwin("lib" + self.LIBNAME + ".dylib")
        else:
            from ctypes import windll
            self.lib = windll.LoadLibrary(str(self.LIBNAME + ".dll"))

        self.resolution = self.ADC_RESOLUTIONS["12"]

        super(PS4000a, self).__init__(serialNumber, connect)

    def _lowLevelOpenUnit(self, sn):
        c_handle = c_int16()
        if sn is not None:
            serialNullTermStr = create_string_buffer(str(sn))
        else:
            serialNullTermStr = None
        # Passing None is the same as passing NULL
        m = self.lib.ps4000aOpenUnit(byref(c_handle), serialNullTermStr)
        self.checkResult(m)
        self.handle = c_handle.value

        self.model = self.getUnitInfo('VariantInfo')

    def _lowLevelOpenUnitAsync(self, sn):
        c_status = c_int16()
        if sn is not None:
            serialNullTermStr = create_string_buffer(sn)
        else:
            serialNullTermStr = None

        # Passing None is the same as passing NULL
        m = self.lib.ps4000aOpenUnitAsync(byref(c_status), serialNullTermStr)
        self.checkResult(m)

        return c_status.value

    def _lowLevelOpenUnitProgress(self):
        complete = c_int16()
        progressPercent = c_int16()
        handle = c_int16()

        m = self.lib.ps4000aOpenUnitProgress(byref(handle),
                                             byref(progressPercent),
                                             byref(complete))
        self.checkResult(m)

        if complete.value != 0:
            self.handle = handle.value

        # if we only wanted to return one value, we could do somethign like
        # progressPercent = progressPercent * (1 - 0.1 * complete)
        return (progressPercent.value, complete.value)

    def _lowLevelCloseUnit(self):
        m = self.lib.ps4000aCloseUnit(c_int16(self.handle))
        self.checkResult(m)

    def _lowLevelEnumerateUnits(self):
        count = c_int16(0)
        serials = c_int8(0)
        serialLth = c_int16(0)

        m = self.lib.ps4000aEnumerateUnits(byref(count), byref(serials),
                                           byref(serialLth))
        self.checkResult(m)
        # a serial number is rouhgly 8 characters
        # an extra character for the comma
        # and an extra one for the space after the comma?
        # the extra two also work for the null termination
        serialLth = c_int16(count.value * (8 + 2))
        serials = create_string_buffer(serialLth.value + 1)

        m = self.lib.ps4000aEnumerateUnits(byref(count), serials,
                                           byref(serialLth))
        self.checkResult(m)

        serialList = str(serials.value.decode('utf-8')).split(',')

        serialList = [x.strip() for x in serialList]

        return serialList

    def _lowLevelSetChannel(self, chNum, enabled, coupling, VRange, VOffset,
                            BWLimited):
        m = self.lib.ps4000aSetChannel(c_int16(self.handle), c_enum(chNum),
                                       c_int16(enabled), c_enum(coupling),
                                       c_enum(VRange), c_float(VOffset))
        self.checkResult(m)

    def _lowLevelStop(self):
        m = self.lib.ps4000aStop(c_int16(self.handle))
        self.checkResult(m)

    def _lowLevelGetUnitInfo(self, info):
        s = create_string_buffer(256)
        requiredSize = c_int16(0)

        m = self.lib.ps4000aGetUnitInfo(c_int16(self.handle), byref(s),
                                        c_int16(len(s)), byref(requiredSize),
                                        c_enum(info))
        self.checkResult(m)
        if requiredSize.value > len(s):
            s = create_string_buffer(requiredSize.value + 1)
            m = self.lib.ps4000aGetUnitInfo(c_int16(self.handle), byref(s),
                                            c_int16(len(s)),
                                            byref(requiredSize), c_enum(info))
            self.checkResult(m)

        # should this bee ascii instead?
        # I think they are equivalent...
        return s.value.decode('utf-8')

    def _lowLevelFlashLed(self, times):
        m = self.lib.ps4000aFlashLed(c_int16(self.handle), c_int16(times))
        self.checkResult(m)

    def _lowLevelSetSimpleTrigger(self, enabled, trigsrc, threshold_adc,
                                  direction, delay, timeout_ms):
        m = self.lib.ps4000aSetSimpleTrigger(
            c_int16(self.handle), c_int16(enabled),
            c_enum(trigsrc), c_int16(threshold_adc),
            c_enum(direction), c_uint32(delay), c_int16(timeout_ms))
        self.checkResult(m)

    def _lowLevelRunBlock(self, numPreTrigSamples, numPostTrigSamples,
                          timebase, oversample, segmentIndex):
        timeIndisposedMs = c_int32()
        m = self.lib.ps4000aRunBlock(
            c_int16(self.handle), c_int32(numPreTrigSamples),
            c_int32(numPostTrigSamples), c_uint32(timebase),
            byref(timeIndisposedMs),
            c_uint32(segmentIndex), c_void_p(), c_void_p())
        self.checkResult(m)
        return timeIndisposedMs.value

    def _lowLevelIsReady(self):
        ready = c_int16()
        m = self.lib.ps4000aIsReady(c_int16(self.handle), byref(ready))
        self.checkResult(m)
        if ready.value:
            return True
        else:
            return False

    def _lowLevelGetTimebase(self, tb, noSamples, oversample, segmentIndex):
        """Return (timeIntervalSeconds, maxSamples)."""
        maxSamples = c_int32()
        sampleRate = c_float()

        m = self.lib.ps4000aGetTimebase2(c_int16(self.handle), c_uint32(tb),
                                         c_int32(noSamples), byref(sampleRate),
                                         byref(maxSamples),
                                         c_uint32(segmentIndex))
        self.checkResult(m)

        return (sampleRate.value / 1.0E9, maxSamples.value)

    def getTimeBaseNum(self, sampleTimeS):
        """ Convert the sample interval (float of seconds) to the
        corresponding integer timebase value as defined by the API.
        See "Timebases" section of the PS4000a programmers guide
        for more information.
        """

        if self.model == '4828':
            maxSampleTime = (((2 ** 32 - 1) + 1) / 8E7)

            if sampleTimeS <= 12.5E-9:
                timebase = 0
            else:
                # Otherwise in range 2^32-1
                if sampleTimeS > maxSampleTime:
                    sampleTimeS = maxSampleTime

                timebase = math.floor((sampleTimeS * 2e7) + 1)

        elif self.model == '4444':
            maxSampleTime = (((2 ** 32 - 1) - 2) / 5.0E7)

            if (sampleTimeS <= 2.5E-9 and
                    self.resolution == self.ADC_RESOLUTIONS["12"]):
                timebase = 0
            elif (sampleTimeS <= 20E-9 and
                    self.resolution == self.ADC_RESOLUTIONS["14"]):
                timebase = 3
            else:
                # Otherwise in range 2^32-1
                if sampleTimeS > maxSampleTime:
                    sampleTimeS = maxSampleTime

                timebase = math.floor((sampleTimeS * 5.0E7) + 2)

        else:  # The original case from non "A" series
            warnings.warn("The model PS4000a you are using may not be "
                          "fully supported", stacklevel=2)
            maxSampleTime = (((2 ** 32 - 1) - 4) / 2e7)

            if sampleTimeS <= 12.5E-9:
                timebase = math.floor(math.log(sampleTimeS * 8E7, 2))
                timebase = max(timebase, 0)
            else:
                # Otherwise in range 2^32-1
                if sampleTimeS > maxSampleTime:
                    sampleTimeS = maxSampleTime

                timebase = math.floor((sampleTimeS * 2e7) + 1)

        # is this cast needed?
        timebase = int(timebase)
        return timebase

    def getTimestepFromTimebase(self, timebase):
        """Return timebase to sampletime as seconds."""
        if self.model == '4828':
            dt = (timebase + 1) / 8.0E7
        elif self.model == '4444':
            if timebase < 3:
                dt = 2.5 ** timebase / 4.0E8
            else:
                dt = (timebase - 2) / 5.0E7

        else:  # The original case from non "A" series
            warnings.warn("The model PS4000a you are using may not be "
                          "fully supported", stacklevel=2)
            if timebase < 3:
                dt = 2. ** timebase / 8e7
            else:
                dt = (timebase - 1) / 2e7
            return dt
        return dt

    def _lowLevelSetDataBuffer(self, channel, data, downSampleMode,
                               segmentIndex):
        """Set the data buffer.

        Be sure to call _lowLevelClearDataBuffer
        when you are done with the data array
        or else subsequent calls to GetValue will still use the same array.

        segmentIndex is unused, but required by other versions of the API
        (eg PS5000a)
        """
        dataPtr = data.ctypes.data_as(POINTER(c_int16))
        numSamples = len(data)

        m = self.lib.ps4000aSetDataBuffer(c_int16(self.handle),
                                          c_enum(channel),
                                          dataPtr, c_uint32(numSamples),
                                          c_uint32(segmentIndex),
                                          c_uint32(downSampleMode))
        self.checkResult(m)

    def _lowLevelClearDataBuffer(self, channel, segmentIndex):
        m = self.lib.ps4000aSetDataBuffer(c_int16(self.handle),
                                          c_enum(channel),
                                          c_void_p(), c_uint32(0), c_uint32(0),
                                          c_enum(0))
        self.checkResult(m)

    def _lowLevelGetValues(self, numSamples, startIndex, downSampleRatio,
                           downSampleMode, segmentIndex):
        numSamplesReturned = c_uint32()
        numSamplesReturned.value = numSamples
        overflow = c_int16()
        m = self.lib.ps4000aGetValues(
            c_int16(self.handle), c_uint32(startIndex),
            byref(numSamplesReturned), c_uint32(downSampleRatio),
            c_enum(downSampleMode), c_uint16(segmentIndex),
            byref(overflow))
        self.checkResult(m)
        return (numSamplesReturned.value, overflow.value)

    def _lowLevelSetDeviceResolution(self, resolution):
        self.resolution = resolution
        m = self.lib.ps4000aSetDeviceResolution(
            c_int16(self.handle),
            c_enum(resolution))
        self.checkResult(m)

    def _lowLevelGetValuesBulk(self, numSamples, fromSegment, toSegment,
                               downSampleRatio, downSampleMode, overflow):
        """Copy data from several memory segments at once."""
        overflowPoint = overflow.ctypes.data_as(POINTER(c_int16))
        m = self.lib.ps4000aGetValuesBulk(
            c_int16(self.handle),
            byref(c_int32(numSamples)),
            c_int32(fromSegment),
            c_int32(toSegment),
            c_int32(downSampleRatio),
            c_enum(downSampleMode),
            overflowPoint
        )
        self.checkResult(m)

    def _lowLevelSetDataBufferBulk(self, channel, data, segmentIndex,
                                   downSampleMode):
        """Just calls setDataBuffer with argument order changed.

        For compatibility with current picobase.py.
        """
        self._lowLevelSetDataBuffer(channel, data, downSampleMode,
                                    segmentIndex)

    ####################################################################
    # Untested functions below                                         #
    #                                                                  #
    ####################################################################
    def _lowLevelSetSigGenBuiltInSimple(self, offsetVoltage, pkToPk, waveType,
                                        frequency, shots, triggerType,
                                        triggerSource, stopFreq, increment,
                                        dwellTime, sweepType, numSweeps):
        if stopFreq is None:
            stopFreq = frequency

        m = self.lib.ps4000aSetSigGenBuiltIn(
            c_int16(self.handle),
            c_int32(int(offsetVoltage * 1000000)),
            c_int32(int(pkToPk * 1000000)),
            c_int16(waveType),
            c_float(frequency), c_float(stopFreq),
            c_float(increment), c_float(dwellTime),
            c_enum(sweepType), c_enum(0),
            c_uint32(shots), c_uint32(numSweeps),
            c_enum(triggerType), c_enum(triggerSource),
            c_int16(0))
        self.checkResult(m)

    def _lowLevelGetMaxDownSampleRatio(self, noOfUnaggregatedSamples,
                                       downSampleRatioMode, segmentIndex):
        maxDownSampleRatio = c_uint32()

        m = self.lib.ps4000aGetMaxDownSampleRatio(
            c_int16(self.handle),
            c_uint32(noOfUnaggregatedSamples),
            byref(maxDownSampleRatio),
            c_enum(downSampleRatioMode),
            c_uint16(segmentIndex))
        self.checkResult(m)

        return maxDownSampleRatio.value

    def _lowLevelGetNoOfCaptures(self):
        nCaptures = c_uint32()

        m = self.lib.ps4000aGetNoOfCaptures(c_int16(self.handle),
                                            byref(nCaptures))
        self.checkResult(m)

        return nCaptures.value

    def _lowLevelGetTriggerTimeOffset(self, segmentIndex):
        time = c_uint64()
        timeUnits = c_enum()

        m = self.lib.ps4000aGetTriggerTimeOffset64(
            c_int16(self.handle),
            byref(time),
            byref(timeUnits),
            c_uint16(segmentIndex))

        self.checkResult(m)

        if timeUnits.value == 0:  # PS4000a_FS
            return time.value * 1E-15
        elif timeUnits.value == 1:  # PS4000a_PS
            return time.value * 1E-12
        elif timeUnits.value == 2:  # PS4000a_NS
            return time.value * 1E-9
        elif timeUnits.value == 3:  # PS4000a_US
            return time.value * 1E-6
        elif timeUnits.value == 4:  # PS4000a_MS
            return time.value * 1E-3
        elif timeUnits.value == 5:  # PS4000a_S
            return time.value * 1E0
        else:
            raise TypeError("Unknown timeUnits %d" % timeUnits.value)

    def _lowLevelMemorySegments(self, nSegments):
        nMaxSamples = c_uint32()

        m = self.lib.ps4000aMemorySegments(c_int16(self.handle),
                                           c_uint16(nSegments),
                                           byref(nMaxSamples))
        self.checkResult(m)

        return nMaxSamples.value

    def _lowLevelSetDataBuffers(self, channel, bufferMax, bufferMin,
                                downSampleRatioMode):
        bufferMaxPtr = bufferMax.ctypes.data_as(POINTER(c_int16))
        bufferMinPtr = bufferMin.ctypes.data_as(POINTER(c_int16))
        bufferLth = len(bufferMax)

        m = self.lib.ps4000aSetDataBuffers(
            c_int16(self.handle),
            c_enum(channel),
            bufferMaxPtr,
            bufferMinPtr,
            c_uint32(bufferLth))
        self.checkResult(m)

    def _lowLevelClearDataBuffers(self, channel):
        m = self.lib.ps4000aSetDataBuffers(
            c_int16(self.handle),
            c_enum(channel),
            c_void_p(),
            c_void_p(),
            c_uint32(0))
        self.checkResult(m)

    def _lowLevelSetNoOfCaptures(self, nCaptures):
        m = self.lib.ps4000aSetNoOfCaptures(
            c_int16(self.handle),
            c_uint16(nCaptures))
        self.checkResult(m)

    # ETS Functions
    def _lowLevelSetEts(self):
        pass

    def _lowLevelSetEtsTimeBuffer(self):
        pass

    def _lowLevelSetEtsTimeBuffers(self):
        pass

    def _lowLevelSetExternalClock(self):
        pass

    # Complicated triggering
    # need to understand structs for this one to work
    def _lowLevelIsTriggerOrPulseWidthQualifierEnabled(self):
        pass

    def _lowLevelGetValuesTriggerTimeOffsetBulk(self):
        pass

    def _lowLevelSetTriggerChannelConditions(self):
        pass

    def _lowLevelSetTriggerChannelDirections(self):
        pass

    def _lowLevelSetTriggerChannelProperties(self):
        pass

    def _lowLevelSetPulseWidthQualifier(self):
        pass

    def _lowLevelSetTriggerDelay(self):
        pass

    # Async functions
    # would be nice, but we would have to learn to implement callbacks
    def _lowLevelGetValuesAsync(self):
        pass

    def _lowLevelGetValuesBulkAsync(self):
        pass

    # overlapped functions
    def _lowLevelGetValuesOverlapped(self):
        pass

    def _lowLevelGetValuesOverlappedBulk(self):
        pass

    # Streaming related functions
    def _lowLevelGetStreamingLatestValues(self, lpPs4000Ready,
                                          pParameter=c_void_p()):
        m = self.lib.ps4000aGetStreamingLatestValues(
            c_uint16(self.handle),
            lpPs4000Ready,
            pParameter)
        self.checkResult(m)

    def _lowLevelNoOfStreamingValues(self):
        noOfValues = c_uint32()

        m = self.lib.ps4000aNoOfStreamingValues(c_int16(self.handle),
                                                byref(noOfValues))
        self.checkResult(m)

        return noOfValues.value

    def _lowLevelRunStreaming(self, sampleInterval, sampleIntervalTimeUnits,
                              maxPreTriggerSamples, maxPostTriggerSamples,
                              autoStop, downSampleRatio, downSampleRatioMode,
                              overviewBufferSize):
        m = self.lib.ps4000aRunStreaming(
            c_int16(self.handle),
            byref(c_uint32(sampleInterval)),
            c_enum(sampleIntervalTimeUnits),
            c_uint32(maxPreTriggerSamples),
            c_uint32(maxPostTriggerSamples),
            c_int16(autoStop),
            c_uint32(downSampleRatio),
            c_enum(downSampleRatioMode),
            c_uint32(overviewBufferSize))

        self.checkResult(m)

    def _lowLevelStreamingReady(self):
        pass
Esempio n. 10
0
class PS5000a(_PicoscopeBase):
    """The following are low-level functions for the PS5000."""

    LIBNAME = "ps5000a"

    NUM_CHANNELS = 4
    CHANNELS = {"A": 0, "B": 1, "C": 2, "D": 3,
                "External": 4, "MaxChannels": 4, "TriggerAux": 5}

    ADC_RESOLUTIONS = {"8": 0, "12": 1, "14": 2, "15": 3, "16": 4}

    CHANNEL_RANGE = [{"rangeV": 10E-3, "apivalue": 0, "rangeStr": "10 mV"},
                     {"rangeV": 20E-3, "apivalue": 1, "rangeStr": "20 mV"},
                     {"rangeV": 50E-3, "apivalue": 2, "rangeStr": "50 mV"},
                     {"rangeV": 100E-3, "apivalue": 3, "rangeStr": "100 mV"},
                     {"rangeV": 200E-3, "apivalue": 4, "rangeStr": "200 mV"},
                     {"rangeV": 500E-3, "apivalue": 5, "rangeStr": "500 mV"},
                     {"rangeV": 1.0, "apivalue": 6, "rangeStr": "1 V"},
                     {"rangeV": 2.0, "apivalue": 7, "rangeStr": "2 V"},
                     {"rangeV": 5.0, "apivalue": 8, "rangeStr": "5 V"},
                     {"rangeV": 10.0, "apivalue": 9, "rangeStr": "10 V"},
                     {"rangeV": 20.0, "apivalue": 10, "rangeStr": "20 V"},
                     {"rangeV": 50.0, "apivalue": 11, "rangeStr": "50 V"},
                     ]

    CHANNEL_COUPLINGS = {"DC": 1, "AC": 0}

    # has_sig_gen = True
    WAVE_TYPES = {"Sine": 0, "Square": 1, "Triangle": 2,
                  "RampUp": 3, "RampDown": 4,
                  "Sinc": 5, "Gaussian": 6, "HalfSine": 7, "DCVoltage": 8,
                  "WhiteNoise": 9}

    SWEEP_TYPES = {"Up": 0, "Down": 1, "UpDown": 2, "DownUp": 3}

    SIGGEN_TRIGGER_TYPES = {"Rising": 0, "Falling": 1,
                            "GateHigh": 2, "GateLow": 3}
    SIGGEN_TRIGGER_SOURCES = {"None": 0, "ScopeTrig": 1, "AuxIn": 2,
                              "ExtIn": 3, "SoftTrig": 4, "TriggerRaw": 5}

    # This is actually different depending on the AB/CD models
    # I wonder how we could detect the difference between the oscilloscopes
    # I believe we can obtain this information from the setInfo function
    # by readign the hardware version
    # for the PS6403B version, the hardware version is "1 1",
    # an other possibility is that the PS6403B shows up as 6403 when using
    # VARIANT_INFO and others show up as PS6403X where X = A,C or D

    AWGPhaseAccumulatorSize = 32

    AWGDACInterval = 5E-9  # in seconds
    AWGDACFrequency = 1 / AWGDACInterval

    AWG_INDEX_MODES = {"Single": 0, "Dual": 1, "Quad": 2}

    MAX_VALUE_8BIT = 32512
    MIN_VALUE_8BIT = -32512
    MAX_VALUE_OTHER = 32767
    MIN_VALUE_OTHER = -32767

    EXT_RANGE_VOLTS = 5

    def __init__(self, serialNumber=None, connect=True):
        """Load DLL etc."""
        if platform.system() == 'Linux':
            from ctypes import cdll
            self.lib = cdll.LoadLibrary("lib" + self.LIBNAME + ".so")
        elif platform.system() == 'Darwin':
            from picoscope.darwin_utils import LoadLibraryDarwin
            self.lib = LoadLibraryDarwin("lib" + self.LIBNAME + ".dylib")
        else:
            from ctypes import windll
            self.lib = windll.LoadLibrary(str(self.LIBNAME + ".dll"))

        self.resolution = self.ADC_RESOLUTIONS["8"]

        super(PS5000a, self).__init__(serialNumber, connect)

    def _lowLevelOpenUnit(self, sn):
        c_handle = c_int16()
        if sn is not None:
            serialNullTermStr = create_string_buffer(sn)
        else:
            serialNullTermStr = None
        # Passing None is the same as passing NULL
        m = self.lib.ps5000aOpenUnit(byref(c_handle), serialNullTermStr,
                                     self.resolution)
        self.handle = c_handle.value

        # This will check if the power supply is not connected
        # and change the power supply accordingly
        # Personally (me = Mark), I don't like this
        # since the user should address this immediately, and we
        # shouldn't let this go as a soft error
        # but I think this should do for now
        if m == 0x11A:
            self.changePowerSource(m)
        else:
            self.checkResult(m)

        # B models have different AWG buffer sizes
        # 5242B, 5442B: 2**14
        # 5243B, 5443B: 2**15
        # 5444B, 5244B: 3 * 2**14
        # Model 5444B identifies itself properly in VariantInfo, I will assume
        # the others do as well.

        self.model = self.getUnitInfo('VariantInfo')
        # print("Checking variant, found: " + str(self.model))
        if self.model in ('5244B', '5444B'):
            self.AWGBufferAddressWidth = math.log(3 * 2**14, 2)
            self.AWGMaxVal = 32767
            self.AWGMinVal = -32768
            self.AWGMaxSamples = 49152
        elif self.model in ('5243B', '5443B'):
            self.AWGBufferAddressWidth = 15
            self.AWGMaxVal = 32767
            self.AWGMinVal = -32768
            self.AWGMaxSamples = 2**self.AWGBufferAddressWidth
        else:
            # This is what the previous PS5000a used for all scopes.
            # I am leaving it the same, although I think the AWGMaxVal and
            # AWGMinVal issue was fixed and should be -32768 to 32767 for all
            # 5000 models
            self.AWGBufferAddressWidth = 14
            # Note this is NOT what is written in the Programming guide as of
            # version # 10_5_0_28
            # This issue was acknowledged in this thread
            # http://www.picotech.com/support/topic13217.html
            self.AWGMaxVal = 0x0FFF
            self.AWGMinVal = 0x0000
            self.AWGMaxSamples = 2**self.AWGBufferAddressWidth

    def _lowLevelCloseUnit(self):
        m = self.lib.ps5000aCloseUnit(c_int16(self.handle))
        self.checkResult(m)

    def _lowLevelSetChannel(self, chNum, enabled, coupling, VRange, VOffset,
                            bandwidth):
        m = self.lib.ps5000aSetChannel(c_int16(self.handle), c_enum(chNum),
                                       c_int16(enabled), c_enum(coupling),
                                       c_enum(VRange), c_float(VOffset))
        self.checkResult(m)

        # The error this might through are
        #    INVALID_HANDLE
        #    INVALID_CHANNEL
        #    INVALID_BANDWIDTH
        # Invalid bandwidth is the only case that could go wrong.
        # The others would be thrown above (assuming no race condition:
        # i.e. unplugging the scope in between this call.
        # I decided to keep the logic below to avoid a possible error
        # picobase/SetChannel should be changed to the following
        # Set the channel
        # save the new channel settings
        # check if ps5000a
        # change the bandwidth separately
        # changing the bandwidth would be it's own function (implemented below)
        if bandwidth:
            m = self.lib.ps5000aSetBandwidthFilter(c_int16(self.handle),
                                                   c_enum(chNum), c_enum(1))
        else:
            m = self.lib.ps5000aSetBandwidthFilter(c_int16(self.handle),
                                                   c_enum(chNum), c_enum(0))
        self.checkResult(m)

    def _lowLevelSetBandwidthFilter(self, channel, bandwidth):
        m = self.lib.ps5000aSetBandwidthFilter(c_int16(self.handle),
                                               c_enum(channel),
                                               c_enum(bandwidth))
        self.checkResult(m)

    def _lowLevelStop(self):
        m = self.lib.ps5000aStop(c_int16(self.handle))
        self.checkResult(m)

    def _lowLevelGetUnitInfo(self, info):
        s = create_string_buffer(256)
        requiredSize = c_int16(0)

        m = self.lib.ps5000aGetUnitInfo(c_int16(self.handle), byref(s),
                                        c_int16(len(s)), byref(requiredSize),
                                        c_enum(info))
        self.checkResult(m)
        if requiredSize.value > len(s):
            s = create_string_buffer(requiredSize.value + 1)
            m = self.lib.ps5000aGetUnitInfo(c_int16(self.handle), byref(s),
                                            c_int16(len(s)),
                                            byref(requiredSize), c_enum(info))
            self.checkResult(m)

        # should this bee ascii instead?
        # I think they are equivalent...
        return s.value.decode('utf-8')

    def _lowLevelFlashLed(self, times):
        m = self.lib.ps5000aFlashLed(c_int16(self.handle), c_int16(times))
        self.checkResult(m)

    def _lowLevelSetSimpleTrigger(self, enabled, trigsrc, threshold_adc,
                                  direction, timeout_ms, auto):
        m = self.lib.ps5000aSetSimpleTrigger(
            c_int16(self.handle), c_int16(enabled),
            c_enum(trigsrc), c_int16(threshold_adc),
            c_enum(direction), c_uint32(timeout_ms), c_int16(auto))
        self.checkResult(m)

    def _lowLevelRunBlock(self, numPreTrigSamples, numPostTrigSamples,
                          timebase, oversample, segmentIndex):
        # Oversample is NOT used!
        timeIndisposedMs = c_int32()
        m = self.lib.ps5000aRunBlock(
            c_int16(self.handle), c_uint32(numPreTrigSamples),
            c_uint32(numPostTrigSamples), c_uint32(timebase),
            byref(timeIndisposedMs), c_uint32(segmentIndex),
            c_void_p(), c_void_p())
        self.checkResult(m)
        return timeIndisposedMs.value

    def _lowLevelIsReady(self):
        ready = c_int16()
        m = self.lib.ps5000aIsReady(c_int16(self.handle), byref(ready))
        self.checkResult(m)
        if ready.value:
            return True
        else:
            return False

    def _lowLevelPingUnit(self):
        m = self.lib.ps5000aPingUnit(c_int16(self.handle))
        return m

    def _lowLevelGetTimebase(self, tb, noSamples, oversample, segmentIndex):
        """Return (timeIntervalSeconds, maxSamples)."""
        maxSamples = c_int32()
        sampleRate = c_float()

        m = self.lib.ps5000aGetTimebase2(c_int16(self.handle), c_uint32(tb),
                                         c_uint32(noSamples),
                                         byref(sampleRate),
                                         byref(maxSamples),
                                         c_uint32(segmentIndex))
        self.checkResult(m)

        return (sampleRate.value / 1.0E9, maxSamples.value)

    def getTimeBaseNum(self, sampleTimeS):
        """Convert sample time in S to something to pass to API Call."""
        if self.resolution == self.ADC_RESOLUTIONS["8"]:
            maxSampleTime = (((2 ** 32 - 1) - 2) / 125000000)
            if sampleTimeS < 8.0E-9:
                st = math.floor(math.log(sampleTimeS * 1E9, 2))
                st = max(st, 0)
            else:
                if sampleTimeS > maxSampleTime:
                    sampleTimeS = maxSampleTime
                st = math.floor((sampleTimeS * 125000000) + 2)

        elif self.resolution == self.ADC_RESOLUTIONS["12"]:
            maxSampleTime = (((2 ** 32 - 1) - 3) / 62500000)
            if sampleTimeS < 16.0E-9:
                st = math.floor(math.log(sampleTimeS * 5E8, 2)) + 1
                st = max(st, 1)
            else:
                if sampleTimeS > maxSampleTime:
                    sampleTimeS = maxSampleTime
                st = math.floor((sampleTimeS * 62500000) + 3)

        elif (self.resolution == self.ADC_RESOLUTIONS["14"]) or (
                self.resolution == self.ADC_RESOLUTIONS["15"]):
            maxSampleTime = (((2 ** 32 - 1) - 2) / 125000000)
            if sampleTimeS > maxSampleTime:
                sampleTimeS = maxSampleTime
            st = math.floor((sampleTimeS * 125000000) + 2)
            st = max(st, 3)

        elif self.resolution == self.ADC_RESOLUTIONS["16"]:
            maxSampleTime = (((2 ** 32 - 1) - 3) / 62500000)
            if sampleTimeS > maxSampleTime:
                sampleTimeS = maxSampleTime
            st = math.floor((sampleTimeS * 62500000) + 3)
            st = max(st, 3)

        else:
            raise ValueError("Invalid Resolution for Device?")

        # is this cast needed?
        st = int(st)
        return st

    def getTimestepFromTimebase(self, timebase):
        """Return Timestep from timebase."""
        if self.resolution == self.ADC_RESOLUTIONS["8"]:
            if timebase < 3:
                dt = 2. ** timebase / 1.0E9
            else:
                dt = (timebase - 2.0) / 125000000.
        elif self.resolution == self.ADC_RESOLUTIONS["12"]:
            if timebase < 4:
                dt = 2. ** (timebase - 1) / 5.0E8
            else:
                dt = (timebase - 3.0) / 62500000.
        elif (self.resolution == self.ADC_RESOLUTIONS["14"]) or (
                self.resolution == self.ADC_RESOLUTIONS["15"]):
            dt = (timebase - 2.0) / 125000000.
        elif self.resolution == self.ADC_RESOLUTIONS["16"]:
            dt = (timebase - 3.0) / 62500000.
        return dt

    def _lowLevelSetAWGSimpleDeltaPhase(self, waveform, deltaPhase,
                                        offsetVoltage, pkToPk, indexMode,
                                        shots, triggerType, triggerSource):
        """Waveform should be an array of shorts."""
        waveformPtr = waveform.ctypes.data_as(POINTER(c_int16))

        m = self.lib.ps5000aSetSigGenArbitrary(
            c_int16(self.handle),
            c_uint32(int(offsetVoltage * 1E6)),  # offset voltage in microvolts
            c_uint32(int(pkToPk * 1E6)),         # pkToPk in microvolts
            c_uint32(int(deltaPhase)),           # startDeltaPhase
            c_uint32(int(deltaPhase)),           # stopDeltaPhase
            c_uint32(0),                         # deltaPhaseIncrement
            c_uint32(0),                         # dwellCount
            waveformPtr,                         # arbitraryWaveform
            c_int32(len(waveform)),              # arbitraryWaveformSize
            c_enum(0),                           # sweepType for deltaPhase
            c_enum(0),            # operation (adding random noise and whatnot)
            c_enum(indexMode),                   # single, dual, quad
            c_uint32(shots),
            c_uint32(0),                         # sweeps
            c_uint32(triggerType),
            c_uint32(triggerSource),
            c_int16(0))                          # extInThreshold
        self.checkResult(m)

    def _lowLevelSetDataBuffer(self, channel, data, downSampleMode,
                               segmentIndex):
        """Set the data buffer.

        Be sure to call _lowLevelClearDataBuffer
        when you are done with the data array
        or else subsequent calls to GetValue will still use the same array.
        """
        dataPtr = data.ctypes.data_as(POINTER(c_int16))
        numSamples = len(data)

        m = self.lib.ps5000aSetDataBuffer(c_int16(self.handle),
                                          c_enum(channel),
                                          dataPtr, c_int32(numSamples),
                                          c_uint32(segmentIndex),
                                          c_enum(downSampleMode))
        self.checkResult(m)

    def _lowLevelSetDataBufferBulk(self, channel, data, segmentIndex,
                                   downSampleMode):
        """Just calls setDataBuffer with argument order changed.

        For compatibility with current picobase.py.
        """
        self._lowLevelSetDataBuffer(channel,
                                    data,
                                    downSampleMode,
                                    segmentIndex)

    def _lowLevelClearDataBuffer(self, channel, segmentIndex):
        m = self.lib.ps5000aSetDataBuffer(c_int16(self.handle),
                                          c_enum(channel),
                                          c_void_p(), c_uint32(0),
                                          c_uint32(segmentIndex),
                                          c_enum(0))
        self.checkResult(m)

    def _lowLevelGetValues(self, numSamples, startIndex, downSampleRatio,
                           downSampleMode, segmentIndex):
        numSamplesReturned = c_uint32()
        numSamplesReturned.value = numSamples
        overflow = c_int16()
        m = self.lib.ps5000aGetValues(
            c_int16(self.handle), c_uint32(startIndex),
            byref(numSamplesReturned), c_uint32(downSampleRatio),
            c_enum(downSampleMode), c_uint32(segmentIndex),
            byref(overflow))
        self.checkResult(m)
        return (numSamplesReturned.value, overflow.value)

    def _lowLevelSetSigGenBuiltInSimple(self, offsetVoltage, pkToPk, waveType,
                                        frequency, shots, triggerType,
                                        triggerSource, stopFreq, increment,
                                        dwellTime, sweepType, numSweeps):
        # TODO, I just noticed that V2 exists
        # Maybe change to V2 in the future

        if stopFreq is None:
            stopFreq = frequency

        m = self.lib.ps5000aSetSigGenBuiltIn(
            c_int16(self.handle),
            c_int32(int(offsetVoltage * 1000000)),
            c_int32(int(pkToPk * 1000000)),
            c_int16(waveType),
            c_float(frequency), c_float(stopFreq),
            c_float(increment), c_float(dwellTime),
            c_enum(sweepType), c_enum(0),
            c_uint32(shots), c_uint32(numSweeps),
            c_enum(triggerType), c_enum(triggerSource),
            c_int16(0))
        self.checkResult(m)

    def _lowLevelSetDeviceResolution(self, resolution):
        self.resolution = resolution
        m = self.lib.ps5000aSetDeviceResolution(
            c_int16(self.handle),
            c_enum(resolution))
        self.checkResult(m)

    def _lowLevelChangePowerSource(self, powerstate):
        m = self.lib.ps5000aChangePowerSource(
            c_int16(self.handle),
            c_enum(powerstate))
        self.checkResult(m)

    # Morgan's additions
    def _lowLevelGetValuesBulk(self, numSamples, fromSegment, toSegment,
                               downSampleRatio, downSampleMode, overflow):
        """Copy data from several memory segments at once."""
        overflowPoint = overflow.ctypes.data_as(POINTER(c_int16))
        m = self.lib.ps5000aGetValuesBulk(
            c_int16(self.handle),
            byref(c_int32(numSamples)),
            c_int32(fromSegment),
            c_int32(toSegment),
            c_int32(downSampleRatio),
            c_enum(downSampleMode),
            overflowPoint
            )
        self.checkResult(m)

    def _lowLevelSetNoOfCaptures(self, numCaptures):
        m = self.lib.ps5000aSetNoOfCaptures(
            c_int16(self.handle),
            c_uint32(numCaptures))
        self.checkResult(m)

    def _lowLevelMemorySegments(self, numSegments):
        maxSamples = c_int32()
        m = self.lib.ps5000aMemorySegments(
            c_int16(self.handle), c_uint32(numSegments), byref(maxSamples))
        self.checkResult(m)
        return maxSamples.value

    def _lowLevelGetValuesTriggerTimeOffsetBulk(self, fromSegment, toSegment):
        """Supposedly gets the trigger times for a bunch of segments at once.

        For block mode.
        Can't get it to work yet, however.
        """
        import numpy as np

        nSegments = toSegment - fromSegment + 1
        # time = c_int64()
        times = np.ascontiguousarray(
            np.zeros(nSegments, dtype=np.int64)
            )
        timeUnits = np.ascontiguousarray(
            np.zeros(nSegments, dtype=np.int32)
            )

        m = self.lib.ps5000aGetValuesTriggerTimeOffsetBulk64(
            c_int16(self.handle),
            times.ctypes.data_as(POINTER(c_int64)),
            timeUnits.ctypes.data_as(POINTER(c_enum)),
            c_uint32(fromSegment),
            c_uint32(toSegment)
            )
        self.checkResult(m)
        # timeUnits=np.array([self.TIME_UNITS[tu] for tu in timeUnits])
        return times, timeUnits
Esempio n. 11
0
class PS3000a(_PicoscopeBase):
    """The following are low-level functions for the PS3000a."""

    LIBNAME = "ps3000a"

    NUM_CHANNELS = 4
    CHANNELS = {
        "A": 0,
        "B": 1,
        "C": 2,
        "D": 3,
        "External": 4,
        "MaxChannels": 4,
        "TriggerAux": 5
    }

    ADC_RESOLUTIONS = {"8": 0, "12": 1, "14": 2, "15": 3, "16": 4}

    CHANNEL_RANGE = [
        {
            "rangeV": 10E-3,
            "apivalue": 0,
            "rangeStr": "10 mV"
        },
        {
            "rangeV": 20E-3,
            "apivalue": 1,
            "rangeStr": "20 mV"
        },
        {
            "rangeV": 50E-3,
            "apivalue": 2,
            "rangeStr": "50 mV"
        },
        {
            "rangeV": 100E-3,
            "apivalue": 3,
            "rangeStr": "100 mV"
        },
        {
            "rangeV": 200E-3,
            "apivalue": 4,
            "rangeStr": "200 mV"
        },
        {
            "rangeV": 500E-3,
            "apivalue": 5,
            "rangeStr": "500 mV"
        },
        {
            "rangeV": 1.0,
            "apivalue": 6,
            "rangeStr": "1 V"
        },
        {
            "rangeV": 2.0,
            "apivalue": 7,
            "rangeStr": "2 V"
        },
        {
            "rangeV": 5.0,
            "apivalue": 8,
            "rangeStr": "5 V"
        },
        {
            "rangeV": 10.0,
            "apivalue": 9,
            "rangeStr": "10 V"
        },
        {
            "rangeV": 20.0,
            "apivalue": 10,
            "rangeStr": "20 V"
        },
        {
            "rangeV": 50.0,
            "apivalue": 11,
            "rangeStr": "50 V"
        },
    ]

    CHANNEL_COUPLINGS = {"DC": 1, "AC": 0}

    # has_sig_gen = True
    WAVE_TYPES = {
        "Sine": 0,
        "Square": 1,
        "Triangle": 2,
        "RampUp": 3,
        "RampDown": 4,
        "Sinc": 5,
        "Gaussian": 6,
        "HalfSine": 7,
        "DCVoltage": 8,
        "WhiteNoise": 9
    }

    SWEEP_TYPES = {"Up": 0, "Down": 1, "UpDown": 2, "DownUp": 3}

    SIGGEN_TRIGGER_TYPES = {
        "Rising": 0,
        "Falling": 1,
        "GateHigh": 2,
        "GateLow": 3
    }
    SIGGEN_TRIGGER_SOURCES = {
        "None": 0,
        "ScopeTrig": 1,
        "AuxIn": 2,
        "ExtIn": 3,
        "SoftTrig": 4,
        "TriggerRaw": 5
    }

    # This is actually different depending on the AB/CD models
    # I wonder how we could detect the difference between the oscilloscopes
    # I believe we can obtain this information from the setInfo function
    # by readign the hardware version
    # for the PS6403B version, the hardware version is "1 1",
    # an other possibility is that the PS6403B shows up as 6403 when using
    # VARIANT_INFO and others show up as PS6403X where X = A,C or D

    AWGPhaseAccumulatorSize = 32
    AWGBufferAddressWidth = 14
    AWGMaxSamples = 2**AWGBufferAddressWidth

    AWGDACInterval = 5E-9  # in seconds
    AWGDACFrequency = 1 / AWGDACInterval

    # Note this is NOT what is written in the Programming guide as of version
    # 10_5_0_28
    # This issue was acknowledged in this thread
    # http://www.picotech.com/support/topic13217.html
    AWGMaxVal = 32767
    AWGMinVal = -32767

    AWG_INDEX_MODES = {"Single": 0, "Dual": 1, "Quad": 2}

    MAX_VALUE_8BIT = 32512
    MIN_VALUE_8BIT = -32512
    MAX_VALUE_OTHER = 32767
    MIN_VALUE_OTHER = -32767

    EXT_RANGE_VOLTS = 5

    def __init__(self, serialNumber=None, connect=True):
        """Load DLL etc."""
        if platform.system() == 'Linux':
            from ctypes import cdll
            self.lib = cdll.LoadLibrary("lib" + self.LIBNAME + ".so")
        elif platform.system() == 'Darwin':
            from picoscope.darwin_utils import LoadLibraryDarwin
            self.lib = LoadLibraryDarwin("lib" + self.LIBNAME + ".dylib")
        else:
            from ctypes import windll
            from ctypes.util import find_library
            self.lib = windll.LoadLibrary(
                find_library(str(self.LIBNAME + ".dll")))

        self.resolution = self.ADC_RESOLUTIONS["8"]

        super(PS3000a, self).__init__(serialNumber, connect)

    def _lowLevelOpenUnit(self, serialNumber):
        c_handle = c_int16()
        if serialNumber is not None:
            serialNumberStr = create_string_buffer(
                bytes(serialNumber, encoding='utf-8'))
        else:
            serialNumberStr = None
        # Passing None is the same as passing NULL
        m = self.lib.ps3000aOpenUnit(byref(c_handle), serialNumberStr)
        self.handle = c_handle.value

        # copied over from ps5000a:
        # This will check if the power supply is not connected
        # and change the power supply accordingly
        # Personally (me = Mark), I don't like this
        # since the user should address this immediately, and we
        # shouldn't let this go as a soft error
        # but I think this should do for now
        if m == 0x11A:
            self.changePowerSource("PICO_POWER_SUPPLY_NOT_CONNECTED")
        else:
            self.checkResult(m)

    def _lowLevelCloseUnit(self):
        m = self.lib.ps3000aCloseUnit(c_int16(self.handle))
        self.checkResult(m)

    def _lowLevelEnumerateUnits(self):
        count = c_int16(0)
        m = self.lib.ps3000aEnumerateUnits(byref(count), None, None)
        self.checkResult(m)
        # a serial number is rouhgly 8 characters
        # an extra character for the comma
        # and an extra one for the space after the comma?
        # the extra two also work for the null termination
        serialLth = c_int16(count.value * (8 + 2))
        serials = create_string_buffer(serialLth.value + 1)

        m = self.lib.ps3000aEnumerateUnits(byref(count), serials,
                                           byref(serialLth))
        self.checkResult(m)

        serialList = str(serials.value.decode('utf-8')).split(',')

        serialList = [x.strip() for x in serialList]

        return serialList

    def _lowLevelSetChannel(self, chNum, enabled, coupling, VRange, VOffset,
                            BWLimited):
        m = self.lib.ps3000aSetChannel(c_int16(self.handle), c_enum(chNum),
                                       c_int16(enabled), c_enum(coupling),
                                       c_enum(VRange), c_float(VOffset))
        self.checkResult(m)

        m = self.lib.ps3000aSetBandwidthFilter(c_int16(self.handle),
                                               c_enum(chNum),
                                               c_enum(BWLimited))
        self.checkResult(m)

    def _lowLevelStop(self):
        m = self.lib.ps3000aStop(c_int16(self.handle))
        self.checkResult(m)

    def _lowLevelGetUnitInfo(self, info):
        s = create_string_buffer(256)
        requiredSize = c_int16(0)

        m = self.lib.ps3000aGetUnitInfo(c_int16(self.handle), byref(s),
                                        c_int16(len(s)), byref(requiredSize),
                                        c_enum(info))
        self.checkResult(m)
        if requiredSize.value > len(s):
            s = create_string_buffer(requiredSize.value + 1)
            m = self.lib.ps3000aGetUnitInfo(c_int16(self.handle), byref(s),
                                            c_int16(len(s)),
                                            byref(requiredSize), c_enum(info))
            self.checkResult(m)

        # should this bee ascii instead?
        # I think they are equivalent...
        return s.value.decode('utf-8')

    def _lowLevelFlashLed(self, times):
        m = self.lib.ps3000aFlashLed(c_int16(self.handle), c_int16(times))
        self.checkResult(m)

    def _lowLevelSetSimpleTrigger(self, enabled, trigsrc, threshold_adc,
                                  direction, delay, auto):
        m = self.lib.ps3000aSetSimpleTrigger(c_int16(self.handle),
                                             c_int16(enabled), c_enum(trigsrc),
                                             c_int16(threshold_adc),
                                             c_enum(direction),
                                             c_uint32(delay), c_int16(auto))
        self.checkResult(m)

    def _lowLevelSetNoOfCaptures(self, numCaptures):
        m = self.lib.ps3000aSetNoOfCaptures(c_int16(self.handle),
                                            c_uint16(numCaptures))
        self.checkResult(m)

    def _lowLevelMemorySegments(self, numSegments):
        maxSamples = c_int32()
        m = self.lib.ps3000aMemorySegments(c_int16(self.handle),
                                           c_uint16(numSegments),
                                           byref(maxSamples))
        self.checkResult(m)
        return maxSamples.value

    def _lowLevelGetMaxSegments(self):
        maxSegments = c_int16()
        m = self.lib.ps3000aGetMaxSegments(c_int16(self.handle),
                                           byref(maxSegments))
        self.checkResult(m)
        return maxSegments.value

    def _lowLevelRunBlock(self, numPreTrigSamples, numPostTrigSamples,
                          timebase, oversample, segmentIndex, callback,
                          pParameter):
        # NOT: Oversample is NOT used!
        timeIndisposedMs = c_int32()
        m = self.lib.ps3000aRunBlock(c_int16(self.handle),
                                     c_uint32(numPreTrigSamples),
                                     c_uint32(numPostTrigSamples),
                                     c_uint32(timebase), c_int16(oversample),
                                     byref(timeIndisposedMs),
                                     c_uint32(segmentIndex), c_void_p(),
                                     c_void_p())
        # According to the documentation, 'callback, pParameter' should work
        # instead of the last two c_void_p parameters.
        self.checkResult(m)
        return timeIndisposedMs.value

    def _lowLevelIsReady(self):
        ready = c_int16()
        m = self.lib.ps3000aIsReady(c_int16(self.handle), byref(ready))
        self.checkResult(m)
        if ready.value:
            return True
        else:
            return False

    def _lowlevelPingUnit(self):
        m = self.lib.ps3000aPingUnit(c_int16(self.handle))
        return m

    def _lowLevelGetTimebase(self, tb, noSamples, oversample, segmentIndex):
        """Return (timeIntervalSeconds, maxSamples)."""
        maxSamples = c_int32()
        intervalNanoSec = c_float()

        m = self.lib.ps3000aGetTimebase2(c_int16(self.handle), c_uint32(tb),
                                         c_uint32(noSamples),
                                         byref(intervalNanoSec),
                                         c_int16(oversample),
                                         byref(maxSamples),
                                         c_uint32(segmentIndex))
        self.checkResult(m)
        # divide by 1e9 to return interval in seconds
        return (intervalNanoSec.value * 1e-9, maxSamples.value)

    def getTimeBaseNum(self, sampleTimeS):
        """Convert sample time in S to something to pass to API Call."""
        maxSampleTime = (((2**32 - 1) - 2) / 125000000)
        if sampleTimeS < 8.0E-9:
            st = math.floor(math.log(sampleTimeS * 1E9, 2))
            st = max(st, 0)
        else:
            if sampleTimeS > maxSampleTime:
                sampleTimeS = maxSampleTime
            st = math.floor((sampleTimeS * 125000000) + 2)

        # is this cast needed?
        st = int(st)
        return st

    def getTimestepFromTimebase(self, timebase):
        """Take API timestep code and returns the sampling period.

        API timestep is an integer from 0-32
        """
        if timebase < 3:
            dt = 2.**timebase / 1.0E9
        else:
            dt = (timebase - 2.0) / 125000000.
        return dt

    def _lowLevelSetAWGSimpleDeltaPhase(self, waveform, deltaPhase,
                                        offsetVoltage, pkToPk, indexMode,
                                        shots, triggerType, triggerSource):
        """Waveform should be an array of shorts."""
        waveformPtr = waveform.ctypes.data_as(POINTER(c_int16))

        m = self.lib.ps3000aSetSigGenArbitrary(
            c_int16(self.handle),
            c_uint32(int(offsetVoltage * 1E6)),  # offset voltage in microvolts
            c_uint32(int(pkToPk * 1E6)),  # pkToPk in microvolts
            c_uint32(int(deltaPhase)),  # startDeltaPhase
            c_uint32(int(deltaPhase)),  # stopDeltaPhase
            c_uint32(0),  # deltaPhaseIncrement
            c_uint32(0),  # dwellCount
            waveformPtr,  # arbitraryWaveform
            c_int32(len(waveform)),  # arbitraryWaveformSize
            c_enum(0),  # sweepType for deltaPhase
            c_enum(0),  # operation (adding random noise and whatnot)
            c_enum(indexMode),  # single, dual, quad
            c_uint32(shots),
            c_uint32(0),  # sweeps
            c_uint32(triggerType),
            c_uint32(triggerSource),
            c_int16(0))  # extInThreshold
        self.checkResult(m)

    def _lowLevelSetDataBuffer(self, channel, data, downSampleMode,
                               segmentIndex):
        """Set the data buffer.

        Be sure to call _lowLevelClearDataBuffer
        when you are done with the data array
        or else subsequent calls to GetValue will still use the same array.
        """
        dataPtr = data.ctypes.data_as(POINTER(c_int16))
        numSamples = len(data)

        m = self.lib.ps3000aSetDataBuffer(c_int16(self.handle),
                                          c_enum(channel), dataPtr,
                                          c_int32(numSamples),
                                          c_uint32(segmentIndex),
                                          c_enum(downSampleMode))
        self.checkResult(m)

    def _lowLevelSetDataBufferBulk(self, channel, data, segmentIndex,
                                   downSampleMode):
        """Set the bulk data buffer.

        In ps3000a, ps3000aSetDataBuffer combines the functionality of
        psX000YSetDataBuffer and psX000YSetDataBufferBulk. Since the rapid
        block functions in picoscope.py call the Bulk version, a delegator is
        needed. Note that the order of segmentIndex and downSampleMode is
        reversed.
        """
        self._lowLevelSetDataBuffer(channel, data, downSampleMode,
                                    segmentIndex)

    def _lowLevelSetMultipleDataBuffers(self, channel, data, downSampleMode):
        max_segments = self._lowLevelGetMaxSegments()
        if data.shape[0] < max_segments:
            raise ValueError("data array has fewer rows than current number " +
                             "of memory segments")
        if data.shape[1] < self.maxSamples:
            raise ValueError("data array has fewer columns than maxSamples")

        for i in range(max_segments):
            m = self._lowLevelSetDataBuffer(channel, data[i, :],
                                            downSampleMode, i)
            self.checkResult(m)

    def _lowLevelClearDataBuffer(self, channel, segmentIndex):
        m = self.lib.ps3000aSetDataBuffer(c_int16(self.handle),
                                          c_enum(channel), c_void_p(),
                                          c_uint32(0), c_uint32(segmentIndex),
                                          c_enum(0))
        self.checkResult(m)

    def _lowLevelGetValues(self, numSamples, startIndex, downSampleRatio,
                           downSampleMode, segmentIndex):
        numSamplesReturned = c_uint32()
        numSamplesReturned.value = numSamples
        overflow = c_int16()
        m = self.lib.ps3000aGetValues(c_int16(self.handle),
                                      c_uint32(startIndex),
                                      byref(numSamplesReturned),
                                      c_uint32(downSampleRatio),
                                      c_enum(downSampleMode),
                                      c_uint32(segmentIndex), byref(overflow))
        self.checkResult(m)
        return (numSamplesReturned.value, overflow.value)

    def _lowLevelGetValuesBulk(self, numSamples, fromSegment, toSegment,
                               downSampleRatio, downSampleMode, overflow):
        m = self.lib.ps3000aGetValuesBulk(
            c_int16(self.handle), byref(c_uint32(numSamples)),
            c_uint32(fromSegment), c_uint32(toSegment),
            c_uint32(downSampleRatio), c_int16(downSampleMode),
            overflow.ctypes.data_as(POINTER(c_int16)))
        self.checkResult(m)
        return overflow, numSamples

    def _lowLevelSetSigGenBuiltInSimple(self, offsetVoltage, pkToPk, waveType,
                                        frequency, shots, triggerType,
                                        triggerSource, stopFreq, increment,
                                        dwellTime, sweepType, numSweeps):
        # TODO, I just noticed that V2 exists
        # Maybe change to V2 in the future
        if stopFreq is None:
            stopFreq = frequency

        m = self.lib.ps3000aSetSigGenBuiltIn(
            c_int16(self.handle), c_int32(int(offsetVoltage * 1000000)),
            c_int32(int(pkToPk * 1000000)), c_int16(waveType),
            c_float(frequency), c_float(stopFreq), c_float(increment),
            c_float(dwellTime), c_enum(sweepType), c_enum(0), c_uint32(shots),
            c_uint32(numSweeps), c_enum(triggerType), c_enum(triggerSource),
            c_int16(0))
        self.checkResult(m)

    def _lowLevelChangePowerSource(self, powerstate):
        m = self.lib.ps3000aChangePowerSource(c_int16(self.handle),
                                              c_enum(powerstate))
        self.checkResult(m)

    def _lowLevelSigGenSoftwareControl(self, triggerType):
        m = self.lib.ps3000aSigGenSoftwareControl(c_int16(self.handle),
                                                  c_enum(triggerType))
        self.checkResult(m)
Esempio n. 12
0
class PS5000a(_PicoscopeBase):
    """The following are low-level functions for the PS5000."""

    LIBNAME = "ps5000a"

    NUM_CHANNELS = 4
    CHANNELS = {"A": 0, "B": 1, "C": 2, "D": 3,
                "External": 4, "MaxChannels": 4, "TriggerAux": 5}

    ADC_RESOLUTIONS = {"8": 0, "12": 1, "14": 2, "15": 3, "16": 4}

    CHANNEL_RANGE = [{"rangeV": 10E-3, "apivalue": 0, "rangeStr": "10 mV"},
                     {"rangeV": 20E-3, "apivalue": 1, "rangeStr": "20 mV"},
                     {"rangeV": 50E-3, "apivalue": 2, "rangeStr": "50 mV"},
                     {"rangeV": 100E-3, "apivalue": 3, "rangeStr": "100 mV"},
                     {"rangeV": 200E-3, "apivalue": 4, "rangeStr": "200 mV"},
                     {"rangeV": 500E-3, "apivalue": 5, "rangeStr": "500 mV"},
                     {"rangeV": 1.0, "apivalue": 6, "rangeStr": "1 V"},
                     {"rangeV": 2.0, "apivalue": 7, "rangeStr": "2 V"},
                     {"rangeV": 5.0, "apivalue": 8, "rangeStr": "5 V"},
                     {"rangeV": 10.0, "apivalue": 9, "rangeStr": "10 V"},
                     {"rangeV": 20.0, "apivalue": 10, "rangeStr": "20 V"},
                     {"rangeV": 50.0, "apivalue": 11, "rangeStr": "50 V"},
                     ]

    CHANNEL_COUPLINGS = {"DC": 1, "AC": 0}

    # has_sig_gen = True
    WAVE_TYPES = {"Sine": 0, "Square": 1, "Triangle": 2,
                  "RampUp": 3, "RampDown": 4,
                  "Sinc": 5, "Gaussian": 6, "HalfSine": 7, "DCVoltage": 8,
                  "WhiteNoise": 9}

    SWEEP_TYPES = {"Up": 0, "Down": 1, "UpDown": 2, "DownUp": 3}

    SIGGEN_TRIGGER_TYPES = {"Rising": 0, "Falling": 1,
                            "GateHigh": 2, "GateLow": 3}
    SIGGEN_TRIGGER_SOURCES = {"None": 0, "ScopeTrig": 1, "AuxIn": 2,
                              "ExtIn": 3, "SoftTrig": 4, "TriggerRaw": 5}

    # This is actually different depending on the AB/CD models
    # I wonder how we could detect the difference between the oscilloscopes
    # I believe we can obtain this information from the setInfo function
    # by readign the hardware version
    # for the PS6403B version, the hardware version is "1 1",
    # an other possibility is that the PS6403B shows up as 6403 when using
    # VARIANT_INFO and others show up as PS6403X where X = A,C or D

    AWGPhaseAccumulatorSize = 32

    AWGDACInterval = 5E-9  # in seconds
    AWGDACFrequency = 1 / AWGDACInterval

    AWG_INDEX_MODES = {"Single": 0, "Dual": 1, "Quad": 2}

    MAX_VALUE_8BIT = 32512
    MIN_VALUE_8BIT = -32512
    MAX_VALUE_OTHER = 32767
    MIN_VALUE_OTHER = -32767

    EXT_RANGE_VOLTS = 5

    def __init__(self, serialNumber=None, connect=True):
        """Load DLL etc."""
        if platform.system() == 'Linux':
            from ctypes import cdll
            self.lib = cdll.LoadLibrary("lib" + self.LIBNAME + ".so")
        elif platform.system() == 'Darwin':
            from picoscope.darwin_utils import LoadLibraryDarwin
            self.lib = LoadLibraryDarwin("lib" + self.LIBNAME + ".dylib")
        else:
            from ctypes import windll
            self.lib = windll.LoadLibrary(str(self.LIBNAME + ".dll"))

        self.resolution = self.ADC_RESOLUTIONS["8"]

        super(PS5000a, self).__init__(serialNumber, connect)

    def _lowLevelOpenUnit(self, sn):
        c_handle = c_int16()
        if sn is not None:
            serialNullTermStr = create_string_buffer(sn)
        else:
            serialNullTermStr = None
        # Passing None is the same as passing NULL
        m = self.lib.ps5000aOpenUnit(byref(c_handle), serialNullTermStr,
                                     self.resolution)
        self.handle = c_handle.value

        # This will check if the power supply is not connected
        # and change the power supply accordingly
        # Personally (me = Mark), I don't like this
        # since the user should address this immediately, and we
        # shouldn't let this go as a soft error
        # but I think this should do for now
        if m == 0x11A:
            self.changePowerSource(m)
        else:
            self.checkResult(m)

        # B models have different AWG buffer sizes
        # 5242B, 5442B: 2**14
        # 5243B, 5443B: 2**15
        # 5444B, 5244B: 3 * 2**14
        # Model 5444B identifies itself properly in VariantInfo, I will assume
        # the others do as well.

        self.model = self.getUnitInfo('VariantInfo')
        # print("Checking variant, found: " + str(self.model))
        if self.model in ('5244B', '5444B'):
            self.AWGBufferAddressWidth = math.log(3 * 2**14, 2)
            self.AWGMaxVal = 32767
            self.AWGMinVal = -32768
            self.AWGMaxSamples = 49152
        elif self.model in ('5243B', '5443B'):
            self.AWGBufferAddressWidth = 15
            self.AWGMaxVal = 32767
            self.AWGMinVal = -32768
            self.AWGMaxSamples = 2**self.AWGBufferAddressWidth
        else:
            # This is what the previous PS5000a used for all scopes.
            # I am leaving it the same, although I think the AWGMaxVal and
            # AWGMinVal issue was fixed and should be -32768 to 32767 for all
            # 5000 models
            self.AWGBufferAddressWidth = 14
            # Note this is NOT what is written in the Programming guide as of
            # version # 10_5_0_28
            # This issue was acknowledged in this thread
            # http://www.picotech.com/support/topic13217.html
            self.AWGMaxVal = 0x0FFF
            self.AWGMinVal = 0x0000
            self.AWGMaxSamples = 2**self.AWGBufferAddressWidth

    def _lowLevelCloseUnit(self):
        m = self.lib.ps5000aCloseUnit(c_int16(self.handle))
        self.checkResult(m)

    def _lowLevelSetChannel(self, chNum, enabled, coupling, VRange, VOffset,
                            bandwidth):
        m = self.lib.ps5000aSetChannel(c_int16(self.handle), c_enum(chNum),
                                       c_int16(enabled), c_enum(coupling),
                                       c_enum(VRange), c_float(VOffset))
        self.checkResult(m)

        # The error this might through are
        #    INVALID_HANDLE
        #    INVALID_CHANNEL
        #    INVALID_BANDWIDTH
        # Invalid bandwidth is the only case that could go wrong.
        # The others would be thrown above (assuming no race condition:
        # i.e. unplugging the scope in between this call.
        # I decided to keep the logic below to avoid a possible error
        # picobase/SetChannel should be changed to the following
        # Set the channel
        # save the new channel settings
        # check if ps5000a
        # change the bandwidth separately
        # changing the bandwidth would be it's own function (implemented below)
        if bandwidth:
            m = self.lib.ps5000aSetBandwidthFilter(c_int16(self.handle),
                                                   c_enum(chNum), c_enum(1))
        else:
            m = self.lib.ps5000aSetBandwidthFilter(c_int16(self.handle),
                                                   c_enum(chNum), c_enum(0))
        self.checkResult(m)

    def _lowLevelSetBandwidthFilter(self, channel, bandwidth):
        m = self.lib.ps5000aSetBandwidthFilter(c_int16(self.handle),
                                               c_enum(channel),
                                               c_enum(bandwidth))
        self.checkResult(m)

    def _lowLevelStop(self):
        m = self.lib.ps5000aStop(c_int16(self.handle))
        self.checkResult(m)

    def _lowLevelGetUnitInfo(self, info):
        s = create_string_buffer(256)
        requiredSize = c_int16(0)

        m = self.lib.ps5000aGetUnitInfo(c_int16(self.handle), byref(s),
                                        c_int16(len(s)), byref(requiredSize),
                                        c_enum(info))
        self.checkResult(m)
        if requiredSize.value > len(s):
            s = create_string_buffer(requiredSize.value + 1)
            m = self.lib.ps5000aGetUnitInfo(c_int16(self.handle), byref(s),
                                            c_int16(len(s)),
                                            byref(requiredSize), c_enum(info))
            self.checkResult(m)

        # should this bee ascii instead?
        # I think they are equivalent...
        return s.value.decode('utf-8')

    def _lowLevelFlashLed(self, times):
        m = self.lib.ps5000aFlashLed(c_int16(self.handle), c_int16(times))
        self.checkResult(m)

    def _lowLevelSetSimpleTrigger(self, enabled, trigsrc, threshold_adc,
                                  direction, delay, timeout_ms):
        m = self.lib.ps5000aSetSimpleTrigger(
            c_int16(self.handle), c_int16(enabled),
            c_enum(trigsrc), c_int16(threshold_adc),
            c_enum(direction), c_uint32(delay), c_int16(timeout_ms))
        self.checkResult(m)

    def _lowLevelRunBlock(self, numPreTrigSamples, numPostTrigSamples,
                          timebase, oversample, segmentIndex):
        # Oversample is NOT used!
        timeIndisposedMs = c_int32()
        m = self.lib.ps5000aRunBlock(
            c_int16(self.handle), c_uint32(numPreTrigSamples),
            c_uint32(numPostTrigSamples), c_uint32(timebase),
            byref(timeIndisposedMs), c_uint32(segmentIndex),
            c_void_p(), c_void_p())
        self.checkResult(m)
        return timeIndisposedMs.value

    def _lowLevelIsReady(self):
        ready = c_int16()
        m = self.lib.ps5000aIsReady(c_int16(self.handle), byref(ready))
        self.checkResult(m)
        if ready.value:
            return True
        else:
            return False

    def _lowLevelPingUnit(self):
        m = self.lib.ps5000aPingUnit(c_int16(self.handle))
        return m

    def _lowLevelGetTimebase(self, tb, noSamples, oversample, segmentIndex):
        """Return (timeIntervalSeconds, maxSamples)."""
        maxSamples = c_int32()
        sampleRate = c_float()

        m = self.lib.ps5000aGetTimebase2(c_int16(self.handle), c_uint32(tb),
                                         c_uint32(noSamples),
                                         byref(sampleRate),
                                         byref(maxSamples),
                                         c_uint32(segmentIndex))
        self.checkResult(m)

        return (sampleRate.value / 1.0E9, maxSamples.value)

    def getTimeBaseNum(self, sampleTimeS):
        """Convert sample time in S to something to pass to API Call."""
        if self.resolution == self.ADC_RESOLUTIONS["8"]:
            maxSampleTime = (((2 ** 32 - 1) - 2) / 125000000)
            if sampleTimeS < 8.0E-9:
                st = math.floor(math.log(sampleTimeS * 1E9, 2))
                st = max(st, 0)
            else:
                if sampleTimeS > maxSampleTime:
                    sampleTimeS = maxSampleTime
                st = math.floor((sampleTimeS * 125000000) + 2)

        elif self.resolution == self.ADC_RESOLUTIONS["12"]:
            maxSampleTime = (((2 ** 32 - 1) - 3) / 62500000)
            if sampleTimeS < 16.0E-9:
                st = math.floor(math.log(sampleTimeS * 5E8, 2)) + 1
                st = max(st, 1)
            else:
                if sampleTimeS > maxSampleTime:
                    sampleTimeS = maxSampleTime
                st = math.floor((sampleTimeS * 62500000) + 3)

        elif (self.resolution == self.ADC_RESOLUTIONS["14"]) or (
                self.resolution == self.ADC_RESOLUTIONS["15"]):
            maxSampleTime = (((2 ** 32 - 1) - 2) / 125000000)
            if sampleTimeS > maxSampleTime:
                sampleTimeS = maxSampleTime
            st = math.floor((sampleTimeS * 125000000) + 2)
            st = max(st, 3)

        elif self.resolution == self.ADC_RESOLUTIONS["16"]:
            maxSampleTime = (((2 ** 32 - 1) - 3) / 62500000)
            if sampleTimeS > maxSampleTime:
                sampleTimeS = maxSampleTime
            st = math.floor((sampleTimeS * 62500000) + 3)
            st = max(st, 3)

        else:
            raise ValueError("Invalid Resolution for Device?")

        # is this cast needed?
        st = int(st)
        return st

    def getTimestepFromTimebase(self, timebase):
        """Return Timestep from timebase."""
        if self.resolution == self.ADC_RESOLUTIONS["8"]:
            if timebase < 3:
                dt = 2. ** timebase / 1.0E9
            else:
                dt = (timebase - 2.0) / 125000000.
        elif self.resolution == self.ADC_RESOLUTIONS["12"]:
            if timebase < 4:
                dt = 2. ** (timebase - 1) / 5.0E8
            else:
                dt = (timebase - 3.0) / 62500000.
        elif (self.resolution == self.ADC_RESOLUTIONS["14"]) or (
                self.resolution == self.ADC_RESOLUTIONS["15"]):
            dt = (timebase - 2.0) / 125000000.
        elif self.resolution == self.ADC_RESOLUTIONS["16"]:
            dt = (timebase - 3.0) / 62500000.
        return dt

    def _lowLevelSetAWGSimpleDeltaPhase(self, waveform, deltaPhase,
                                        offsetVoltage, pkToPk, indexMode,
                                        shots, triggerType, triggerSource):
        """Waveform should be an array of shorts."""
        waveformPtr = waveform.ctypes.data_as(POINTER(c_int16))

        m = self.lib.ps5000aSetSigGenArbitrary(
            c_int16(self.handle),
            c_uint32(int(offsetVoltage * 1E6)),  # offset voltage in microvolts
            c_uint32(int(pkToPk * 1E6)),         # pkToPk in microvolts
            c_uint32(int(deltaPhase)),           # startDeltaPhase
            c_uint32(int(deltaPhase)),           # stopDeltaPhase
            c_uint32(0),                         # deltaPhaseIncrement
            c_uint32(0),                         # dwellCount
            waveformPtr,                         # arbitraryWaveform
            c_int32(len(waveform)),              # arbitraryWaveformSize
            c_enum(0),                           # sweepType for deltaPhase
            c_enum(0),            # operation (adding random noise and whatnot)
            c_enum(indexMode),                   # single, dual, quad
            c_uint32(shots),
            c_uint32(0),                         # sweeps
            c_uint32(triggerType),
            c_uint32(triggerSource),
            c_int16(0))                          # extInThreshold
        self.checkResult(m)

    def _lowLevelSetDataBuffer(self, channel, data, downSampleMode,
                               segmentIndex):
        """Set the data buffer.

        Be sure to call _lowLevelClearDataBuffer
        when you are done with the data array
        or else subsequent calls to GetValue will still use the same array.
        """
        dataPtr = data.ctypes.data_as(POINTER(c_int16))
        numSamples = len(data)

        m = self.lib.ps5000aSetDataBuffer(c_int16(self.handle),
                                          c_enum(channel),
                                          dataPtr, c_int32(numSamples),
                                          c_uint32(segmentIndex),
                                          c_enum(downSampleMode))
        self.checkResult(m)

    def _lowLevelSetDataBufferBulk(self, channel, data, segmentIndex,
                                   downSampleMode):
        """Just calls setDataBuffer with argument order changed.

        For compatibility with current picobase.py.
        """
        self._lowLevelSetDataBuffer(channel,
                                    data,
                                    downSampleMode,
                                    segmentIndex)

    def _lowLevelClearDataBuffer(self, channel, segmentIndex):
        m = self.lib.ps5000aSetDataBuffer(c_int16(self.handle),
                                          c_enum(channel),
                                          c_void_p(), c_uint32(0),
                                          c_uint32(segmentIndex),
                                          c_enum(0))
        self.checkResult(m)

    def _lowLevelGetValues(self, numSamples, startIndex, downSampleRatio,
                           downSampleMode, segmentIndex):
        numSamplesReturned = c_uint32()
        numSamplesReturned.value = numSamples
        overflow = c_int16()
        m = self.lib.ps5000aGetValues(
            c_int16(self.handle), c_uint32(startIndex),
            byref(numSamplesReturned), c_uint32(downSampleRatio),
            c_enum(downSampleMode), c_uint32(segmentIndex),
            byref(overflow))
        self.checkResult(m)
        return (numSamplesReturned.value, overflow.value)

    def _lowLevelSetSigGenBuiltInSimple(self, offsetVoltage, pkToPk, waveType,
                                        frequency, shots, triggerType,
                                        triggerSource, stopFreq, increment,
                                        dwellTime, sweepType, numSweeps):
        # TODO, I just noticed that V2 exists
        # Maybe change to V2 in the future

        if stopFreq is None:
            stopFreq = frequency

        m = self.lib.ps5000aSetSigGenBuiltIn(
            c_int16(self.handle),
            c_int32(int(offsetVoltage * 1000000)),
            c_int32(int(pkToPk * 1000000)),
            c_int16(waveType),
            c_float(frequency), c_float(stopFreq),
            c_float(increment), c_float(dwellTime),
            c_enum(sweepType), c_enum(0),
            c_uint32(shots), c_uint32(numSweeps),
            c_enum(triggerType), c_enum(triggerSource),
            c_int16(0))
        self.checkResult(m)

    def _lowLevelSetDeviceResolution(self, resolution):
        self.resolution = resolution
        m = self.lib.ps5000aSetDeviceResolution(
            c_int16(self.handle),
            c_enum(resolution))
        self.checkResult(m)

    def _lowLevelChangePowerSource(self, powerstate):
        m = self.lib.ps5000aChangePowerSource(
            c_int16(self.handle),
            c_enum(powerstate))
        self.checkResult(m)

    # Morgan's additions
    def _lowLevelGetValuesBulk(self, numSamples, fromSegment, toSegment,
                               downSampleRatio, downSampleMode, overflow):
        """Copy data from several memory segments at once."""
        overflowPoint = overflow.ctypes.data_as(POINTER(c_int16))
        m = self.lib.ps5000aGetValuesBulk(
            c_int16(self.handle),
            byref(c_int32(numSamples)),
            c_int32(fromSegment),
            c_int32(toSegment),
            c_int32(downSampleRatio),
            c_enum(downSampleMode),
            overflowPoint
            )
        self.checkResult(m)

    def _lowLevelSetNoOfCaptures(self, numCaptures):
        m = self.lib.ps5000aSetNoOfCaptures(
            c_int16(self.handle),
            c_uint32(numCaptures))
        self.checkResult(m)

    def _lowLevelMemorySegments(self, numSegments):
        maxSamples = c_int32()
        m = self.lib.ps5000aMemorySegments(
            c_int16(self.handle), c_uint32(numSegments), byref(maxSamples))
        self.checkResult(m)
        return maxSamples.value

    def _lowLevelGetValuesTriggerTimeOffsetBulk(self, fromSegment, toSegment):
        """Supposedly gets the trigger times for a bunch of segments at once.

        For block mode.
        Can't get it to work yet, however.
        """
        import numpy as np

        nSegments = toSegment - fromSegment + 1
        # time = c_int64()
        times = np.ascontiguousarray(
            np.zeros(nSegments, dtype=np.int64)
            )
        timeUnits = np.ascontiguousarray(
            np.zeros(nSegments, dtype=np.int32)
            )

        m = self.lib.ps5000aGetValuesTriggerTimeOffsetBulk64(
            c_int16(self.handle),
            times.ctypes.data_as(POINTER(c_int64)),
            timeUnits.ctypes.data_as(POINTER(c_enum)),
            c_uint32(fromSegment),
            c_uint32(toSegment)
            )
        self.checkResult(m)
        # timeUnits=np.array([self.TIME_UNITS[tu] for tu in timeUnits])
        return times, timeUnits
Esempio n. 13
0
class PS3000(_PicoscopeBase):
    """The following are low-level functions for the PS3000."""

    LIBNAME = "ps3000"

    NUM_CHANNELS = 2
    CHANNELS = {"A": 0, "B": 1, "MaxChannels": 2}

    THRESHOLD_TYPE = {"Rising": 0,
                      "Falling": 1}

    CHANNEL_RANGE = [
        {"rangeV": 20E-3, "apivalue": 1, "rangeStr": "20 mV"},
        {"rangeV": 50E-3, "apivalue": 2, "rangeStr": "50 mV"},
        {"rangeV": 100E-3, "apivalue": 3, "rangeStr": "100 mV"},
        {"rangeV": 200E-3, "apivalue": 4, "rangeStr": "200 mV"},
        {"rangeV": 500E-3, "apivalue": 5, "rangeStr": "500 mV"},
        {"rangeV": 1.0, "apivalue": 6, "rangeStr": "1 V"},
        {"rangeV": 2.0, "apivalue": 7, "rangeStr": "2 V"},
        {"rangeV": 5.0, "apivalue": 8, "rangeStr": "5 V"},
        {"rangeV": 10.0, "apivalue": 9, "rangeStr": "10 V"},
        {"rangeV": 20.0, "apivalue": 10, "rangeStr": "20 V"},
        ]

    CHANNEL_COUPLINGS = {"DC": 1, "AC": 0}

    # has_sig_gen = True
    WAVE_TYPES = {"Sine": 0, "Square": 1, "Triangle": 2,
                  "RampUp": 3, "RampDown": 4, "DCVoltage": 5}

    SWEEP_TYPES = {"Up": 0, "Down": 1, "UpDown": 2, "DownUp": 3}

    TIME_UNITS = {"FS": 0, "PS": 1, "NS": 2, "US": 3, "MS": 4, "S": 5}

    MAX_VALUE = 32767
    MIN_VALUE = -32767

    MAX_TIMEBASES = 19

    UNIT_INFO_TYPES = {"DriverVersion": 0x0,
                       "USBVersion": 0x1,
                       "HardwareVersion": 0x2,
                       "VariantInfo": 0x3,
                       "BatchAndSerial": 0x4,
                       "CalDate": 0x5,
                       "ErrorCode": 0x6,
                       "KernelVersion": 0x7}

    channelBuffersPtr = [c_void_p(), c_void_p()]
    channelBuffersLen = [0, 0]

    # SIGGEN_TRIGGER_TYPES = {"Rising": 0, "Falling": 1,
    #                         "GateHigh": 2, "GateLow": 3}
    # SIGGEN_TRIGGER_SOURCES = {"None": 0, "ScopeTrig": 1, "AuxIn": 2,
    #                           "ExtIn": 3, "SoftTrig": 4, "TriggerRaw": 5}

    def __init__(self, serialNumber=None, connect=True):
        """Load DLL etc."""
        if platform.system() == 'Linux':
            from ctypes import cdll
            self.lib = cdll.LoadLibrary("lib" + self.LIBNAME + ".so")
        elif platform.system() == 'Darwin':
            from picoscope.darwin_utils import LoadLibraryDarwin
            self.lib = LoadLibraryDarwin("lib" + self.LIBNAME + ".dylib")
        else:
            from ctypes import windll
            self.lib = windll.LoadLibrary(str(self.LIBNAME + ".dll"))

        super(PS3000, self).__init__(serialNumber, connect)

    def _lowLevelOpenUnit(self, sn):
        if sn is not None:
            raise ValueError("PS3000 Doesn't Support Open by Serial Number")

        m = self.lib.ps3000_open_unit()

        if m < 0:
            raise IOError("Failed to Find PS3000 Unit. " +
                          "Should you be using PS3000a driver?")

        self.handle = m
        self.suggested_time_units = self.TIME_UNITS["NS"]

    def _lowLevelCloseUnit(self):
        m = self.lib.ps3000_close_unit(c_int16(self.handle))
        self.checkResult(m)

    def _lowLevelSetChannel(self, chNum, enabled, coupling, VRange, VOffset,
                            BWLimited):
        m = self.lib.ps3000_set_channel(
            c_int16(self.handle), c_enum(chNum),
            c_int16(enabled), c_enum(coupling),
            c_enum(VRange))
        self.checkResult(m)

    def _lowLevelStop(self):
        m = self.lib.ps3000_stop(c_int16(self.handle))
        self.checkResult(m)

    def _lowLevelGetUnitInfo(self, info):
        s = create_string_buffer(256)

        m = self.lib.ps3000_get_unit_info(
            c_int16(self.handle), byref(s), c_int16(len(s)), c_enum(info))
        self.checkResult(m)

        # should this bee ascii instead?
        # I think they are equivalent...
        return s.value.decode('utf-8')

    def _lowLevelFlashLed(self, times):
        m = self.lib.ps3000_flash_led(c_int16(self.handle))
        self.checkResult(m)

    def _lowLevelSetSimpleTrigger(self, enabled, trigsrc, threshold_adc,
                                  direction, delay, timeout_ms):
        # TODO: Fix 'auto' which is where trigger occurs in block.
        # Delay is not used

        m = self.lib.ps3000_set_trigger(
            c_int16(self.handle), c_enum(trigsrc), c_int16(threshold_adc),
            c_enum(direction), c_int16(delay), c_int16(timeout_ms))
        self.checkResult(m)

    def _lowLevelRunBlock(self, numPreTrigSamples, numPostTrigSamples,
                          timebase, oversample, segmentIndex):
        # NOT: Oversample is NOT used!

        # TODO: Fix 'delay' which is where trigger occurs in block
        if numPreTrigSamples > 0:
            raise ValueError("numPreTrigSamples isn't supported on PS3000")

        timeIndisposedMs = c_int32()
        m = self.lib.ps3000_run_block(
            c_int16(self.handle), c_uint32(numPostTrigSamples),
            c_uint32(timebase), c_int16(1), byref(timeIndisposedMs))

        self.checkResult(m)
        return timeIndisposedMs.value

    def _lowLevelIsReady(self):
        ready = c_int16()
        ready = self.lib.ps3000_ready(c_int16(self.handle))
        if ready > 0:
            return True
        elif ready == 0:
            return False
        else:
            raise IOError("ps3000_ready returned %d" % ready.value)

    def _lowLevelGetTimebase(self, tb, noSamples, oversample, segmentIndex):
        """Return (timeIntervalSeconds, maxSamples)."""
        maxSamples = c_int32()
        time_interval = c_int32()
        time_units = c_int16()

        m = self.lib.ps3000_get_timebase(
            c_int16(self.handle), c_int16(tb), c_uint32(noSamples),
            byref(time_interval), byref(time_units), c_int16(1),
            byref(maxSamples))

        self.checkResult(m)

        self.suggested_time_units = time_units.value

        return (time_interval.value / 1.0E9, maxSamples.value)

    def getTimeBaseNum(self, sampleTimeS):
        """ps3000 doesn't seem to have published formula like other scopes."""
        time_interval = c_int32()
        timebases = [None] * self.MAX_TIMEBASES

        # Convert to nS
        sampleTimenS = sampleTimeS * 1E9

        tb = 0
        while tb < self.MAX_TIMEBASES:
            rv = self.lib.ps3000_get_timebase(
                c_int16(self.handle), c_int16(tb), c_uint32(512),
                byref(time_interval), c_void_p(), c_int16(1),  c_void_p())
            if rv != 0:
                timebases[tb] = time_interval.value

            tb += 1

        # Figure out closest option
        besterror = 1E99
        bestindx = 0
        for indx, val in enumerate(timebases):
            if val is not None:
                error = sampleTimenS - val
                if abs(error) < besterror:
                    besterror = abs(error)
                    bestindx = indx

        return bestindx

    def getTimestepFromTimebase(self, timebase):
        """Return timestep from Timebase."""
        time_interval = c_int32()
        m = self.lib.ps3000_get_timebase(
            c_int16(self.handle), c_int16(timebase), c_uint32(512),
            byref(time_interval), c_void_p(), c_int16(1),  c_void_p())
        self.checkResult(m)
        return (time_interval.value / 1.0E9)

    def _lowLevelSetDataBuffer(self, channel, data, downSampleMode,
                               segmentIndex):
        dataPtr = data.ctypes.data_as(POINTER(c_int16))
        numSamples = len(data)

        self.channelBuffersPtr[channel] = dataPtr
        self.channelBuffersLen[channel] = numSamples

    def _lowLevelClearDataBuffer(self, channel, segmentIndex):
        self.channelBuffersPtr[channel] = c_void_p()
        self.channelBuffersLen[channel] = 0

    def _lowLevelGetValues(self, numSamples, startIndex, downSampleRatio,
                           downSampleMode, segmentIndex):
        # TODO:
        # Check overflow in channelBuffersLen against numSamples,
        # but need to not raise error if channelBuffersPtr is void

        overflow = c_int16()
        rv = self.lib.ps3000_get_values(
            c_int16(self.handle),
            self.channelBuffersPtr[0],
            self.channelBuffersPtr[1],
            c_void_p(), c_void_p(),
            byref(overflow), c_int32(numSamples))

        self.checkResult(rv)
        return (rv, overflow.value)

    def _lowLevelSetSigGenBuiltInSimple(self, offsetVoltage, pkToPk, waveType,
                                        frequency, shots, triggerType,
                                        triggerSource, stopFreq, increment,
                                        dwellTime, sweepType, numSweeps):
        if stopFreq is None:
            stopFreq = frequency

        m = self.lib.ps3000_set_sig_gen_built_in(
            c_int16(self.handle),
            c_int32(int(offsetVoltage * 1000000)),
            c_int32(int(pkToPk * 1000000)),
            c_int16(waveType),
            c_float(frequency), c_float(stopFreq),
            c_float(increment), c_float(dwellTime), c_enum(sweepType),
            c_uint32(numSweeps))
        self.checkResult(m)

    def checkResult(self, ec):
        """Check result of function calls, raise exception if not 0."""
        # PS3000 differs from other drivers in that non-zero is good
        if ec == 0:
            raise IOError('Error calling %s' % (inspect.stack()[1][3]))

        return 0
Esempio n. 14
0
class PS3000(_PicoscopeBase):
    """The following are low-level functions for the PS3000."""

    LIBNAME = "ps3000"

    NUM_CHANNELS = 2
    CHANNELS = {"A": 0, "B": 1, "MaxChannels": 2}

    THRESHOLD_TYPE = {"Rising": 0, "Falling": 1}

    CHANNEL_RANGE = [
        {
            "rangeV": 20E-3,
            "apivalue": 1,
            "rangeStr": "20 mV"
        },
        {
            "rangeV": 50E-3,
            "apivalue": 2,
            "rangeStr": "50 mV"
        },
        {
            "rangeV": 100E-3,
            "apivalue": 3,
            "rangeStr": "100 mV"
        },
        {
            "rangeV": 200E-3,
            "apivalue": 4,
            "rangeStr": "200 mV"
        },
        {
            "rangeV": 500E-3,
            "apivalue": 5,
            "rangeStr": "500 mV"
        },
        {
            "rangeV": 1.0,
            "apivalue": 6,
            "rangeStr": "1 V"
        },
        {
            "rangeV": 2.0,
            "apivalue": 7,
            "rangeStr": "2 V"
        },
        {
            "rangeV": 5.0,
            "apivalue": 8,
            "rangeStr": "5 V"
        },
        {
            "rangeV": 10.0,
            "apivalue": 9,
            "rangeStr": "10 V"
        },
        {
            "rangeV": 20.0,
            "apivalue": 10,
            "rangeStr": "20 V"
        },
    ]

    CHANNEL_COUPLINGS = {"DC": 1, "AC": 0}

    # has_sig_gen = True
    WAVE_TYPES = {
        "Sine": 0,
        "Square": 1,
        "Triangle": 2,
        "RampUp": 3,
        "RampDown": 4,
        "DCVoltage": 5
    }

    SWEEP_TYPES = {"Up": 0, "Down": 1, "UpDown": 2, "DownUp": 3}

    TIME_UNITS = {"FS": 0, "PS": 1, "NS": 2, "US": 3, "MS": 4, "S": 5}

    MAX_VALUE = 32767
    MIN_VALUE = -32767

    MAX_TIMEBASES = 19

    UNIT_INFO_TYPES = {
        "DriverVersion": 0x0,
        "USBVersion": 0x1,
        "HardwareVersion": 0x2,
        "VariantInfo": 0x3,
        "BatchAndSerial": 0x4,
        "CalDate": 0x5,
        "ErrorCode": 0x6,
        "KernelVersion": 0x7
    }

    channelBuffersPtr = [c_void_p(), c_void_p()]
    channelBuffersLen = [0, 0]

    # SIGGEN_TRIGGER_TYPES = {"Rising": 0, "Falling": 1,
    #                         "GateHigh": 2, "GateLow": 3}
    # SIGGEN_TRIGGER_SOURCES = {"None": 0, "ScopeTrig": 1, "AuxIn": 2,
    #                           "ExtIn": 3, "SoftTrig": 4, "TriggerRaw": 5}

    def __init__(self, serialNumber=None, connect=True):
        """Load DLL etc."""
        if platform.system() == 'Linux':
            from ctypes import cdll
            self.lib = cdll.LoadLibrary("lib" + self.LIBNAME + ".so")
        elif platform.system() == 'Darwin':
            from picoscope.darwin_utils import LoadLibraryDarwin
            self.lib = LoadLibraryDarwin("lib" + self.LIBNAME + ".dylib")
        else:
            from ctypes import windll
            self.lib = windll.LoadLibrary(str(self.LIBNAME + ".dll"))

        super(PS3000, self).__init__(serialNumber, connect)

    def _lowLevelOpenUnit(self, sn):
        if sn is not None:
            raise ValueError("PS3000 Doesn't Support Open by Serial Number")

        m = self.lib.ps3000_open_unit()

        if m < 0:
            raise IOError("Failed to Find PS3000 Unit. " +
                          "Should you be using PS3000a driver?")

        self.handle = m
        self.suggested_time_units = self.TIME_UNITS["NS"]

    def _lowLevelCloseUnit(self):
        m = self.lib.ps3000_close_unit(c_int16(self.handle))
        self.checkResult(m)

    def _lowLevelSetChannel(self, chNum, enabled, coupling, VRange, VOffset,
                            BWLimited):
        m = self.lib.ps3000_set_channel(c_int16(self.handle), c_enum(chNum),
                                        c_int16(enabled), c_enum(coupling),
                                        c_enum(VRange))
        self.checkResult(m)

    def _lowLevelStop(self):
        m = self.lib.ps3000_stop(c_int16(self.handle))
        self.checkResult(m)

    def _lowLevelGetUnitInfo(self, info):
        s = create_string_buffer(256)

        m = self.lib.ps3000_get_unit_info(c_int16(self.handle), byref(s),
                                          c_int16(len(s)), c_enum(info))
        self.checkResult(m)

        # should this bee ascii instead?
        # I think they are equivalent...
        return s.value.decode('utf-8')

    def _lowLevelFlashLed(self, times):
        m = self.lib.ps3000_flash_led(c_int16(self.handle))
        self.checkResult(m)

    def _lowLevelSetSimpleTrigger(self, enabled, trigsrc, threshold_adc,
                                  direction, delay, timeout_ms):
        # TODO: Fix 'auto' which is where trigger occurs in block.
        # Delay is not used

        m = self.lib.ps3000_set_trigger(c_int16(self.handle), c_enum(trigsrc),
                                        c_int16(threshold_adc),
                                        c_enum(direction), c_int16(delay),
                                        c_int16(timeout_ms))
        self.checkResult(m)

    def _lowLevelRunBlock(self, numPreTrigSamples, numPostTrigSamples,
                          timebase, oversample, segmentIndex):
        # NOT: Oversample is NOT used!

        # TODO: Fix 'delay' which is where trigger occurs in block
        if numPreTrigSamples > 0:
            raise ValueError("numPreTrigSamples isn't supported on PS3000")

        timeIndisposedMs = c_int32()
        m = self.lib.ps3000_run_block(c_int16(self.handle),
                                      c_uint32(numPostTrigSamples),
                                      c_uint32(timebase), c_int16(1),
                                      byref(timeIndisposedMs))

        self.checkResult(m)
        return timeIndisposedMs.value

    def _lowLevelIsReady(self):
        ready = c_int16()
        ready = self.lib.ps3000_ready(c_int16(self.handle))
        if ready > 0:
            return True
        elif ready == 0:
            return False
        else:
            raise IOError("ps3000_ready returned %d" % ready.value)

    def _lowLevelGetTimebase(self, tb, noSamples, oversample, segmentIndex):
        """Return (timeIntervalSeconds, maxSamples)."""
        maxSamples = c_int32()
        time_interval = c_int32()
        time_units = c_int16()

        m = self.lib.ps3000_get_timebase(c_int16(self.handle), c_int16(tb),
                                         c_uint32(noSamples),
                                         byref(time_interval),
                                         byref(time_units), c_int16(1),
                                         byref(maxSamples))

        self.checkResult(m)

        self.suggested_time_units = time_units.value

        return (time_interval.value / 1.0E9, maxSamples.value)

    def getTimeBaseNum(self, sampleTimeS):
        """ps3000 doesn't seem to have published formula like other scopes."""
        time_interval = c_int32()
        timebases = [None] * self.MAX_TIMEBASES

        # Convert to nS
        sampleTimenS = sampleTimeS * 1E9

        tb = 0
        while tb < self.MAX_TIMEBASES:
            rv = self.lib.ps3000_get_timebase(c_int16(self.handle),
                                              c_int16(tb), c_uint32(512),
                                              byref(time_interval), c_void_p(),
                                              c_int16(1), c_void_p())
            if rv != 0:
                timebases[tb] = time_interval.value

            tb += 1

        # Figure out closest option
        besterror = 1E99
        bestindx = 0
        for indx, val in enumerate(timebases):
            if val is not None:
                error = sampleTimenS - val
                if abs(error) < besterror:
                    besterror = abs(error)
                    bestindx = indx

        return bestindx

    def getTimestepFromTimebase(self, timebase):
        """Return timestep from Timebase."""
        time_interval = c_int32()
        m = self.lib.ps3000_get_timebase(c_int16(self.handle),
                                         c_int16(timebase), c_uint32(512),
                                         byref(time_interval), c_void_p(),
                                         c_int16(1), c_void_p())
        self.checkResult(m)
        return (time_interval.value / 1.0E9)

    def _lowLevelSetDataBuffer(self, channel, data, downSampleMode,
                               segmentIndex):
        dataPtr = data.ctypes.data_as(POINTER(c_int16))
        numSamples = len(data)

        self.channelBuffersPtr[channel] = dataPtr
        self.channelBuffersLen[channel] = numSamples

    def _lowLevelClearDataBuffer(self, channel, segmentIndex):
        self.channelBuffersPtr[channel] = c_void_p()
        self.channelBuffersLen[channel] = 0

    def _lowLevelGetValues(self, numSamples, startIndex, downSampleRatio,
                           downSampleMode, segmentIndex):
        # TODO:
        # Check overflow in channelBuffersLen against numSamples,
        # but need to not raise error if channelBuffersPtr is void

        overflow = c_int16()
        rv = self.lib.ps3000_get_values(c_int16(self.handle),
                                        self.channelBuffersPtr[0],
                                        self.channelBuffersPtr[1], c_void_p(),
                                        c_void_p(), byref(overflow),
                                        c_int32(numSamples))

        self.checkResult(rv)
        return (rv, overflow.value)

    def _lowLevelSetSigGenBuiltInSimple(self, offsetVoltage, pkToPk, waveType,
                                        frequency, shots, triggerType,
                                        triggerSource, stopFreq, increment,
                                        dwellTime, sweepType, numSweeps):
        if stopFreq is None:
            stopFreq = frequency

        m = self.lib.ps3000_set_sig_gen_built_in(
            c_int16(self.handle), c_int32(int(offsetVoltage * 1000000)),
            c_int32(int(pkToPk * 1000000)), c_int16(waveType),
            c_float(frequency), c_float(stopFreq), c_float(increment),
            c_float(dwellTime), c_enum(sweepType), c_uint32(numSweeps))
        self.checkResult(m)

    def checkResult(self, ec):
        """Check result of function calls, raise exception if not 0."""
        # PS3000 differs from other drivers in that non-zero is good
        if ec == 0:
            raise IOError('Error calling %s' % (inspect.stack()[1][3]))

        return 0
Esempio n. 15
0
class PS4000(_PicoscopeBase):
    """The following are low-level functions for the PS4000."""

    LIBNAME = "ps4000"

    MAX_VALUE = 32764
    MIN_VALUE = -32764

    # AWG stuff here:
    AWGMaxSamples = 4096

    AWGDACInterval = 1 / 192000  # [s]
    AWGDACFrequency = 192000  # [Hz]
    AWGPhaseAccumulatorSize = 32

    AWGMaxVal = 32767
    AWGMinVal = -32768

    AWG_INDEX_MODES = {"Single": 0, "Dual": 1, "Quad": 2}

    AWGBufferAddressWidth = 12
    AWGMaxSamples = 2**AWGBufferAddressWidth

    SIGGEN_TRIGGER_TYPES = {
        "Rising": 0,
        "Falling": 1,
        "GateHigh": 2,
        "GateLow": 3
    }

    SIGGEN_TRIGGER_SOURCES = {
        "None": 0,
        "ScopeTrig": 1,
        "AuxIn": 2,
        "ExtIn": 3,
        "SoftTrig": 4
    }

    # EXT/AUX seems to have an imput impedence of 50 ohm (PS6403B)
    EXT_MAX_VALUE = 32767
    EXT_MIN_VALUE = -32767
    EXT_RANGE_VOLTS = 1

    # I don't think that the 50V range is allowed, but I left it there anyway
    # The 10V and 20V ranges are only allowed in high impedence modes
    CHANNEL_RANGE = [
        {
            "rangeV": 20E-3,
            "apivalue": 1,
            "rangeStr": "20 mV"
        },
        {
            "rangeV": 50E-3,
            "apivalue": 2,
            "rangeStr": "50 mV"
        },
        {
            "rangeV": 100E-3,
            "apivalue": 3,
            "rangeStr": "100 mV"
        },
        {
            "rangeV": 200E-3,
            "apivalue": 4,
            "rangeStr": "200 mV"
        },
        {
            "rangeV": 500E-3,
            "apivalue": 5,
            "rangeStr": "500 mV"
        },
        {
            "rangeV": 1.0,
            "apivalue": 6,
            "rangeStr": "1 V"
        },
        {
            "rangeV": 2.0,
            "apivalue": 7,
            "rangeStr": "2 V"
        },
        {
            "rangeV": 5.0,
            "apivalue": 8,
            "rangeStr": "5 V"
        },
        {
            "rangeV": 10.0,
            "apivalue": 9,
            "rangeStr": "10 V"
        },
        {
            "rangeV": 20.0,
            "apivalue": 10,
            "rangeStr": "20 V"
        },
        {
            "rangeV": 50.0,
            "apivalue": 11,
            "rangeStr": "50 V"
        },
    ]

    NUM_CHANNELS = 4
    CHANNELS = {"A": 0, "B": 1, "C": 2, "D": 3, "EXT": 4}

    CHANNEL_COUPLINGS = {"DC50": 2, "DC": 1, "AC": 0}

    WAVE_TYPES = {
        "Sine": 0,
        "Square": 1,
        "Triangle": 2,
        "RampUp": 3,
        "RampDown": 4,
        "Sinc": 5,
        "Gaussian": 6,
        "HalfSine": 7,
        "DCVoltage": 8,
        "WhiteNoise": 9
    }

    SWEEP_TYPES = {"Up": 0, "Down": 1, "UpDown": 2, "DownUp": 3}

    def __init__(self, serialNumber=None, connect=True):
        """Load DLLs."""
        self.handle = None
        if platform.system() == 'Linux':
            from ctypes import cdll
            # ok I don't know what is wrong with my installer,
            # but I need to include .so.2
            self.lib = cdll.LoadLibrary("lib" + self.LIBNAME + ".so.2")
        elif platform.system() == 'Darwin':
            from picoscope.darwin_utils import LoadLibraryDarwin
            self.lib = LoadLibraryDarwin("lib" + self.LIBNAME + ".dylib")
        else:
            from ctypes import windll
            self.lib = windll.LoadLibrary(str(self.LIBNAME + ".dll"))

        super(PS4000, self).__init__(serialNumber, connect)
        # check to see which model we have and use special functions if needed

        unit_number = self.getUnitInfo('VariantInfo')
        if unit_number == '4262':
            self.getTimestepFromTimebase = self._getTimestepFromTimebase4262
            self.getTimeBaseNum = self._getTimeBaseNum4262
        elif unit_number in {'4223', '4224', '4423', '4424'}:
            self.getTimestepFromTimebase = self._getTimestepFromTimebase4223
            self.getTimeBaseNum = self._getTimeBaseNum4223
        else:
            raise NotImplementedError('Timebase functions have not been '
                                      'written for the ' + unit_number)

    def _lowLevelOpenUnit(self, sn):
        c_handle = c_int16()
        if sn is not None:
            serialNullTermStr = create_string_buffer(sn)
        else:
            serialNullTermStr = None
        # Passing None is the same as passing NULL
        m = self.lib.ps4000OpenUnit(byref(c_handle), serialNullTermStr)
        self.checkResult(m)
        self.handle = c_handle.value

    def _lowLevelOpenUnitAsync(self, sn):
        c_status = c_int16()
        if sn is not None:
            serialNullTermStr = create_string_buffer(sn)
        else:
            serialNullTermStr = None

        # Passing None is the same as passing NULL
        m = self.lib.ps4000OpenUnitAsync(byref(c_status), serialNullTermStr)
        self.checkResult(m)

        return c_status.value

    def _lowLevelOpenUnitProgress(self):
        complete = c_int16()
        progressPercent = c_int16()
        handle = c_int16()

        m = self.lib.ps4000OpenUnitProgress(byref(handle),
                                            byref(progressPercent),
                                            byref(complete))
        self.checkResult(m)

        if complete.value != 0:
            self.handle = handle.value

        # if we only wanted to return one value, we could do somethign like
        # progressPercent = progressPercent * (1 - 0.1 * complete)
        return (progressPercent.value, complete.value)

    def _lowLevelCloseUnit(self):
        m = self.lib.ps4000CloseUnit(c_int16(self.handle))
        self.checkResult(m)

    def _lowLevelEnumerateUnits(self):
        count = c_int16(0)
        m = self.lib.ps4000EnumerateUnits(byref(count), None, None)
        self.checkResult(m)
        # a serial number is rouhgly 8 characters
        # an extra character for the comma
        # and an extra one for the space after the comma?
        # the extra two also work for the null termination
        serialLth = c_int16(count.value * (8 + 2))
        serials = create_string_buffer(serialLth.value + 1)

        m = self.lib.ps4000EnumerateUnits(byref(count), serials,
                                          byref(serialLth))
        self.checkResult(m)

        serialList = str(serials.value.decode('utf-8')).split(',')

        serialList = [x.strip() for x in serialList]

        return serialList

    def _lowLevelSetChannel(self, chNum, enabled, coupling, VRange, VOffset,
                            BWLimited):
        m = self.lib.ps4000SetChannel(c_int16(self.handle), c_enum(chNum),
                                      c_int16(enabled), c_enum(coupling),
                                      c_enum(VRange), c_float(VOffset),
                                      c_enum(BWLimited))
        self.checkResult(m)

    def _lowLevelStop(self):
        m = self.lib.ps4000Stop(c_int16(self.handle))
        self.checkResult(m)

    def _lowLevelGetUnitInfo(self, info):
        s = create_string_buffer(256)
        requiredSize = c_int16(0)

        m = self.lib.ps4000GetUnitInfo(c_int16(self.handle), byref(s),
                                       c_int16(len(s)), byref(requiredSize),
                                       c_enum(info))
        self.checkResult(m)
        if requiredSize.value > len(s):
            s = create_string_buffer(requiredSize.value + 1)
            m = self.lib.ps4000GetUnitInfo(c_int16(self.handle), byref(s),
                                           c_int16(len(s)),
                                           byref(requiredSize), c_enum(info))
            self.checkResult(m)

        # should this bee ascii instead?
        # I think they are equivalent...
        return s.value.decode('utf-8')

    def _lowLevelFlashLed(self, times):
        m = self.lib.ps4000FlashLed(c_int16(self.handle), c_int16(times))
        self.checkResult(m)

    def _lowLevelSetSimpleTrigger(self, enabled, trigsrc, threshold_adc,
                                  direction, delay, timeout_ms):
        m = self.lib.ps4000SetSimpleTrigger(c_int16(self.handle),
                                            c_int16(enabled), c_enum(trigsrc),
                                            c_int16(threshold_adc),
                                            c_enum(direction), c_uint32(delay),
                                            c_int16(timeout_ms))
        self.checkResult(m)

    def _lowLevelRunBlock(self, numPreTrigSamples, numPostTrigSamples,
                          timebase, oversample, segmentIndex):
        timeIndisposedMs = c_int32()
        m = self.lib.ps4000RunBlock(c_int16(self.handle),
                                    c_uint32(numPreTrigSamples),
                                    c_uint32(numPostTrigSamples),
                                    c_uint32(timebase), c_int16(oversample),
                                    byref(timeIndisposedMs),
                                    c_uint16(segmentIndex), c_void_p(),
                                    c_void_p())
        self.checkResult(m)
        return timeIndisposedMs.value

    def _lowLevelIsReady(self):
        ready = c_int16()
        m = self.lib.ps4000IsReady(c_int16(self.handle), byref(ready))
        self.checkResult(m)
        if ready.value:
            return True
        else:
            return False

    def _lowLevelGetTimebase(self, tb, noSamples, oversample, segmentIndex):
        """Return (timeIntervalSeconds, maxSamples)."""
        maxSamples = c_int32()
        sampleRate = c_float()

        m = self.lib.ps4000GetTimebase2(c_int16(self.handle), c_uint32(tb),
                                        c_uint32(noSamples), byref(sampleRate),
                                        c_int16(oversample), byref(maxSamples),
                                        c_uint16(segmentIndex))
        self.checkResult(m)

        return (sampleRate.value / 1.0E9, maxSamples.value)

    def getTimeBaseNum(self, sampleTimeS):
        """Return sample time in seconds to timebase as int for API calls."""
        raise NotImplementedError('Timebase functions have not been '
                                  'written for the scope.')

    def getTimestepFromTimebase(self, timebase):
        """Return timebase to sampletime as seconds."""
        raise NotImplementedError('Timebase functions have not been '
                                  'written for the scope.')

    def _getTimeBaseNum4223(self, sampleTimeS):
        """Return sample time in seconds to timebase as int for API calls."""
        maxSampleTime = (((2**32 - 1) - 4) / 2e7)

        if sampleTimeS <= 12.5E-9:
            timebase = math.floor(math.log(sampleTimeS * 8E7, 2))
            timebase = max(timebase, 0)
        else:
            # Otherwise in range 2^32-1
            if sampleTimeS > maxSampleTime:
                sampleTimeS = maxSampleTime

            timebase = math.floor((sampleTimeS * 2e7) + 1)

        # is this cast needed?
        timebase = int(timebase)
        return timebase

    def _getTimestepFromTimebase4223(self, timebase):
        """Return timebase to sampletime as seconds."""
        if timebase < 3:
            dt = 2.**timebase / 8e7
        else:
            dt = (timebase - 1) / 2e7
        return dt

    def _getTimestepFromTimebase4262(self, timebase):
        """Return timebase to sampletime as seconds for ps4262."""
        someConstant = 10000000.0
        return (timebase + 1) / someConstant

    def _getTimeBaseNum4262(self, sampleTimeS):
        """Return timebase for the ps4262.

        Return sample time in seconds to timebase as
        int for API calls for ps4262.
        """
        someConstant = 10000000.0
        maxSampleTime = ((2**30) / someConstant)
        minSampleTime = 1 / someConstant

        if sampleTimeS <= minSampleTime:
            timebase = 0
        elif sampleTimeS >= maxSampleTime:
            timebase = (2**30)
        else:
            timebase = round(sampleTimeS * someConstant) - 1

        # is this cast needed?
        timebase = int(timebase)
        return timebase

    def _lowLevelSetDataBuffer(self, channel, data, downSampleMode,
                               segmentIndex):
        """Set the data buffer.

        Be sure to call _lowLevelClearDataBuffer
        when you are done with the data array
        or else subsequent calls to GetValue will still use the same array.

        segmentIndex is unused, but required by other versions of the API
        (eg PS5000a)
        """
        dataPtr = data.ctypes.data_as(POINTER(c_int16))
        numSamples = len(data)

        m = self.lib.ps4000SetDataBuffer(c_int16(self.handle), c_enum(channel),
                                         dataPtr, c_uint32(numSamples))
        self.checkResult(m)

    def _lowLevelClearDataBuffer(self, channel, segmentIndex):
        m = self.lib.ps4000SetDataBuffer(c_int16(self.handle), c_enum(channel),
                                         c_void_p(), c_uint32(0), c_enum(0))
        self.checkResult(m)

    def _lowLevelGetValues(self, numSamples, startIndex, downSampleRatio,
                           downSampleMode, segmentIndex):
        numSamplesReturned = c_uint32()
        numSamplesReturned.value = numSamples
        overflow = c_int16()
        m = self.lib.ps4000GetValues(c_int16(self.handle),
                                     c_uint32(startIndex),
                                     byref(numSamplesReturned),
                                     c_uint32(downSampleRatio),
                                     c_enum(downSampleMode),
                                     c_uint16(segmentIndex), byref(overflow))
        self.checkResult(m)
        return (numSamplesReturned.value, overflow.value)

    def _lowLevelSetAWGSimpleDeltaPhase(self, waveform, deltaPhase,
                                        offsetVoltage, pkToPk, indexMode,
                                        shots, triggerType, triggerSource):
        """Waveform should be an array of shorts."""
        waveformPtr = waveform.ctypes.data_as(POINTER(c_int16))

        m = self.lib.ps4000SetSigGenArbitrary(
            c_int16(self.handle),
            c_uint32(int(offsetVoltage * 1E6)),  # offset voltage in microvolts
            c_uint32(int(pkToPk * 1E6)),  # pkToPk in microvolts
            c_uint32(int(deltaPhase)),  # startDeltaPhase
            c_uint32(int(deltaPhase)),  # stopDeltaPhase
            c_uint32(0),  # deltaPhaseIncrement
            c_uint32(0),  # dwellCount
            waveformPtr,  # arbitraryWaveform
            c_int32(len(waveform)),  # arbitraryWaveformSize
            c_enum(0),  # sweepType for deltaPhase
            c_enum(0),  # operation (adding random noise and whatnot)
            c_enum(indexMode),  # single, dual, quad
            c_uint32(shots),
            c_uint32(0),  # sweeps
            c_uint32(triggerType),
            c_uint32(triggerSource),
            c_int16(0))  # extInThreshold
        self.checkResult(m)

    def _lowLevelSetSigGenBuiltInSimple(self, offsetVoltage, pkToPk, waveType,
                                        frequency, shots, triggerType,
                                        triggerSource, stopFreq, increment,
                                        dwellTime, sweepType, numSweeps):
        if stopFreq is None:
            stopFreq = frequency

        m = self.lib.ps4000SetSigGenBuiltIn(
            c_int16(self.handle), c_int32(int(offsetVoltage * 1000000)),
            c_int32(int(pkToPk * 1000000)), c_int16(waveType),
            c_float(frequency), c_float(stopFreq), c_float(increment),
            c_float(dwellTime), c_enum(sweepType), c_enum(0), c_uint32(shots),
            c_uint32(numSweeps), c_enum(triggerType), c_enum(triggerSource),
            c_int16(0))
        self.checkResult(m)

    def _lowLevelSigGenSoftwareControl(self, triggerType):

        m = self.lib.ps4000SigGenSoftwareControl(c_int16(self.handle),
                                                 c_enum(triggerType))
        self.checkResult(m)

    # use VRange=5 for +/- 500mV signal range
    # OR
    # VRange=8 for +/- 5V range
    def _lowLevelSetExtTriggerRange(self, VRange):
        m = self.lib.ps4000SetExtTriggerRange(c_int16(self.handle),
                                              c_enum(VRange))
        self.checkResult(m)

    ####################################################################
    # Untested functions below                                         #
    #                                                                  #
    ####################################################################

    def _lowLevelGetMaxDownSampleRatio(self, noOfUnaggregatedSamples,
                                       downSampleRatioMode, segmentIndex):
        maxDownSampleRatio = c_uint32()

        m = self.lib.ps4000GetMaxDownSampleRatio(
            c_int16(self.handle), c_uint32(noOfUnaggregatedSamples),
            byref(maxDownSampleRatio), c_enum(downSampleRatioMode),
            c_uint16(segmentIndex))
        self.checkResult(m)

        return maxDownSampleRatio.value

    def _lowLevelGetNoOfCaptures(self):
        nCaptures = c_uint32()

        m = self.lib.ps4000GetNoOfCaptures(c_int16(self.handle),
                                           byref(nCaptures))
        self.checkResult(m)

        return nCaptures.value

    def _lowLevelGetTriggerTimeOffset(self, segmentIndex):
        time = c_uint64()
        timeUnits = c_enum()

        m = self.lib.ps4000GetTriggerTimeOffset64(c_int16(self.handle),
                                                  byref(time),
                                                  byref(timeUnits),
                                                  c_uint16(segmentIndex))

        self.checkResult(m)

        if timeUnits.value == 0:  # PS4000_FS
            return time.value * 1E-15
        elif timeUnits.value == 1:  # PS4000_PS
            return time.value * 1E-12
        elif timeUnits.value == 2:  # PS4000_NS
            return time.value * 1E-9
        elif timeUnits.value == 3:  # PS4000_US
            return time.value * 1E-6
        elif timeUnits.value == 4:  # PS4000_MS
            return time.value * 1E-3
        elif timeUnits.value == 5:  # PS4000_S
            return time.value * 1E0
        else:
            raise TypeError("Unknown timeUnits %d" % timeUnits.value)

    def _lowLevelMemorySegments(self, nSegments):
        nMaxSamples = c_uint32()

        m = self.lib.ps4000MemorySegments(c_int16(self.handle),
                                          c_uint16(nSegments),
                                          byref(nMaxSamples))
        self.checkResult(m)

        return nMaxSamples.value

    def _lowLevelSetDataBuffers(self, channel, bufferMax, bufferMin,
                                downSampleRatioMode):
        bufferMaxPtr = bufferMax.ctypes.data_as(POINTER(c_int16))
        bufferMinPtr = bufferMin.ctypes.data_as(POINTER(c_int16))
        bufferLth = len(bufferMax)

        m = self.lib.ps4000SetDataBuffers(c_int16(self.handle),
                                          c_enum(channel), bufferMaxPtr,
                                          bufferMinPtr, c_uint32(bufferLth))
        self.checkResult(m)

    def _lowLevelClearDataBuffers(self, channel):
        m = self.lib.ps4000SetDataBuffers(c_int16(self.handle),
                                          c_enum(channel), c_void_p(),
                                          c_void_p(), c_uint32(0))
        self.checkResult(m)

    # Bulk values.
    # These would be nice, but the user would have to provide us
    # with an array.
    # we would have to make sure that it is contiguous amonts other things
    def _lowLevelGetValuesBulk(self, numSamples, fromSegmentIndex,
                               toSegmentIndex, downSampleRatio, downSampleMode,
                               overflow):
        noOfSamples = c_uint32(numSamples)

        m = self.lib.ps4000GetValuesBulk(
            c_int16(self.handle), byref(noOfSamples),
            c_uint16(fromSegmentIndex), c_uint16(toSegmentIndex),
            overflow.ctypes.data_as(POINTER(c_int16)))
        self.checkResult(m)
        return noOfSamples.value

    def _lowLevelSetDataBufferBulk(self, channel, buffer, waveform,
                                   downSampleRatioMode):
        bufferPtr = buffer.ctypes.data_as(POINTER(c_int16))
        bufferLth = len(buffer)

        m = self.lib.ps4000SetDataBufferBulk(c_int16(self.handle),
                                             c_enum(channel), bufferPtr,
                                             c_uint32(bufferLth),
                                             c_uint16(waveform))
        self.checkResult(m)

    def _lowLevelSetNoOfCaptures(self, nCaptures):
        m = self.lib.ps4000SetNoOfCaptures(c_int16(self.handle),
                                           c_uint16(nCaptures))
        self.checkResult(m)

    # ETS Functions
    def _lowLevelSetEts():
        pass

    def _lowLevelSetEtsTimeBuffer():
        pass

    def _lowLevelSetEtsTimeBuffers():
        pass

    def _lowLevelSetExternalClock():
        pass

    # Complicated triggering
    # need to understand structs for this one to work
    def _lowLevelIsTriggerOrPulseWidthQualifierEnabled():
        pass

    def _lowLevelGetValuesTriggerTimeOffsetBulk():
        pass

    def _lowLevelSetTriggerChannelConditions():
        pass

    def _lowLevelSetTriggerChannelDirections():
        pass

    def _lowLevelSetTriggerChannelProperties():
        pass

    def _lowLevelSetPulseWidthQualifier():
        pass

    def _lowLevelSetTriggerDelay():
        pass

    # Async functions
    # would be nice, but we would have to learn to implement callbacks
    def _lowLevelGetValuesAsync():
        pass

    def _lowLevelGetValuesBulkAsync():
        pass

    # overlapped functions
    def _lowLevelGetValuesOverlapped():
        pass

    def _lowLevelGetValuesOverlappedBulk():
        pass

    # Streaming related functions
    def _lowLevelGetStreamingLatestValues():
        pass

    def _lowLevelNoOfStreamingValues(self):
        noOfValues = c_uint32()

        m = self.lib.ps4000NoOfStreamingValues(c_int16(self.handle),
                                               byref(noOfValues))
        self.checkResult(m)

        return noOfValues.value

    def _lowLevelRunStreaming():
        pass

    def _lowLevelStreamingReady():
        pass
Esempio n. 16
0
class PS4000(_PicoscopeBase):
    """The following are low-level functions for the PS4000."""

    LIBNAME = "ps4000"

    MAX_VALUE = 32764
    MIN_VALUE = -32764

    # AWG stuff here:
    AWGMaxSamples = 4096

    AWGDACInterval = 1 / 192000  # [s]
    AWGDACFrequency = 192000  # [Hz]
    AWGPhaseAccumulatorSize = 32

    AWGMaxVal = 32767
    AWGMinVal = -32768

    AWG_INDEX_MODES = {"Single": 0, "Dual": 1, "Quad": 2}

    AWGBufferAddressWidth = 12
    AWGMaxSamples = 2 ** AWGBufferAddressWidth

    SIGGEN_TRIGGER_TYPES = {"Rising": 0, "Falling": 1,
                            "GateHigh": 2, "GateLow": 3}

    SIGGEN_TRIGGER_SOURCES = {"None": 0, "ScopeTrig": 1,
                              "AuxIn": 2, "ExtIn": 3, "SoftTrig": 4}

    # EXT/AUX seems to have an imput impedence of 50 ohm (PS6403B)
    EXT_MAX_VALUE = 32767
    EXT_MIN_VALUE = -32767
    EXT_RANGE_VOLTS = 1

    # I don't think that the 50V range is allowed, but I left it there anyway
    # The 10V and 20V ranges are only allowed in high impedence modes
    CHANNEL_RANGE = [{"rangeV": 20E-3,  "apivalue": 1, "rangeStr": "20 mV"},
                     {"rangeV": 50E-3,  "apivalue": 2, "rangeStr": "50 mV"},
                     {"rangeV": 100E-3, "apivalue": 3, "rangeStr": "100 mV"},
                     {"rangeV": 200E-3, "apivalue": 4, "rangeStr": "200 mV"},
                     {"rangeV": 500E-3, "apivalue": 5, "rangeStr": "500 mV"},
                     {"rangeV": 1.0,    "apivalue": 6, "rangeStr": "1 V"},
                     {"rangeV": 2.0,    "apivalue": 7, "rangeStr": "2 V"},
                     {"rangeV": 5.0,    "apivalue": 8, "rangeStr": "5 V"},
                     {"rangeV": 10.0,   "apivalue": 9, "rangeStr": "10 V"},
                     {"rangeV": 20.0,   "apivalue": 10, "rangeStr": "20 V"},
                     {"rangeV": 50.0,   "apivalue": 11, "rangeStr": "50 V"},
                     ]

    NUM_CHANNELS = 4
    CHANNELS = {"A": 0, "B": 1, "C": 2, "D": 3, "EXT": 4}

    CHANNEL_COUPLINGS = {"DC50": 2, "DC": 1, "AC": 0}

    WAVE_TYPES = {"Sine": 0, "Square": 1, "Triangle": 2,
                  "RampUp": 3, "RampDown": 4,
                  "Sinc": 5, "Gaussian": 6, "HalfSine": 7, "DCVoltage": 8,
                  "WhiteNoise": 9}

    SWEEP_TYPES = {"Up": 0, "Down": 1, "UpDown": 2, "DownUp": 3}

    def __init__(self, serialNumber=None, connect=True):
        """Load DLLs."""
        self.handle = None
        if platform.system() == 'Linux':
            from ctypes import cdll
            # ok I don't know what is wrong with my installer,
            # but I need to include .so.2
            self.lib = cdll.LoadLibrary("lib" + self.LIBNAME + ".so.2")
        elif platform.system() == 'Darwin':
            from picoscope.darwin_utils import LoadLibraryDarwin
            self.lib = LoadLibraryDarwin("lib" + self.LIBNAME + ".dylib")
        else:
            from ctypes import windll
            self.lib = windll.LoadLibrary(str(self.LIBNAME + ".dll"))

        super(PS4000, self).__init__(serialNumber, connect)
        # check to see which model we have and use special functions if needed

        unit_number = self.getUnitInfo('VariantInfo')
        if unit_number == '4262':
            self.getTimestepFromTimebase = self._getTimestepFromTimebase4262
            self.getTimeBaseNum = self._getTimeBaseNum4262
        elif unit_number in {'4223', '4224', '4423', '4424'}:
            self.getTimestepFromTimebase = self._getTimestepFromTimebase4223
            self.getTimeBaseNum = self._getTimeBaseNum4223
        else:
            raise NotImplementedError('Timebase functions have not been '
                                      'written for the ' + unit_number)

    def _lowLevelOpenUnit(self, sn):
        c_handle = c_int16()
        if sn is not None:
            serialNullTermStr = create_string_buffer(sn)
        else:
            serialNullTermStr = None
        # Passing None is the same as passing NULL
        m = self.lib.ps4000OpenUnit(byref(c_handle), serialNullTermStr)
        self.checkResult(m)
        self.handle = c_handle.value

    def _lowLevelOpenUnitAsync(self, sn):
        c_status = c_int16()
        if sn is not None:
            serialNullTermStr = create_string_buffer(sn)
        else:
            serialNullTermStr = None

        # Passing None is the same as passing NULL
        m = self.lib.ps4000OpenUnitAsync(byref(c_status), serialNullTermStr)
        self.checkResult(m)

        return c_status.value

    def _lowLevelOpenUnitProgress(self):
        complete = c_int16()
        progressPercent = c_int16()
        handle = c_int16()

        m = self.lib.ps4000OpenUnitProgress(byref(handle),
                                            byref(progressPercent),
                                            byref(complete))
        self.checkResult(m)

        if complete.value != 0:
            self.handle = handle.value

        # if we only wanted to return one value, we could do somethign like
        # progressPercent = progressPercent * (1 - 0.1 * complete)
        return (progressPercent.value, complete.value)

    def _lowLevelCloseUnit(self):
        m = self.lib.ps4000CloseUnit(c_int16(self.handle))
        self.checkResult(m)

    def _lowLevelEnumerateUnits(self):
        count = c_int16(0)
        m = self.lib.ps4000EnumerateUnits(byref(count), None, None)
        self.checkResult(m)
        # a serial number is rouhgly 8 characters
        # an extra character for the comma
        # and an extra one for the space after the comma?
        # the extra two also work for the null termination
        serialLth = c_int16(count.value * (8 + 2))
        serials = create_string_buffer(serialLth.value + 1)

        m = self.lib.ps4000EnumerateUnits(byref(count), serials,
                                          byref(serialLth))
        self.checkResult(m)

        serialList = str(serials.value.decode('utf-8')).split(',')

        serialList = [x.strip() for x in serialList]

        return serialList

    def _lowLevelSetChannel(self, chNum, enabled, coupling, VRange, VOffset,
                            BWLimited):
        m = self.lib.ps4000SetChannel(c_int16(self.handle), c_enum(chNum),
                                      c_int16(enabled), c_enum(coupling),
                                      c_enum(VRange), c_float(VOffset),
                                      c_enum(BWLimited))
        self.checkResult(m)

    def _lowLevelStop(self):
        m = self.lib.ps4000Stop(c_int16(self.handle))
        self.checkResult(m)

    def _lowLevelGetUnitInfo(self, info):
        s = create_string_buffer(256)
        requiredSize = c_int16(0)

        m = self.lib.ps4000GetUnitInfo(c_int16(self.handle), byref(s),
                                       c_int16(len(s)), byref(requiredSize),
                                       c_enum(info))
        self.checkResult(m)
        if requiredSize.value > len(s):
            s = create_string_buffer(requiredSize.value + 1)
            m = self.lib.ps4000GetUnitInfo(c_int16(self.handle), byref(s),
                                           c_int16(len(s)),
                                           byref(requiredSize), c_enum(info))
            self.checkResult(m)

        # should this bee ascii instead?
        # I think they are equivalent...
        return s.value.decode('utf-8')

    def _lowLevelFlashLed(self, times):
        m = self.lib.ps4000FlashLed(c_int16(self.handle), c_int16(times))
        self.checkResult(m)

    def _lowLevelSetSimpleTrigger(self, enabled, trigsrc, threshold_adc,
                                  direction, delay, timeout_ms):
        m = self.lib.ps4000SetSimpleTrigger(
            c_int16(self.handle), c_int16(enabled),
            c_enum(trigsrc), c_int16(threshold_adc),
            c_enum(direction), c_uint32(delay), c_int16(timeout_ms))
        self.checkResult(m)

    def _lowLevelRunBlock(self, numPreTrigSamples, numPostTrigSamples,
                          timebase, oversample, segmentIndex):
        timeIndisposedMs = c_int32()
        m = self.lib.ps4000RunBlock(
            c_int16(self.handle), c_uint32(numPreTrigSamples),
            c_uint32(numPostTrigSamples), c_uint32(timebase),
            c_int16(oversample), byref(timeIndisposedMs),
            c_uint16(segmentIndex), c_void_p(), c_void_p())
        self.checkResult(m)
        return timeIndisposedMs.value

    def _lowLevelIsReady(self):
        ready = c_int16()
        m = self.lib.ps4000IsReady(c_int16(self.handle), byref(ready))
        self.checkResult(m)
        if ready.value:
            return True
        else:
            return False

    def _lowLevelGetTimebase(self, tb, noSamples, oversample, segmentIndex):
        """Return (timeIntervalSeconds, maxSamples)."""
        maxSamples = c_int32()
        sampleRate = c_float()

        m = self.lib.ps4000GetTimebase2(c_int16(self.handle), c_uint32(tb),
                                        c_uint32(noSamples), byref(sampleRate),
                                        c_int16(oversample), byref(maxSamples),
                                        c_uint16(segmentIndex))
        self.checkResult(m)

        return (sampleRate.value / 1.0E9, maxSamples.value)

    def getTimeBaseNum(self, sampleTimeS):
        """Return sample time in seconds to timebase as int for API calls."""
        raise NotImplementedError('Timebase functions have not been '
                                  'written for the scope.')

    def getTimestepFromTimebase(self, timebase):
        """Return timebase to sampletime as seconds."""
        raise NotImplementedError('Timebase functions have not been '
                                  'written for the scope.')

    def _getTimeBaseNum4223(self, sampleTimeS):
        """Return sample time in seconds to timebase as int for API calls."""
        maxSampleTime = (((2 ** 32 - 1) - 4) / 2e7)

        if sampleTimeS <= 12.5E-9:
            timebase = math.floor(math.log(sampleTimeS * 8E7, 2))
            timebase = max(timebase, 0)
        else:
            # Otherwise in range 2^32-1
            if sampleTimeS > maxSampleTime:
                sampleTimeS = maxSampleTime

            timebase = math.floor((sampleTimeS * 2e7) + 1)

        # is this cast needed?
        timebase = int(timebase)
        return timebase

    def _getTimestepFromTimebase4223(self, timebase):
        """Return timebase to sampletime as seconds."""
        if timebase < 3:
            dt = 2. ** timebase / 8e7
        else:
            dt = (timebase - 1) / 2e7
        return dt

    def _getTimestepFromTimebase4262(self, timebase):
        """Return timebase to sampletime as seconds for ps4262."""
        someConstant = 10000000.0
        return (timebase+1) / someConstant

    def _getTimeBaseNum4262(self, sampleTimeS):
        """Return timebase for the ps4262.

        Return sample time in seconds to timebase as
        int for API calls for ps4262.
        """
        someConstant = 10000000.0
        maxSampleTime = ((2 ** 30) / someConstant)
        minSampleTime = 1 / someConstant

        if sampleTimeS <= minSampleTime:
            timebase = 0
        elif sampleTimeS >= maxSampleTime:
            timebase = (2 ** 30)
        else:
            timebase = round(sampleTimeS * someConstant) - 1

        # is this cast needed?
        timebase = int(timebase)
        return timebase

    def _lowLevelSetDataBuffer(self, channel, data, downSampleMode,
                               segmentIndex):
        """Set the data buffer.

        Be sure to call _lowLevelClearDataBuffer
        when you are done with the data array
        or else subsequent calls to GetValue will still use the same array.

        segmentIndex is unused, but required by other versions of the API
        (eg PS5000a)
        """
        dataPtr = data.ctypes.data_as(POINTER(c_int16))
        numSamples = len(data)

        m = self.lib.ps4000SetDataBuffer(c_int16(self.handle), c_enum(channel),
                                         dataPtr, c_uint32(numSamples))
        self.checkResult(m)

    def _lowLevelClearDataBuffer(self, channel, segmentIndex):
        m = self.lib.ps4000SetDataBuffer(c_int16(self.handle), c_enum(channel),
                                         c_void_p(), c_uint32(0), c_enum(0))
        self.checkResult(m)

    def _lowLevelGetValues(self, numSamples, startIndex, downSampleRatio,
                           downSampleMode, segmentIndex):
        numSamplesReturned = c_uint32()
        numSamplesReturned.value = numSamples
        overflow = c_int16()
        m = self.lib.ps4000GetValues(
            c_int16(self.handle), c_uint32(startIndex),
            byref(numSamplesReturned), c_uint32(downSampleRatio),
            c_enum(downSampleMode), c_uint16(segmentIndex),
            byref(overflow))
        self.checkResult(m)
        return (numSamplesReturned.value, overflow.value)

    def _lowLevelSetAWGSimpleDeltaPhase(self, waveform, deltaPhase,
                                        offsetVoltage, pkToPk, indexMode,
                                        shots, triggerType, triggerSource):
        """Waveform should be an array of shorts."""
        waveformPtr = waveform.ctypes.data_as(POINTER(c_int16))

        m = self.lib.ps4000SetSigGenArbitrary(
            c_int16(self.handle),
            c_uint32(int(offsetVoltage * 1E6)),  # offset voltage in microvolts
            c_uint32(int(pkToPk * 1E6)),         # pkToPk in microvolts
            c_uint32(int(deltaPhase)),           # startDeltaPhase
            c_uint32(int(deltaPhase)),           # stopDeltaPhase
            c_uint32(0),                         # deltaPhaseIncrement
            c_uint32(0),                         # dwellCount
            waveformPtr,                         # arbitraryWaveform
            c_int32(len(waveform)),              # arbitraryWaveformSize
            c_enum(0),                           # sweepType for deltaPhase
            c_enum(0),            # operation (adding random noise and whatnot)
            c_enum(indexMode),                   # single, dual, quad
            c_uint32(shots),
            c_uint32(0),                         # sweeps
            c_uint32(triggerType),
            c_uint32(triggerSource),
            c_int16(0))                          # extInThreshold
        self.checkResult(m)

    def _lowLevelSetSigGenBuiltInSimple(self, offsetVoltage, pkToPk, waveType,
                                        frequency, shots, triggerType,
                                        triggerSource, stopFreq, increment,
                                        dwellTime, sweepType, numSweeps):
        if stopFreq is None:
            stopFreq = frequency

        m = self.lib.ps4000SetSigGenBuiltIn(
            c_int16(self.handle),
            c_int32(int(offsetVoltage * 1000000)),
            c_int32(int(pkToPk * 1000000)),
            c_int16(waveType),
            c_float(frequency), c_float(stopFreq),
            c_float(increment), c_float(dwellTime),
            c_enum(sweepType), c_enum(0),
            c_uint32(shots), c_uint32(numSweeps),
            c_enum(triggerType), c_enum(triggerSource),
            c_int16(0))
        self.checkResult(m)

    def _lowLevelSigGenSoftwareControl(self, triggerType):

        m = self.lib.ps4000SigGenSoftwareControl(
            c_int16(self.handle), c_enum(triggerType))
        self.checkResult(m)

    # use VRange=5 for +/- 500mV signal range
    # OR
    # VRange=8 for +/- 5V range
    def _lowLevelSetExtTriggerRange(self, VRange):
        m = self.lib.ps4000SetExtTriggerRange(
            c_int16(self.handle),
            c_enum(VRange))
        self.checkResult(m)

    ####################################################################
    # Untested functions below                                         #
    #                                                                  #
    ####################################################################

    def _lowLevelGetMaxDownSampleRatio(self, noOfUnaggregatedSamples,
                                       downSampleRatioMode, segmentIndex):
        maxDownSampleRatio = c_uint32()

        m = self.lib.ps4000GetMaxDownSampleRatio(
            c_int16(self.handle),
            c_uint32(noOfUnaggregatedSamples),
            byref(maxDownSampleRatio),
            c_enum(downSampleRatioMode),
            c_uint16(segmentIndex))
        self.checkResult(m)

        return maxDownSampleRatio.value

    def _lowLevelGetNoOfCaptures(self):
        nCaptures = c_uint32()

        m = self.lib.ps4000GetNoOfCaptures(
            c_int16(self.handle), byref(nCaptures))
        self.checkResult(m)

        return nCaptures.value

    def _lowLevelGetTriggerTimeOffset(self, segmentIndex):
        time = c_uint64()
        timeUnits = c_enum()

        m = self.lib.ps4000GetTriggerTimeOffset64(
            c_int16(self.handle),
            byref(time),
            byref(timeUnits),
            c_uint16(segmentIndex))

        self.checkResult(m)

        if timeUnits.value == 0:    # PS4000_FS
            return time.value * 1E-15
        elif timeUnits.value == 1:  # PS4000_PS
            return time.value * 1E-12
        elif timeUnits.value == 2:  # PS4000_NS
            return time.value * 1E-9
        elif timeUnits.value == 3:  # PS4000_US
            return time.value * 1E-6
        elif timeUnits.value == 4:  # PS4000_MS
            return time.value * 1E-3
        elif timeUnits.value == 5:  # PS4000_S
            return time.value * 1E0
        else:
            raise TypeError("Unknown timeUnits %d" % timeUnits.value)

    def _lowLevelMemorySegments(self, nSegments):
        nMaxSamples = c_uint32()

        m = self.lib.ps4000MemorySegments(c_int16(self.handle),
                                          c_uint16(nSegments),
                                          byref(nMaxSamples))
        self.checkResult(m)

        return nMaxSamples.value

    def _lowLevelSetDataBuffers(self, channel, bufferMax, bufferMin,
                                downSampleRatioMode):
        bufferMaxPtr = bufferMax.ctypes.data_as(POINTER(c_int16))
        bufferMinPtr = bufferMin.ctypes.data_as(POINTER(c_int16))
        bufferLth = len(bufferMax)

        m = self.lib.ps4000SetDataBuffers(
            c_int16(self.handle),
            c_enum(channel),
            bufferMaxPtr,
            bufferMinPtr,
            c_uint32(bufferLth))
        self.checkResult(m)

    def _lowLevelClearDataBuffers(self, channel):
        m = self.lib.ps4000SetDataBuffers(
            c_int16(self.handle),
            c_enum(channel),
            c_void_p(),
            c_void_p(),
            c_uint32(0))
        self.checkResult(m)

    # Bulk values.
    # These would be nice, but the user would have to provide us
    # with an array.
    # we would have to make sure that it is contiguous amonts other things
    def _lowLevelGetValuesBulk(self,
                               numSamples,
                               fromSegmentIndex,
                               toSegmentIndex,
                               downSampleRatio,
                               downSampleMode,
                               overflow):
        noOfSamples = c_uint32(numSamples)

        m = self.lib.ps4000GetValuesBulk(
            c_int16(self.handle),
            byref(noOfSamples),
            c_uint16(fromSegmentIndex), c_uint16(toSegmentIndex),
            overflow.ctypes.data_as(POINTER(c_int16)))
        self.checkResult(m)
        return noOfSamples.value

    def _lowLevelSetDataBufferBulk(self, channel, buffer, waveform,
                                   downSampleRatioMode):
        bufferPtr = buffer.ctypes.data_as(POINTER(c_int16))
        bufferLth = len(buffer)

        m = self.lib.ps4000SetDataBufferBulk(
            c_int16(self.handle),
            c_enum(channel),
            bufferPtr,
            c_uint32(bufferLth),
            c_uint16(waveform))
        self.checkResult(m)

    def _lowLevelSetNoOfCaptures(self, nCaptures):
        m = self.lib.ps4000SetNoOfCaptures(
            c_int16(self.handle),
            c_uint16(nCaptures))
        self.checkResult(m)

    # ETS Functions
    def _lowLevelSetEts():
        pass

    def _lowLevelSetEtsTimeBuffer():
        pass

    def _lowLevelSetEtsTimeBuffers():
        pass

    def _lowLevelSetExternalClock():
        pass

    # Complicated triggering
    # need to understand structs for this one to work
    def _lowLevelIsTriggerOrPulseWidthQualifierEnabled():
        pass

    def _lowLevelGetValuesTriggerTimeOffsetBulk():
        pass

    def _lowLevelSetTriggerChannelConditions():
        pass

    def _lowLevelSetTriggerChannelDirections():
        pass

    def _lowLevelSetTriggerChannelProperties():
        pass

    def _lowLevelSetPulseWidthQualifier():
        pass

    def _lowLevelSetTriggerDelay():
        pass

    # Async functions
    # would be nice, but we would have to learn to implement callbacks
    def _lowLevelGetValuesAsync():
        pass

    def _lowLevelGetValuesBulkAsync():
        pass

    # overlapped functions
    def _lowLevelGetValuesOverlapped():
        pass

    def _lowLevelGetValuesOverlappedBulk():
        pass

    # Streaming related functions
    def _lowLevelGetStreamingLatestValues():
        pass

    def _lowLevelNoOfStreamingValues(self):
        noOfValues = c_uint32()

        m = self.lib.ps4000NoOfStreamingValues(
            c_int16(self.handle), byref(noOfValues))
        self.checkResult(m)

        return noOfValues.value

    def _lowLevelRunStreaming():
        pass

    def _lowLevelStreamingReady():
        pass
Esempio n. 17
0
class PS3000a(_PicoscopeBase):
    """The following are low-level functions for the PS3000a."""

    LIBNAME = "ps3000a"

    NUM_CHANNELS = 4
    CHANNELS = {"A": 0, "B": 1, "C": 2, "D": 3,
                "External": 4, "MaxChannels": 4, "TriggerAux": 5}

    ADC_RESOLUTIONS = {"8": 0, "12": 1, "14": 2, "15": 3, "16": 4}

    CHANNEL_RANGE = [{"rangeV": 10E-3, "apivalue": 0, "rangeStr": "10 mV"},
                     {"rangeV": 20E-3, "apivalue": 1, "rangeStr": "20 mV"},
                     {"rangeV": 50E-3, "apivalue": 2, "rangeStr": "50 mV"},
                     {"rangeV": 100E-3, "apivalue": 3, "rangeStr": "100 mV"},
                     {"rangeV": 200E-3, "apivalue": 4, "rangeStr": "200 mV"},
                     {"rangeV": 500E-3, "apivalue": 5, "rangeStr": "500 mV"},
                     {"rangeV": 1.0, "apivalue": 6, "rangeStr": "1 V"},
                     {"rangeV": 2.0, "apivalue": 7, "rangeStr": "2 V"},
                     {"rangeV": 5.0, "apivalue": 8, "rangeStr": "5 V"},
                     {"rangeV": 10.0, "apivalue": 9, "rangeStr": "10 V"},
                     {"rangeV": 20.0, "apivalue": 10, "rangeStr": "20 V"},
                     {"rangeV": 50.0, "apivalue": 11, "rangeStr": "50 V"},
                     ]

    CHANNEL_COUPLINGS = {"DC": 1, "AC": 0}

    # has_sig_gen = True
    WAVE_TYPES = {"Sine": 0, "Square": 1, "Triangle": 2,
                  "RampUp": 3, "RampDown": 4,
                  "Sinc": 5, "Gaussian": 6, "HalfSine": 7, "DCVoltage": 8,
                  "WhiteNoise": 9}

    SWEEP_TYPES = {"Up": 0, "Down": 1, "UpDown": 2, "DownUp": 3}

    SIGGEN_TRIGGER_TYPES = {"Rising": 0, "Falling": 1,
                            "GateHigh": 2, "GateLow": 3}
    SIGGEN_TRIGGER_SOURCES = {"None": 0, "ScopeTrig": 1, "AuxIn": 2,
                              "ExtIn": 3, "SoftTrig": 4, "TriggerRaw": 5}

    # This is actually different depending on the AB/CD models
    # I wonder how we could detect the difference between the oscilloscopes
    # I believe we can obtain this information from the setInfo function
    # by readign the hardware version
    # for the PS6403B version, the hardware version is "1 1",
    # an other possibility is that the PS6403B shows up as 6403 when using
    # VARIANT_INFO and others show up as PS6403X where X = A,C or D

    AWGPhaseAccumulatorSize = 32
    AWGBufferAddressWidth = 14
    AWGMaxSamples = 2 ** AWGBufferAddressWidth

    AWGDACInterval = 5E-9  # in seconds
    AWGDACFrequency = 1 / AWGDACInterval

    # Note this is NOT what is written in the Programming guide as of version
    # 10_5_0_28
    # This issue was acknowledged in this thread
    # http://www.picotech.com/support/topic13217.html
    AWGMaxVa = 32767
    AWGMinVal = -32767

    AWG_INDEX_MODES = {"Single": 0, "Dual": 1, "Quad": 2}

    MAX_VALUE_8BIT = 32512
    MIN_VALUE_8BIT = -32512
    MAX_VALUE_OTHER = 32767
    MIN_VALUE_OTHER = -32767

    EXT_RANGE_VOLTS = 5

    def __init__(self, serialNumber=None, connect=True):
        """Load DLL etc."""
        if platform.system() == 'Linux':
            from ctypes import cdll
            self.lib = cdll.LoadLibrary("lib" + self.LIBNAME + ".so")
        elif platform.system() == 'Darwin':
            from picoscope.darwin_utils import LoadLibraryDarwin
            self.lib = LoadLibraryDarwin("lib" + self.LIBNAME + ".dylib")
        else:
            from ctypes import windll
            self.lib = windll.LoadLibrary(str(self.LIBNAME + ".dll"))

        self.resolution = self.ADC_RESOLUTIONS["8"]

        super(PS3000a, self).__init__(serialNumber, connect)

    def _lowLevelOpenUnit(self, sn):
        c_handle = c_int16()
        if sn is not None:
            serialNullTermStr = byref(create_string_buffer(sn))
        else:
            serialNullTermStr = None
        # Passing None is the same as passing NULL
        m = self.lib.ps3000aOpenUnit(byref(c_handle), serialNullTermStr)
        self.handle = c_handle.value

        # copied over from ps5000a:
        # This will check if the power supply is not connected
        # and change the power supply accordingly
        # Personally (me = Mark), I don't like this
        # since the user should address this immediately, and we
        # shouldn't let this go as a soft error
        # but I think this should do for now
        if m == 0x11A:
            self.changePowerSource("PICO_POWER_SUPPLY_NOT_CONNECTED")
        else:
            self.checkResult(m)

    def _lowLevelCloseUnit(self):
        m = self.lib.ps3000aCloseUnit(c_int16(self.handle))
        self.checkResult(m)

    def _lowLevelEnumerateUnits(self):
        count = c_int16(0)
        m = self.lib.ps3000aEnumerateUnits(byref(count), None, None)
        self.checkResult(m)
        # a serial number is rouhgly 8 characters
        # an extra character for the comma
        # and an extra one for the space after the comma?
        # the extra two also work for the null termination
        serialLth = c_int16(count.value * (8 + 2))
        serials = create_string_buffer(serialLth.value + 1)

        m = self.lib.ps3000aEnumerateUnits(byref(count), serials,
                                           byref(serialLth))
        self.checkResult(m)

        serialList = str(serials.value.decode('utf-8')).split(',')

        serialList = [x.strip() for x in serialList]

        return serialList

    def _lowLevelSetChannel(self, chNum, enabled, coupling, VRange, VOffset,
                            BWLimited):
        m = self.lib.ps3000aSetChannel(c_int16(self.handle), c_enum(chNum),
                                       c_int16(enabled), c_enum(coupling),
                                       c_enum(VRange), c_float(VOffset))
        self.checkResult(m)

    def _lowLevelStop(self):
        m = self.lib.ps3000aStop(c_int16(self.handle))
        self.checkResult(m)

    def _lowLevelGetUnitInfo(self, info):
        s = create_string_buffer(256)
        requiredSize = c_int16(0)

        m = self.lib.ps3000aGetUnitInfo(c_int16(self.handle), byref(s),
                                        c_int16(len(s)), byref(requiredSize),
                                        c_enum(info))
        self.checkResult(m)
        if requiredSize.value > len(s):
            s = create_string_buffer(requiredSize.value + 1)
            m = self.lib.ps3000aGetUnitInfo(c_int16(self.handle), byref(s),
                                            c_int16(len(s)),
                                            byref(requiredSize), c_enum(info))
            self.checkResult(m)

        # should this bee ascii instead?
        # I think they are equivalent...
        return s.value.decode('utf-8')

    def _lowLevelFlashLed(self, times):
        m = self.lib.ps3000aFlashLed(c_int16(self.handle), c_int16(times))
        self.checkResult(m)

    def _lowLevelSetSimpleTrigger(self, enabled, trigsrc, threshold_adc,
                                  direction, delay, auto):
        m = self.lib.ps3000aSetSimpleTrigger(
            c_int16(self.handle), c_int16(enabled),
            c_enum(trigsrc), c_int16(threshold_adc),
            c_enum(direction), c_uint32(delay), c_int16(auto))
        self.checkResult(m)

    def _lowLevelSetNoOfCaptures(self, numCaptures):
        m = self.lib.ps3000aSetNoOfCaptures(c_int16(self.handle),
                                            c_uint16(numCaptures))
        self.checkResult(m)

    def _lowLevelMemorySegments(self, numSegments):
        maxSamples = c_int32()
        m = self.lib.ps3000aMemorySegments(c_int16(self.handle),
                                           c_uint16(numSegments),
                                           byref(maxSamples))
        self.checkResult(m)
        return maxSamples.value

    def _lowLevelGetMaxSegments(self):
        maxSegments = c_int16()
        m = self.lib.ps3000aGetMaxSegments(c_int16(self.handle),
                                           byref(maxSegments))
        self.checkResult(m)
        return maxSegments.value

    def _lowLevelRunBlock(self, numPreTrigSamples, numPostTrigSamples,
                          timebase, oversample, segmentIndex):
        # NOT: Oversample is NOT used!
        timeIndisposedMs = c_int32()
        m = self.lib.ps3000aRunBlock(
            c_int16(self.handle), c_uint32(numPreTrigSamples),
            c_uint32(numPostTrigSamples), c_uint32(timebase),
            c_int16(oversample), byref(timeIndisposedMs),
            c_uint32(segmentIndex),
            c_void_p(), c_void_p())
        self.checkResult(m)
        return timeIndisposedMs.value

    def _lowLevelIsReady(self):
        ready = c_int16()
        m = self.lib.ps3000aIsReady(c_int16(self.handle), byref(ready))
        self.checkResult(m)
        if ready.value:
            return True
        else:
            return False

    def _lowlevelPingUnit(self):
        m = self.lib.ps3000aPingUnit(c_int16(self.handle))
        return m

    def _lowLevelGetTimebase(self, tb, noSamples, oversample, segmentIndex):
        """Return (timeIntervalSeconds, maxSamples)."""
        maxSamples = c_int32()
        intervalNanoSec = c_float()

        m = self.lib.ps3000aGetTimebase2(c_int16(self.handle), c_uint32(tb),
                                         c_uint32(noSamples),
                                         byref(intervalNanoSec),
                                         c_int16(oversample),
                                         byref(maxSamples),
                                         c_uint32(segmentIndex))
        self.checkResult(m)
        # divide by 1e9 to return interval in seconds
        return (intervalNanoSec.value * 1e-9, maxSamples.value)

    def getTimeBaseNum(self, sampleTimeS):
        """Convert sample time in S to something to pass to API Call."""
        maxSampleTime = (((2 ** 32 - 1) - 2) / 125000000)
        if sampleTimeS < 8.0E-9:
            st = math.floor(math.log(sampleTimeS * 1E9, 2))
            st = max(st, 0)
        else:
            if sampleTimeS > maxSampleTime:
                sampleTimeS = maxSampleTime
            st = math.floor((sampleTimeS * 125000000) + 2)

        # is this cast needed?
        st = int(st)
        return st

    def getTimestepFromTimebase(self, timebase):
        """Take API timestep code and returns the sampling period.

        API timestep is an integer from 0-32
        """
        if timebase < 3:
            dt = 2. ** timebase / 1.0E9
        else:
            dt = (timebase - 2.0) / 125000000.
        return dt

    def _lowLevelSetAWGSimpleDeltaPhase(self, waveform, deltaPhase,
                                        offsetVoltage, pkToPk, indexMode,
                                        shots, triggerType, triggerSource):
        """Waveform should be an array of shorts."""
        waveformPtr = waveform.ctypes.data_as(POINTER(c_int16))

        m = self.lib.ps3000aSetSigGenArbitrary(
            c_int16(self.handle),
            c_uint32(int(offsetVoltage * 1E6)),  # offset voltage in microvolts
            c_uint32(int(pkToPk * 1E6)),         # pkToPk in microvolts
            c_uint32(int(deltaPhase)),           # startDeltaPhase
            c_uint32(int(deltaPhase)),           # stopDeltaPhase
            c_uint32(0),                         # deltaPhaseIncrement
            c_uint32(0),                         # dwellCount
            waveformPtr,                         # arbitraryWaveform
            c_int32(len(waveform)),              # arbitraryWaveformSize
            c_enum(0),                           # sweepType for deltaPhase
            c_enum(0),            # operation (adding random noise and whatnot)
            c_enum(indexMode),                   # single, dual, quad
            c_uint32(shots),
            c_uint32(0),                         # sweeps
            c_uint32(triggerType),
            c_uint32(triggerSource),
            c_int16(0))                          # extInThreshold
        self.checkResult(m)

    def _lowLevelSetDataBuffer(self, channel, data, downSampleMode,
                               segmentIndex):
        """Set the data buffer.

        Be sure to call _lowLevelClearDataBuffer
        when you are done with the data array
        or else subsequent calls to GetValue will still use the same array.
        """
        dataPtr = data.ctypes.data_as(POINTER(c_int16))
        numSamples = len(data)

        m = self.lib.ps3000aSetDataBuffer(
            c_int16(self.handle), c_enum(channel),
            dataPtr, c_int32(numSamples),
            c_uint32(segmentIndex),
            c_enum(downSampleMode))
        self.checkResult(m)

    def _lowLevelSetDataBufferBulk(self, channel, data, segmentIndex,
                                   downSampleMode):
        """Set the bulk data buffer.

        In ps3000a, ps3000aSetDataBuffer combines the functionality of
        psX000YSetDataBuffer and psX000YSetDataBufferBulk. Since the rapid
        block functions in picoscope.py call the Bulk version, a delegator is
        needed. Note that the order of segmentIndex and downSampleMode is
        reversed.
        """
        self._lowLevelSetDataBuffer(channel, data, downSampleMode,
                                    segmentIndex)

    def _lowLevelSetMultipleDataBuffers(self, channel, data, downSampleMode):
        max_segments = self._lowLevelGetMaxSegments()
        if data.shape[0] < max_segments:
            raise ValueError("data array has fewer rows than current number " +
                             "of memory segments")
        if data.shape[1] < self.maxSamples:
            raise ValueError("data array has fewer columns than maxSamples")

        for i in range(max_segments):
            m = self._lowLevelSetDataBuffer(channel, data[i, :],
                                            downSampleMode, i)
            self.checkResult(m)

    def _lowLevelClearDataBuffer(self, channel, segmentIndex):
        m = self.lib.ps3000aSetDataBuffer(c_int16(self.handle),
                                          c_enum(channel),
                                          c_void_p(), c_uint32(0),
                                          c_uint32(segmentIndex),
                                          c_enum(0))
        self.checkResult(m)

    def _lowLevelGetValues(self, numSamples, startIndex, downSampleRatio,
                           downSampleMode, segmentIndex):
        numSamplesReturned = c_uint32()
        numSamplesReturned.value = numSamples
        overflow = c_int16()
        m = self.lib.ps3000aGetValues(
            c_int16(self.handle), c_uint32(startIndex),
            byref(numSamplesReturned), c_uint32(downSampleRatio),
            c_enum(downSampleMode), c_uint32(segmentIndex),
            byref(overflow))
        self.checkResult(m)
        return (numSamplesReturned.value, overflow.value)

    def _lowLevelGetValuesBulk(self, numSamples, fromSegment, toSegment,
                               downSampleRatio, downSampleMode, overflow):
        m = self.lib.ps3000aGetValuesBulk(
            c_int16(self.handle),
            byref(c_uint32(numSamples)),
            c_uint32(fromSegment),
            c_uint32(toSegment),
            c_uint32(downSampleRatio),
            c_int16(downSampleMode),
            overflow.ctypes.data_as(POINTER(c_int16))
            )
        self.checkResult(m)
        return overflow, numSamples

    def _lowLevelSetSigGenBuiltInSimple(self, offsetVoltage, pkToPk, waveType,
                                        frequency, shots, triggerType,
                                        triggerSource, stopFreq, increment,
                                        dwellTime, sweepType, numSweeps):
        # TODO, I just noticed that V2 exists
        # Maybe change to V2 in the future
        if stopFreq is None:
            stopFreq = frequency

        m = self.lib.ps3000aSetSigGenBuiltIn(
            c_int16(self.handle),
            c_int32(int(offsetVoltage * 1000000)),
            c_int32(int(pkToPk * 1000000)),
            c_int16(waveType),
            c_float(frequency), c_float(stopFreq),
            c_float(increment), c_float(dwellTime),
            c_enum(sweepType), c_enum(0),
            c_uint32(shots), c_uint32(numSweeps),
            c_enum(triggerType), c_enum(triggerSource),
            c_int16(0))
        self.checkResult(m)

    def _lowLevelChangePowerSource(self, powerstate):
        m = self.lib.ps3000aChangePowerSource(
            c_int16(self.handle),
            c_enum(powerstate))
        self.checkResult(m)
Esempio n. 18
0
class PS5000(_PicoscopeBase):
    """The following are low-level functions for the PS5000."""

    LIBNAME = "ps5000"

    MAX_VALUE = 32521
    MIN_VALUE = -32521

    # EXT/AUX seems to have an imput impedence of 50 ohm (PS6403B)
    EXT_MAX_VALUE = 32767
    EXT_MIN_VALUE = -32767
    EXT_RANGE_VOLTS = 1

    # The 10V and 20V ranges are only allowed in high impedence modes
    CHANNEL_RANGE = [{
        "rangeV": 10E-3,
        "apivalue": 1,
        "rangeStr": "10 mV"
    }, {
        "rangeV": 20E-3,
        "apivalue": 2,
        "rangeStr": "20 mV"
    }, {
        "rangeV": 50E-3,
        "apivalue": 3,
        "rangeStr": "50 mV"
    }, {
        "rangeV": 100E-3,
        "apivalue": 4,
        "rangeStr": "100 mV"
    }, {
        "rangeV": 200E-3,
        "apivalue": 5,
        "rangeStr": "200 mV"
    }, {
        "rangeV": 1.0,
        "apivalue": 6,
        "rangeStr": "1 V"
    }, {
        "rangeV": 2.0,
        "apivalue": 7,
        "rangeStr": "2 V"
    }, {
        "rangeV": 5.0,
        "apivalue": 8,
        "rangeStr": "5 V"
    }, {
        "rangeV": 10.0,
        "apivalue": 9,
        "rangeStr": "10 V"
    }, {
        "rangeV": 20.0,
        "apivalue": 10,
        "rangeStr": "20 V"
    }, {
        "rangeV": 50.0,
        "apivalue": 11,
        "rangeStr": "50 V"
    }]

    NUM_CHANNELS = 4
    CHANNELS = {
        "A": 0,
        "B": 1,
        "C": 2,
        "D": 3,
        "External": 4,
        "MaxChannels": 4,
        "TriggerAux": 5
    }

    CHANNEL_COUPLINGS = {"DC": 1, "AC": 0}

    WAVE_TYPES = {
        "Sine": 0,
        "Square": 1,
        "Triangle": 2,
        "RampUp": 3,
        "RampDown": 4,
        "Sinc": 5,
        "Gaussian": 6,
        "HalfSine": 7,
        "DCVoltage": 8,
        "WhiteNoise": 9
    }

    SWEEP_TYPES = {"Up": 0, "Down": 1, "UpDown": 2, "DownUp": 3}

    SIGGEN_TRIGGER_TYPES = {
        "Rising": 0,
        "Falling": 1,
        "GateHigh": 2,
        "GateLow": 3
    }
    SIGGEN_TRIGGER_SOURCES = {
        "None": 0,
        "ScopeTrig": 1,
        "AuxIn": 2,
        "ExtIn": 3,
        "SoftTrig": 4,
        "TriggerRaw": 5
    }

    # This is actually different depending on the AB/CD models
    # I wonder how we could detect the difference between the oscilloscopes
    # I believe we can obtain this information from the setInfo function
    # by readign the hardware version
    # for the PS6403B version, the hardware version is "1 1",
    # an other possibility is that the PS6403B shows up as 6403 when using
    # VARIANT_INFO and others show up as PS6403X where X = A,C or D

    AWGPhaseAccumulatorSize = 32
    AWGBufferAddressWidth = 14
    AWGMaxSamples = 2**AWGBufferAddressWidth

    AWGDACInterval = 5E-9  # in seconds
    AWGDACFrequency = 1 / AWGDACInterval

    # Note this is NOT what is written in the Programming guide as of version
    # 10_5_0_28
    # This issue was acknowledged in this thread
    # http://www.picotech.com/support/topic13217.html
    AWGMaxVal = 0x0FFF
    AWGMinVal = 0x0000

    AWG_INDEX_MODES = {"Single": 0, "Dual": 1, "Quad": 2}

    def __init__(self, serialNumber=None, connect=True):
        """Load DLLs."""
        if platform.system() == 'Linux':
            from ctypes import cdll
            # ok I don't know what is wrong with my installer,
            # but I need to include .so.2
            self.lib = cdll.LoadLibrary("lib" + self.LIBNAME + ".so.2")
        elif platform.system() == 'Darwin':
            from picoscope.darwin_utils import LoadLibraryDarwin
            self.lib = LoadLibraryDarwin("lib" + self.LIBNAME + ".dylib")
        else:
            from ctypes import windll
            from ctypes.util import find_library
            self.lib = windll.LoadLibrary(
                find_library(str(self.LIBNAME + ".dll")))

        super(PS5000, self).__init__(serialNumber, connect)

    def _lowLevelOpenUnit(self, serialNumber):
        c_handle = c_int16()
        if serialNumber is not None:
            serialNumberStr = create_string_buffer(
                bytes(serialNumber, encoding='utf-8'))
        else:
            serialNumberStr = None
        # Passing None is the same as passing NULL
        m = self.lib.ps5000OpenUnit(byref(c_handle), serialNumberStr)
        self.checkResult(m)
        self.handle = c_handle.value

    def _lowLevelOpenUnitAsync(self, serialNumber):
        c_status = c_int16()
        if serialNumber is not None:
            serialNumberStr = create_string_buffer(
                bytes(serialNumber, encoding='utf-8'))
        else:
            serialNumberStr = None
        # Passing None is the same as passing NULL
        m = self.lib.ps5000OpenUnitAsync(byref(c_status), serialNumberStr)
        self.checkResult(m)

        return c_status.value

    def _lowLevelOpenUnitProgress(self):
        complete = c_int16()
        progressPercent = c_int16()
        handle = c_int16()

        m = self.lib.ps5000OpenUnitProgress(byref(handle),
                                            byref(progressPercent),
                                            byref(complete))
        self.checkResult(m)

        if complete.value != 0:
            self.handle = handle.value

        # if we only wanted to return one value, we could do somethign like
        # progressPercent = progressPercent * (1 - 0.1 * complete)
        return (progressPercent.value, complete.value)

    def _lowLevelCloseUnit(self):
        m = self.lib.ps5000CloseUnit(c_int16(self.handle))
        self.checkResult(m)

    def _lowLevelEnumerateUnits(self):
        count = c_int16(0)
        m = self.lib.ps5000EnumerateUnits(byref(count), None, None)
        self.checkResult(m)
        # a serial number is rouhgly 8 characters
        # an extra character for the comma
        # and an extra one for the space after the comma?
        # the extra two also work for the null termination
        serialLth = c_int16(count.value * (8 + 2))
        serials = create_string_buffer(serialLth.value + 1)

        m = self.lib.ps5000EnumerateUnits(byref(count), serials,
                                          byref(serialLth))
        self.checkResult(m)

        serialList = str(serials.value.decode('utf-8')).split(',')

        serialList = [x.strip() for x in serialList]

        return serialList

    def _lowLevelSetChannel(self, chNum, enabled, coupling, VRange, VOffset,
                            BWLimited):
        m = self.lib.ps5000SetChannel(c_int16(self.handle), c_enum(chNum),
                                      c_int16(enabled), c_enum(coupling),
                                      c_enum(VRange), c_float(VOffset),
                                      c_enum(BWLimited))  # 2 for PS6404
        self.checkResult(m)

    def _lowLevelStop(self):
        m = self.lib.ps5000Stop(c_int16(self.handle))
        self.checkResult(m)

    def _lowLevelGetUnitInfo(self, info):
        s = create_string_buffer(256)
        requiredSize = c_int16(0)

        m = self.lib.ps6000GetUnitInfo(c_int16(self.handle), byref(s),
                                       c_int16(len(s)), byref(requiredSize),
                                       c_enum(info))
        self.checkResult(m)
        if requiredSize.value > len(s):
            s = create_string_buffer(requiredSize.value + 1)
            m = self.lib.ps5000GetUnitInfo(c_int16(self.handle), byref(s),
                                           c_int16(len(s)),
                                           byref(requiredSize), c_enum(info))
            self.checkResult(m)

        # should this bee ascii instead?
        # I think they are equivalent...
        return s.value.decode('utf-8')

    def _lowLevelFlashLed(self, times):
        m = self.lib.ps5000FlashLed(c_int16(self.handle), c_int16(times))
        self.checkResult(m)

    def _lowLevelSetSimpleTrigger(self, enabled, trigsrc, threshold_adc,
                                  direction, delay, timeout_ms):
        m = self.lib.ps5000SetSimpleTrigger(c_int16(self.handle),
                                            c_int16(enabled), c_enum(trigsrc),
                                            c_int16(threshold_adc),
                                            c_enum(direction), c_uint32(delay),
                                            c_int16(timeout_ms))
        self.checkResult(m)

    def _lowLevelRunBlock(self, numPreTrigSamples, numPostTrigSamples,
                          timebase, oversample, segmentIndex, callback,
                          pParameter):
        timeIndisposedMs = c_int32()
        m = self.lib.ps5000RunBlock(c_int16(self.handle),
                                    c_uint32(numPreTrigSamples),
                                    c_uint32(numPostTrigSamples),
                                    c_uint32(timebase), c_int16(oversample),
                                    byref(timeIndisposedMs),
                                    c_uint32(segmentIndex), c_void_p(),
                                    c_void_p())
        # According to the documentation, 'callback, pParameter' should work
        # instead of the last two c_void_p parameters.
        # However to avoid the potential for serious crashes, we decided to
        # not include them in this function call.
        self.checkResult(m)
        return timeIndisposedMs.value

    def _lowLevelIsReady(self):
        ready = c_int16()
        m = self.lib.ps5000IsReady(c_int16(self.handle), byref(ready))
        self.checkResult(m)
        if ready.value:
            return True
        else:
            return False

    def _lowLevelGetTimebase(self, tb, noSamples, oversample, segmentIndex):
        """Return (timeIntervalSeconds, maxSamples)."""
        maxSamples = c_int32()
        sampleRate = c_float()

        m = self.lib.ps5000GetTimebase2(c_int16(self.handle), c_uint32(tb),
                                        c_uint32(noSamples), byref(sampleRate),
                                        c_int16(oversample), byref(maxSamples),
                                        c_uint32(segmentIndex))
        self.checkResult(m)

        return (sampleRate.value / 1.0E9, maxSamples.value)

    def getTimeBaseNum(self, sampleTimeS):
        """Return sample time in seconds to timebase as int for API calls."""
        maxSampleTime = (((2**32 - 1) - 2) / 125000000)

        if sampleTimeS < 8E-9:
            timebase = math.floor(math.log(sampleTimeS * 1E9, 2))
            timebase = max(timebase, 0)
        else:
            # Otherwise in range 2^32-1
            if sampleTimeS > maxSampleTime:
                sampleTimeS = maxSampleTime

            timebase = math.floor((sampleTimeS * 125000000) + 2)

        # is this cast needed?
        timebase = int(timebase)
        return timebase

    def getTimestepFromTimebase(self, timebase):
        """Return timebase to sampletime as seconds."""
        if timebase < 3:
            dt = 2.**timebase / 1E9
        else:
            dt = (timebase - 2) / 125000000.
        return dt

    def _lowLevelSetDataBuffer(self, channel, data, downSampleMode,
                               segmentIndex):
        """Set the data buffer.

        Be sure to call _lowLevelClearDataBuffer
        when you are done with the data array
        or else subsequent calls to GetValue will still use the same array.

        segmentIndex is unused, but required by other versions of the API
        (eg PS5000a)
        """
        dataPtr = data.ctypes.data_as(POINTER(c_int16))
        numSamples = len(data)

        m = self.lib.ps5000SetDataBuffer(c_int16(self.handle), c_enum(channel),
                                         dataPtr, c_uint32(numSamples),
                                         c_enum(downSampleMode))
        self.checkResult(m)

    def _lowLevelClearDataBuffer(self, channel, segmentIndex):
        m = self.lib.ps5000SetDataBuffer(c_int16(self.handle), c_enum(channel),
                                         c_void_p(), c_uint32(0), c_enum(0))
        self.checkResult(m)

    def _lowLevelGetValues(self, numSamples, startIndex, downSampleRatio,
                           downSampleMode, segmentIndex):
        numSamplesReturned = c_uint32()
        numSamplesReturned.value = numSamples
        overflow = c_int16()
        m = self.lib.ps5000GetValues(c_int16(self.handle),
                                     c_uint32(startIndex),
                                     byref(numSamplesReturned),
                                     c_uint32(downSampleRatio),
                                     c_enum(downSampleMode),
                                     c_uint32(segmentIndex), byref(overflow))
        self.checkResult(m)
        return (numSamplesReturned.value, overflow.value)

    ####################################################################
    # Untested functions below                                         #
    #                                                                  #
    ####################################################################
    def _lowLevelSetAWGSimpleDeltaPhase(self, waveform, deltaPhase,
                                        offsetVoltage, pkToPk, indexMode,
                                        shots, triggerType, triggerSource):
        """Waveform should be an array of shorts."""
        waveformPtr = waveform.ctypes.data_as(POINTER(c_int16))

        m = self.lib.ps5000SetSigGenArbitrary(
            c_int16(self.handle),
            c_uint32(int(offsetVoltage * 1E6)),  # offset voltage in microvolts
            c_uint32(int(pkToPk * 1E6)),  # pkToPk in microvolts
            c_uint32(int(deltaPhase)),  # startDeltaPhase
            c_uint32(int(deltaPhase)),  # stopDeltaPhase
            c_uint32(0),  # deltaPhaseIncrement
            c_uint32(0),  # dwellCount
            waveformPtr,  # arbitraryWaveform
            c_int32(len(waveform)),  # arbitraryWaveformSize
            c_enum(0),  # sweepType for deltaPhase
            c_enum(0),  # operation (adding random noise and whatnot)
            c_enum(indexMode),  # single, dual, quad
            c_uint32(shots),
            c_uint32(0),  # sweeps
            c_uint32(triggerType),
            c_uint32(triggerSource),
            c_int16(0))  # extInThreshold
        self.checkResult(m)

    def _lowLevelSetSigGenBuiltInSimple(self, offsetVoltage, pkToPk, waveType,
                                        frequency, shots, triggerType,
                                        triggerSource, stopFreq, increment,
                                        dwellTime, sweepType, numSweeps):
        # TODO, I just noticed that V2 exists
        # Maybe change to V2 in the future

        if stopFreq is None:
            stopFreq = frequency

        m = self.lib.ps5000SetSigGenBuiltIn(
            c_int16(self.handle), c_int32(int(offsetVoltage * 1000000)),
            c_int32(int(pkToPk * 1000000)), c_int16(waveType),
            c_float(frequency), c_float(stopFreq), c_float(increment),
            c_float(dwellTime), c_enum(sweepType), c_enum(0), c_uint32(shots),
            c_uint32(numSweeps), c_enum(triggerType), c_enum(triggerSource),
            c_int16(0))
        self.checkResult(m)

    def _lowLevelGetAnalogueOffset(self, range, coupling):
        # TODO, populate properties with this function
        maximumVoltage = c_float()
        minimumVoltage = c_float()

        m = self.lib.ps5000GetAnalogueOffset(c_int16(self.handle),
                                             c_enum(range), c_enum(coupling),
                                             byref(maximumVoltage),
                                             byref(minimumVoltage))
        self.checkResult(m)

        return (maximumVoltage.value, minimumVoltage.value)

    def _lowLevelGetMaxDownSampleRatio(self, noOfUnaggregatedSamples,
                                       downSampleRatioMode, segmentIndex):
        maxDownSampleRatio = c_uint32()

        m = self.lib.ps5000GetMaxDownSampleRatio(
            c_int16(self.handle), c_uint32(noOfUnaggregatedSamples),
            byref(maxDownSampleRatio), c_enum(downSampleRatioMode),
            c_uint32(segmentIndex))
        self.checkResult(m)

        return maxDownSampleRatio.value

    def _lowLevelGetNoOfCaptures(self):
        nCaptures = c_uint32()

        m = self.lib.ps5000GetNoOfCaptures(c_int16(self.handle),
                                           byref(nCaptures))
        self.checkResult(m)

        return nCaptures.value

    def _lowLevelGetTriggerTimeOffset(self, segmentIndex):
        time = c_uint64()
        timeUnits = c_enum()

        m = self.lib.ps5000GetTriggerTimeOffset64(c_int16(self.handle),
                                                  byref(time),
                                                  byref(timeUnits),
                                                  c_uint32(segmentIndex))
        self.checkResult(m)

        if timeUnits.value == 0:  # PS5000_FS
            return time.value * 1E-15
        elif timeUnits.value == 1:  # PS5000_PS
            return time.value * 1E-12
        elif timeUnits.value == 2:  # PS5000_NS
            return time.value * 1E-9
        elif timeUnits.value == 3:  # PS5000_US
            return time.value * 1E-6
        elif timeUnits.value == 4:  # PS5000_MS
            return time.value * 1E-3
        elif timeUnits.value == 5:  # PS5000_S
            return time.value * 1E0
        else:
            raise TypeError("Unknown timeUnits %d" % timeUnits.value)

    def _lowLevelMemorySegments(self, nSegments):
        nMaxSamples = c_uint32()

        m = self.lib.ps5000MemorySegments(c_int16(self.handle),
                                          c_uint32(nSegments),
                                          byref(nMaxSamples))
        self.checkResult(m)

        return nMaxSamples.value

    def _lowLevelSetDataBuffers(self, channel, bufferMax, bufferMin,
                                downSampleRatioMode):
        bufferMaxPtr = bufferMax.ctypes.data_as(POINTER(c_int16))
        bufferMinPtr = bufferMin.ctypes.data_as(POINTER(c_int16))
        bufferLth = len(bufferMax)

        m = self.lib.ps5000SetDataBuffers(c_int16(self.handle),
                                          c_enum(channel), bufferMaxPtr,
                                          bufferMinPtr, c_uint32(bufferLth),
                                          c_enum(downSampleRatioMode))
        self.checkResult(m)

    def _lowLevelClearDataBuffers(self, channel):
        m = self.lib.ps5000SetDataBuffers(c_int16(self.handle),
                                          c_enum(channel), c_void_p(),
                                          c_void_p(), c_uint32(0), c_enum(0))
        self.checkResult(m)

    # Bulk values.
    # These would be nice, but the user would have to provide us
    # with an array.
    # we would have to make sure that it is contiguous amonts other things
    def _lowLevelGetValuesBulk(self, numSamples, fromSegmentIndex,
                               toSegmentIndex, downSampleRatio,
                               downSampleRatioMode, overflow):
        noOfSamples = c_uint32(numSamples)

        m = self.lib.ps5000GetValuesBulk(
            c_int16(self.handle), byref(noOfSamples),
            c_uint32(fromSegmentIndex), c_uint32(toSegmentIndex),
            c_uint32(downSampleRatio), c_enum(downSampleRatioMode),
            overflow.ctypes.data_as(POINTER(c_int16)))
        self.checkResult(m)
        return noOfSamples.value

    def _lowLevelSetDataBufferBulk(self, channel, buffer, waveform,
                                   downSampleRatioMode):
        bufferPtr = buffer.ctypes.data_as(POINTER(c_int16))
        bufferLth = len(buffer)

        m = self.lib.ps5000SetDataBufferBulk(c_int16(self.handle),
                                             c_enum(channel), bufferPtr,
                                             c_uint32(bufferLth),
                                             c_uint32(waveform),
                                             c_enum(downSampleRatioMode))
        self.checkResult(m)

    def _lowLevelSetDataBuffersBulk(self, channel, bufferMax, bufferMin,
                                    waveform, downSampleRatioMode):
        bufferMaxPtr = bufferMax.ctypes.data_as(POINTER(c_int16))
        bufferMinPtr = bufferMin.ctypes.data_as(POINTER(c_int16))

        bufferLth = len(bufferMax)

        m = self.lib.ps5000SetDataBuffersBulk(c_int16(self.handle),
                                              c_enum(channel),
                                              bufferMaxPtr, bufferMinPtr,
                                              c_uint32(bufferLth),
                                              c_uint32(waveform),
                                              c_enum(downSampleRatioMode))
        self.checkResult(m)

    def _lowLevelSetNoOfCaptures(self, nCaptures):
        m = self.lib.ps5000SetNoOfCaptures(c_int16(self.handle),
                                           c_uint32(nCaptures))
        self.checkResult(m)

    # ETS Functions
    def _lowLevelSetEts():
        pass

    def _lowLevelSetEtsTimeBuffer():
        pass

    def _lowLevelSetEtsTimeBuffers():
        pass

    def _lowLevelSetExternalClock():
        pass

    # Complicated triggering
    # need to understand structs for this one to work
    def _lowLevelIsTriggerOrPulseWidthQualifierEnabled():
        pass

    def _lowLevelGetValuesTriggerTimeOffsetBulk():
        pass

    def _lowLevelSetTriggerChannelConditions():
        pass

    def _lowLevelSetTriggerChannelDirections():
        pass

    def _lowLevelSetTriggerChannelProperties():
        pass

    def _lowLevelSetPulseWidthQualifier():
        pass

    def _lowLevelSetTriggerDelay():
        pass

    # Async functions
    # would be nice, but we would have to learn to implement callbacks
    def _lowLevelGetValuesAsync():
        pass

    def _lowLevelGetValuesBulkAsync():
        pass

    # overlapped functions
    def _lowLevelGetValuesOverlapped():
        pass

    def _lowLevelGetValuesOverlappedBulk():
        pass

    # Streaming related functions
    def _lowLevelGetStreamingLatestValues():
        pass

    def _lowLevelNoOfStreamingValues(self):
        noOfValues = c_uint32()

        m = self.lib.ps5000NoOfStreamingValues(c_int16(self.handle),
                                               byref(noOfValues))
        self.checkResult(m)

        return noOfValues.value

    def _lowLevelRunStreaming():
        pass

    def _lowLevelStreamingReady():
        pass