Source code for mtca4u

# SPDX-FileCopyrightText: Deutsches Elektronen-Synchrotron DESY, MSK, ChimeraTK Project <chimeratk-support@desy.de>
# SPDX-License-Identifier: LGPL-3.0-or-later

import _da_python_bindings as mtca4udeviceaccess  # alias quick fix
import numpy
import sys

__version__ = "02.03.00"

# http://stackoverflow.com/questions/4219717/how-to-assert-output-with-nosetest-unittest-in-python


[docs]def get_info(outputStream=sys.stdout): """ prints details about the module and the deviceaccess library against which it was linked Parameters ---------- outputStream: optional default: sys.stdout Returns ------- Examples -------- >>> import mtca4u >>> mtca4u.get_info() mtca4uPy v02.03.00, linked with mtca4u-deviceaccess v$ChimeraTK-DeviceAccess_VERSION} """ outputStream.write( "mtca4uPy v02.03.00, linked with mtca4u-deviceaccess v02.06")
[docs]def set_dmap_location(dmapFileLocation): """ Sets the location of the dmap file to use The library will check the user specified device Alias names (when creating devices) in this dmap file. Once set, the library will look at this dmap file through out the program lifetime. This is true until a new dmap file is set again using set_dmap_location Parameters ---------- dmapFileLocation: string Path to the desired dmap file. Returns ------- Examples -------- >>> import mtca4u >>> mtca4u.set_dmap_location("../my_example_dmap_file.dmap") >>> device = mtca4u.Device("my_card") # my_card is a alias in my_example_dmap_file.dmap See Also -------- get_dmap_location: View the current dmap file which the library uses for device name (alias) lookup. Device : Open device using specified alias names or using device id and mapfile """ # os.environ["DMAP_FILE"] = dmapFileLocation mtca4udeviceaccess.setDmapFile(dmapFileLocation)
[docs]def get_dmap_location(): """ Get the dmap file which is currently in use by the library. Method returns the file path of the dmap file the library currently uses. This is the dmap file the library uses to look up the device name(alias) and its details Parameters ---------- Returns ------- string: File path of the dmap file the library currently uses. Examples -------- >>> import mtca4u >>> mtca4u.set_dmap_location("../my_example_dmap_file.dmap") >>> dmapPath = mtca4u.get_dmap_location() >>> print dmapPath # prints '../my_example_dmap_file.dmap' See Also -------- set_dmap_location : set dmap file path for the library. Device : Open device using specified alias names or using device id and mapfile """ return mtca4udeviceaccess.getDmapFile()
[docs]class Device: """ Construct Device from user provided device information This constructor is used to open a device listed in the dmap file. Parameters ---------- alias : str The device alias/name in the dmap file for the hardware Examples -------- Creating a device using dmap file: >>> import mtca4u >>> mtca4u.set_dmap_location("../my_example_dmap_file.dmap") >>> device = mtca4u.Device("my_card") # my_card is a alias in my_example_dmap_file.dmap """ def __init__(self, *args): # define a dictiWe do not wantonary to hold multiplexed data accessors # TODO: Limit the number of entries this dictionary can hold, # We do not want to hold on to too much ram self.__accsessor_dictionary = {} if len(args) == 2: deviceFile = args[0] mapFile = args[1] self.__printDeprecationWarning(deviceFile, mapFile) raise SyntaxError("Syntax error.") elif len(args) == 1: cardAlias = args[0] self.__openedDevice = mtca4udeviceaccess.createDevice(cardAlias) else: raise SyntaxError( "Syntax Error: please see help(mtca4u.Device) for usage instructions.")
[docs] def read(self, moduleName="", registerName=None, numberOfElementsToRead=0, elementIndexInRegister=0, registerPath=None): """ Reads out Fixed point converted values from the opened device This method uses the map file to return Fixed Point converted values from a register. It can read the whole register or an arbitary number of register elements. Data can also be read from an offset within the register (through the 'elementIndexInRegister' parameter). .. note:: Both moduleName and registerName parameters are ignored when registerPath is provided. Parameters ---------- moduleName : str, optional The name of the device module to which the register belongs to. If the register is not contained in a module, then provide an empty string as the parameter value. registerName : str, optional The name of the register to read from. numberOfElementsToRead : int, optional Specifies the number of register elements that should be read out. The width and fixed point representation of the register element are internally obtained from the map file. The method returns all elements in the register if this parameter is ommitted or when its value is set as 0. If the value provided as this parameter exceeds the register size, an array with all elements upto the last element is returned elementIndexInRegister : int, optional This is a zero indexed offset from the first element of the register. When an elementIndexInRegister parameter is specified, the method reads out elements starting from this element index. The elemnt at the index position is included in the read as well. registerPath : str, optional When provided, it takes precedences over moduleName and registerName for location lookup. Returns ------- readoutValues: numpy.array, dtype == numpy.float64 The return type for the method is a 1-Dimensional numpy array with datatype numpy.float64. The returned numpy.array would either contain all elements in the register or only the number specified by the numberOfElementsToRead parameter Examples -------- register "WORD_STATUS" is 1 element long.. >>> import mtca4u >>> mtca4u.set_dmap_location("../my_example_dmap_file.dmap") >>> boardWithModules = mtca4u.Device("device_name") >>> boardWithModules.read("BOARD", "WORD_STATUS") array([15.0], dtype=float64) >>> boardWithModules.read(registerPath="/BOARD/WORD_STATUS") array([15.0], dtype=float64) register "WORD_CLK_MUX" is 4 elements long. >>> import mtca4u >>> device = mtca4u.Device("device_name") >>> device.read("", "WORD_CLK_MUX") array([15.0, 14.0, 13.0, 12.0], dtype=float64) >>> device.read("", "WORD_CLK_MUX", 0) array([15.0, 14.0, 13.0, 12.0], dtype=float64) read out select number of elements from specified locations: >>> device.read("", "WORD_CLK_MUX", 1) array([15.0], dtype=float64) >>> device.read("", "WORD_CLK_MUX", 1, 2 ) array([13.0], dtype=float64) >>> device.read("", "WORD_CLK_MUX", 0, 2 ) array([13.0, 12.0], dtype=float64) >>> device.read("", "WORD_CLK_MUX", 5, 2 ) array([13.0, 12.0], dtype=float64) >>> device.read("", "WORD_CLK_MUX", numberOfElementsToRead=1, elementIndexInRegister=2 ) array([13.0], dtype=float64) >>> device.read("", "WORD_CLK_MUX", elementIndexInRegister=2 ) array([13.0, 12.0], dtype=float64) See Also -------- Device.read_raw : Read in 'raw' bit values from a device register """ if registerPath is None: registerPath = '/' + moduleName + '/' + registerName registerAccessor = self.__openedDevice.getOneDAccessor_double(registerPath, numberOfElementsToRead, elementIndexInRegister, []) registerSize = registerAccessor.getNElements() array = numpy.empty(registerSize, numpy.double) return registerAccessor.read(array)
[docs] def write(self, moduleName="", registerName=None, dataToWrite=None, elementIndexInRegister=0, registerPath=None): """ Sets data into a desired register This method writes values into a register on the board. The method internally uses a fixed point converter that is aware of the register width on the device and its fractional representation. This Fixed point converter converts the input into corresponding Fixed Point representaions that fit into the decive register. .. note:: Both moduleName and registerName parameters are ignored when registerPath is provided. Parameters ---------- moduleName : str, optional The name of the device module which has the register to write into. If module name is not applicable to the register, then provide an empty string as the parameter value. registerName : str, optional Mapped name of the register to write to dataToWrite : int, float, \ list of int/float, numpy.array(dtype numpy.float32/64), \ numpy.array(dtype = numpy.int32/64) The data to be written in to the register. it may be a numpy.float32/64 or a numpy.int32/64 array or a list with int or float values . Each value in this array represents an induvidual element of the register. dataToWrite may also take on int/float type when single vaues are passesed elementIndexInRegister : int, optional This is a zero indexed offset from the first element of the register. When an elementIndexInRegister parameter is specified, the method starts the write from this index registerPath : str, optional When provided, it takes precedences over moduleName and registerName for location lookup. Returns ------- Examples -------- register "WORD_STATUS" is 1 element long and belongs to module "BOARD". >>> import mtca4u >>> mtca4u.set_dmap_location("../my_example_dmap_file.dmap") >>> boardWithModules = mtca4u.Device("device_name") >>> boardWithModules.write("BOARD", "WORD_STATUS", 15) >>> boardWithModules.write("BOARD", "WORD_STATUS", 15.0) >>> boardWithModules.write("BOARD", "WORD_STATUS", [15]) >>> boardWithModules.write("BOARD", "WORD_STATUS", [15.0]) >>> boardWithModules.write(registerPath = "/BOARD/WORD_STATUS", dataToWrite = [15.0]) >>> dataToWrite = numpy.array([15.0]) >>> boardWithModules.write("BOARD", "WORD_STATUS", dataToWrite) register "WORD_CLK_MUX" is 4 elements long. >>> import mtca4u >>> mtca4u.set_dmap_location("../my_example_dmap_file.dmap") >>> device = mtca4u.Device("device_name") >>> dataToWrite = numpy.array([15.0, 14.0, 13.0, 12.0]) >>> device.write("", "WORD_CLK_MUX", dataToWrite) >>> dataToWrite = numpy.array([13, 12]) >>> device.write("", "WORD_CLK_MUX", dataToWrite, 2) >>> device.write("", "WORD_CLK_MUX", 2.78) # writes value to first element of register >>> device.write("", "WORD_CLK_MUX", 10, elementIndexInRegister=3) See Also -------- Device.write_raw : Write 'raw' bit values to a device register """ # get register accessor data = numpy.array(dataToWrite) numberOfElementsToWrite = data.size if numberOfElementsToWrite == 0: return if registerPath is None: registerPath = '/' + moduleName + '/' + registerName arguments = (registerPath, numberOfElementsToWrite, elementIndexInRegister, []) device = self.__openedDevice dtype = data.dtype if (dtype == numpy.int32): accessor = device.getOneDAccessor_int32(*arguments) elif (dtype == numpy.int64): accessor = device.getOneDAccessor_int64(*arguments) elif (dtype == numpy.float32): accessor = device.getOneDAccessor_float(*arguments) elif (dtype == numpy.float64): accessor = device.getOneDAccessor_double(*arguments) else: raise RuntimeError("Data format used is unsupported") accessor.write(data)
[docs] def read_raw(self, moduleName='', registerName=None, numberOfElementsToRead=0, elementIndexInRegister=0, registerPath=None): """ Returns 'raw values' (Without fixed point conversion applied) from a device's register This method returns the raw bit values contained in the queried register. The returned values are not Fixed Point converted, but direct binary values contained in the register elements. .. note:: Both moduleName and registerName parameters are ignored when registerPath is provided. Parameters ---------- moduleName : str, optional The name of the device module to which the register to read from belongs. If module name is not applicable to the register, then provide an empty string as the parameter value. registerName : str, optional The name of the device register to read from. numberOfElementsToRead : int, optional Specifies the number of register elements that should be read out. The method returns all elements in the register if this parameter is ommitted or when its value is set as 0. If the value provided as this parameter exceeds the register size, an array will all elements upto the last element is returned elementIndexInRegister : int, optional This is a zero indexed offset from the first element of the register. When an elementIndexInRegister parameter is specified, the method reads out elements starting from this element index. The element at the index position is included in the read as well. registerPath : str, optional When provided, it takes precedences over moduleName and registerName for location lookup. Returns ------- readInRawValues: numpy.array, dtype == numpy.int32 The method returns a numpy.int32 array containing the raw bit values of the register elements. The length of the array either equals the number of elements that make up the register or the number specified through the numberOfElementsToRead parameter Examples -------- register "WORD_STATUS" is 1 element long. >>> import mtca4u >>> mtca4u.set_dmap_location("../my_example_dmap_file.dmap") >>> boardWithModules = mtca4u.Device("device_name") >>> boardWithModules.read_raw("BOARD", "WORD_STATUS") array([15], dtype=int32) >>> boardWithModules.read_raw(registerPath="/BOARD/WORD_STATUS") array([15], dtype=int32) register "WORD_CLK_MUX" is 4 elements long. >>> import mtca4u >>> mtca4u.set_dmap_location("../my_example_dmap_file.dmap") >>> device = mtca4u.Device("device_name") >>> device.read_raw("", "WORD_CLK_MUX") array([15, 14, 13, 12], dtype=int32) >>> device.read_raw("", "WORD_CLK_MUX", 0) array([15, 14, 13, 12], dtype=int32) >>> device.read_raw("", "WORD_CLK_MUX", 1) array([15], dtype=int32) >>> device.read_raw("", "WORD_CLK_MUX", 1, 2 ) array([13], dtype = int32) >>> device.read_raw("", "WORD_CLK_MUX", 0, 2 ) array([13, 12], dtype=int32) >>> device.read_raw("", "WORD_CLK_MUX", numberOfElementsToRead=1, elementIndexInRegister=2 ) array([13], dtype=int32) >>> device.read_raw("", "WORD_CLK_MUX", elementIndexInRegister=2 ) array([13, 12], dtype=int32) See Also -------- Device.read : Read in Fixed Point converted bit values from a device register """ if registerPath is None: registerPath = '/' + moduleName + '/' + registerName # legacy implemention is done with int32 registerAccessor = self.__openedDevice.getOneDAccessor_int32( registerPath, numberOfElementsToRead, elementIndexInRegister, [ mtca4udeviceaccess.AccessMode.raw]) registerSize = registerAccessor.getNElements() array = numpy.empty(registerSize, numpy.int32) return registerAccessor.read(array)
[docs] def write_raw(self, moduleName='', registerName=None, dataToWrite=None, elementIndexInRegister=0, registerPath=None): """ Write raw bit values (no fixed point conversion applied) into the register Provides a way to put in a desired bit value into individual register elements. .. note:: Both moduleName and registerName parameters are ignored when registerPath is provided. Parameters ---------- moduleName : str, optional The name of the device module that has the register we intend to write to. If module name is not applicable to the register, then provide an empty string as the parameter value. registerName : str, optional The name of the desired register to write into. dataToWrite : numpy.array, dtype == numpy.int32 The array holding the bit values to be written into the register. The numpy array is expected to contain numpy.int32 values elementIndexInRegister : int, optional This is a zero indexed offset from the first element of the register. When an elementIndexInRegister parameter is specified, the method starts the write from this index registerPath : str, optional When provided, it takes precedences over moduleName and registerName for location lookup. Returns ------- Examples -------- register "WORD_STATUS" is 1 element long and is part of the module "BOARD". >>> import mtca4u >>> mtca4u.set_dmap_location("../my_example_dmap_file.dmap") >>> boardWithModules = mtca4u.Device("device_name") >>> _dataToWrite = numpy.array([15], dtype=int32) >>> boardWithModules.write_raw("BOARD", "WORD_STATUS", dataToWrite=_dataToWrite) >>> boardWithModules.write_raw(registerPath = "/BOARD/WORD_STATUS", dataToWrite=_dataToWrite) register "WORD_CLK_MUX" is 4 elements long. >>> import mtca4u >>> mtca4u.set_dmap_location("../my_example_dmap_file.dmap") >>> device = mtca4u.Device("device_name") >>> dataToWrite = numpy.array([15, 14, 13, 12], dtype=int32) >>> device.write_raw("", "WORD_CLK_MUX", dataToWrite) >>> dataToWrite = numpy.array([13, 12], dtype=int32) >>> device.write_raw("MODULE1", "WORD_CLK_MUX", dataToWrite, 2) See Also -------- Device.write : Write values that get fixed point converted to the device """ self.__checkAndExitIfArrayNotInt32(dataToWrite) numberOfElementsToWrite = dataToWrite.size if numberOfElementsToWrite == 0: return if registerPath is None: registerPath = '/' + moduleName + '/' + registerName # old binding: # accessor = self.__openedDevice.getRaw1DAccessor(registerPath, numberOfElementsToWrite, elementIndexInRegister) accessor = self.__openedDevice.getOneDAccessor_int32( registerPath, numberOfElementsToWrite, elementIndexInRegister, [mtca4udeviceaccess.AccessMode.raw]) accessor.write(dataToWrite)
[docs] def read_dma_raw(self, moduleName='', DMARegisterName=None, numberOfElementsToRead=0, elementIndexInRegister=0, registerPath=None): """ Read in Data from the DMA region of the card This method can be used to fetch data copied to a dma memory block. The method assumes that the device maps the DMA memory block to a register made up of 32 bit elements. .. note:: Deprecated since 1.0.0; use Device.read_raw instead. Parameters ---------- moduleName : str, optional The name of the device module that has the register we intend to write to. If module name is not applicable to the device, then provide an empty string as the parameter value. DMARegisterName : str, optional The register name to which the DMA memory region is mapped numberOfElementsToRead : int, optional This optional parameter specifies the number of 32 bit elements that have to be returned from the mapped dma register. When this parameter is not specified or is provided with a value of 0, every element in the DMA memory block is returned. If the value provided as this parameter exceeds the register size, an array with all elements upto the last element is returned elementIndexInRegister : int, optional This parameter specifies the index from which the read should commence. registerPath : str, optional When provided, it takes precedences over moduleName and registerName for location lookup. Returns ------- arrayOfRawValues: numpy.array, dtype == numpy.int32 The method returns a numpy.int32 array containing the raw bit values contained in the DMA register elements. The length of the array either equals the number of 32 bit elements that make up the whole DMA region or the number specified through the numberOfElementsToRead parameter Examples -------- Use Device.read_raw: In the example, register "AREA_DMA_VIA_DMA" is the DMA mapped memory made up of 32 bit elements. >>> import mtca4u >>> mtca4u.set_dmap_location("../my_example_dmap_file.dmap") >>> device = mtca4u.Device("device_name") >>> device.read__raw("", "AREA_DMA_VIA_DMA", 10) array([0, 1, 4, 9, 16, 25, 36, 49, 64, 81], dtype=int32) See Also -------- Device.read_raw : Use this method for the same purpose instead. """ return self.read_raw(moduleName, DMARegisterName, numberOfElementsToRead, elementIndexInRegister, registerPath)
[docs] def read_sequences(self, moduleName='', regionName=None, registerPath=None): """ Read in all sequences from a Multiplexed data Region This method returns the demultiplexed sequences in the memory area specified by regionName. The data is returned as a 2D numpy array with the coulums representing induvidual sequences .. note:: Both moduleName and regionName parameters are ignored when registerPath is provided. Parameters ---------- moduleName : str, optional The name of the device module that has the register we intend to write to. If module name is not applicable to the device, then provide an empty string as the parameter value. regionName : str, optional The name of the memory area containing the multiplexed data. registerPath : str, optional When provided, it takes precedences over moduleName and regionName for location lookup. Returns ------- 2DarrayOfValues: numpy.array, dtype == numpy.double The method returns a 2D numpy.double array containing extracted induvidual sequences as the columns Examples -------- "DMA" is the Multiplexed data region name. This region is defined by 'AREA_MULTIPLEXED_SEQUENCE_DMA' in the mapfile. >>> import mtca4u >>> mtca4u.set_dmap_location("../my_example_dmap_file.dmap") >>> device = mtca4u.Device("device_name") >>> device.read_sequences("BOARD.0", "DMA") array([[ 0., 1., 4., 9., 16.], [ 25., 36., 49., 64., 81.], [ 100., 121., 144., 169., 196.], [ 225., 256., 289., 324., 361.] [ 400., 441., 484., 529., 576.]], dtype=double) >>> device.read_sequences(registerPath= '/BOARD.0/DMA') array([[ 0., 1., 4., 9., 16.], [ 25., 36., 49., 64., 81.], [ 100., 121., 144., 169., 196.], [ 225., 256., 289., 324., 361.] [ 400., 441., 484., 529., 576.]], dtype=double) Each column of the 2D matrix represents an extracted sequence: >>> data = device.read_sequences("BOARD.0", "DMA") >>> adc0_values = data[:,0] # array([0., 25., 100., 225., 400.]) >>> adc1_values = data[:,1] # array([1., 36., 49., 64., 81.]) >>> adc3_values = data[:,3] # array([9., 64., 169., 324., 529.]) """ if registerPath is None: registerPath = '/' + moduleName + '/' + regionName # accessor = self.__openedDevice.get2DAccessor(registerPath) # old accessor = self.__openedDevice.getTwoDAccessor_double( registerPath, 0, 0, []) # readFromDevice fetches data from the card to its intenal buffer of the # c++ accessor numberOfSequences = accessor.getNChannels() numberOfBlocks = accessor.getNElementsPerChannel() array2D = self.__create2DArray(numpy.double, numberOfSequences, numberOfBlocks) return numpy.transpose(accessor.read(array2D))
[docs] def getCatalogueMetadata(self, parameterName): """ Reads out metadata form the device catalogue The available metadata depends on the use backend. E.g. for NumericAddressedBackends, metadata will come from the map file. Parameters ---------- parameterName : str Name of the metadata parameter to read. Returns ------- metadataValue: str The value corresponding to the given parameterName. """ return self.__openedDevice.getCatalogueMetadata(parameterName)
# Helper methods below def __exitIfSuppliedIndexIncorrect(self, registerAccessor, elementIndexInRegister): registerSize = registerAccessor.getNumElements() if elementIndexInRegister >= registerSize: if registerSize == 1: # did this for displaying specific error string without the range when # there is only one element in the register errorString = "Element index: {0} incorrect. Valid index is {1}"\ .format(elementIndexInRegister, registerSize - 1) else: errorString = "Element index: {0} incorrect. Valid index range is [0-{1}]"\ .format(elementIndexInRegister, registerSize - 1) raise ValueError(errorString) def __checkAndExitIfArrayNotFloat32(self, dataToWrite): self.__raiseExceptionIfNumpyArraydTypeIncorrect( dataToWrite, numpy.float32) def __checkAndExitIfArrayNotInt32(self, dataToWrite): self.__raiseExceptionIfNumpyArraydTypeIncorrect( dataToWrite, numpy.int32) def __raiseExceptionIfNumpyArraydTypeIncorrect(self, numpyArray, dType): if ((not isinstance(numpyArray, numpy.ndarray)) or (numpyArray.dtype != dType)): raise TypeError("Method expects values in a {0} " " numpy.array".format(dType)) def __getCorrectedElementCount(self, elementCountInRegister, numberOfelements, elementOffset): elementCountInRegister # = registerAccessor.getNumElements() maxFetchableElements = elementCountInRegister - elementOffset correctedElementCount = numberOfelements if ( numberOfelements != 0 and numberOfelements <= maxFetchableElements) else maxFetchableElements return correctedElementCount def __createArray(self, dType, numberOfElementsInRegister, numberOfElementsToRead, elementIndexInRegister): size = self.__getCorrectedElementCount(numberOfElementsInRegister, numberOfElementsToRead, elementIndexInRegister) array = numpy.empty(size, dtype=dType) return array def __create2DArray(self, dType, numberOfRows, numberOfColumns): array2D = numpy.empty((numberOfRows, numberOfColumns), dtype=dType) return array2D def __printDeprecationWarning(self, deviceFile, mapFile): deviceFileName = self.__extractNameFromDeviceFile(deviceFile) print("*************************************************************************************************") print(" >>> mtca4u.Device('" + deviceFile + "', '" + mapFile + "')") print(" ERROR: The above usage for device creation has been phased out!") print( " ") print( " Please consider using a dmap file for device creation. ") print( " Instructions: ") print( " - Create dmap file: <your_dmapfile_name_goes_here>.dmap ") print( " ") print( " - Add this line to your dmap file: ") print(" <your_card_alias_goes_here> sdm://./pci:" + deviceFileName + "; " + mapFile) print( " ") print( " - Tell the library about the dmap file ") print( " >>> mtca4u.set_dmap_location('your_dmapfile_name.dmap') ") print( " ") print( " - Create your device ") print( " >>> device = mtca4u.Device('your_card_alias') ") print( " ") print( " Alternatively, you can pass the sdm string directly in place of the alias ") print( " name, if it includes the map file name, e.g.: ") print(" sdm://./pci:" + deviceFileName + "=" + mapFile) print("*************************************************************************************************") def __extractNameFromDeviceFile(self, deviceFile): index = (deviceFile.rfind('/')) + 1 return deviceFile[index:]