spcm

A high-level, general purpose, object-oriented Python package to control Spectrum Instrumentation GmbH devices.

spcm can connect to digitizers, AWGs, StarHubs and Netboxes. The package contains classes for controlling specific cards and synchronization devices (StarHub) as well as for specific functionality, such as DDS and TimeStamps.

Classes

Hardware classes

Hardware classes are the interfaces to the actual devices. These hardware classes support context management, hence the opening of specific devices is handled as a file open, using the Python with-statement.

Name Parent Description
Device (none) general base class for connecting to Spectrum Instrumentation GmbH devices and an interface for other classes.
Card Device a class to control the low-level API interface of Spectrum Instrumentation cards.
Sync Device a class for controling StarHub devices.
CardStack ExitStack a class that handles the opening and closing of a combination of different cards either with or without a StarHub that synchronizes the cards.
Netbox CardStack a class that handles the opening and closing of a group of cards combined in a Netbox.

Diagram

classDiagram class Device class Card class Sync class `contextlib.ExitStack` class CardStack class Netbox Device <|-- Card Device <|-- Sync `contextlib.ExitStack` <|-- CardStack CardStack <|-- Netbox

Functionality classes

Functionality classes handle specific functionality that is available to the card, as well as specific add-on options. A functionality class is provided with a hardware class object to handle functionality on that card. Some classes, in addition, also support groups of cards and can be provided with objects of type CardStack.

Name Parent Description
CardFunctionality (none) interface class for additional card functionality
Channels (none) class for controlling the channels of a card or a stack of cards
Channel (none) class for controlling a single channel
Clock CardFunctionality class for setting up the clock engine of the card
Trigger CardFunctionality class for setting up the trigger engine of the card
MultiPurposeIOs CardFunctionality class for setting up the multi purpose i/o's of the card
MultiPurposeIO (none) class for handling a single multi purpose i/o line a list of these objects resides inside MultiPurposeIOs
DataTransfer CardFunctionality special class for handling data transfer functionality
Multi DataTransfer special class for handling multiple recording and replay mode functionality
Sequence DataTransfer special class for handling sequence mode functionality
TimeStamp DataTransfer special class for handling time stamped data
SCAPPTransfer DataTransfer special class for handling direct card to GPU class using the SCAPP option
Boxcar Multi special class for handling boxcar averaging
BlockAverage Multi special class for handling block averaging functionality
PulseGenerators CardFunctionality class for handling the pulse generator functionality
PulseGenerator (none) class for handling a single pulse generator a list of these objects resides inside PulseGenerators
DDS CardFunctionality class for handling DDS functionality
DDSCore (none) class for handling a DDS core, a list of these objects resides inside a DDS object
DDSCommandList DDS class for handling streaming DDS commands in blocks
DDSCommandQueue DDS class for handling streaming DDS commands in queues, where commands are added to the queue and automatically written to the card

Diagram

classDiagram class CardFunctionality <<interface>> CardFunctionality class Channels class Clock class Trigger class MultiPurposeIOs class DataTransfer class DDS class DDSCore class DDSCommandList class DDSCommandQueue class PulseGenerators class Multi class TimeStamp class Sequence class SCAPPTransfer class Boxcar class BlockAverage CardFunctionality <|-- Clock CardFunctionality <|-- Trigger CardFunctionality <|-- MultiPurposeIOs CardFunctionality <|-- DataTransfer CardFunctionality <|-- DDS CardFunctionality <|-- PulseGenerators DataTransfer <|-- Multi DataTransfer <|-- TimeStamp DataTransfer <|-- Sequence DataTransfer <|-- SCAPPTransfer Multi <|-- Boxcar Multi <|-- BlockAverage Channels *-- Channel MultiPurposeIOs *-- MultiPurposeIO PulseGenerators *-- PulseGenerator DDS *-- DDSCore DDS <|-- DDSCommandList DDSCommandList <|-- DDSCommandQueue

Exception classes

When an error in the driver occures, the user is notified with an exception that contains an error object. Timeouts are also handled through exceptions and have their own class.

Name Parent Description
SpcmException (none) the main class to control exceptions that are raised due to errors that are raised by the low-level driver.
SpcmTimeout SpcmException when an timeout of the device occurs a special exception is raised of type SpcmTimeout

Diagram

classDiagram class SpcmTimeout class SpcmException SpcmException <|-- SpcmTimeout

Error classes

Errors coming from the driver API, are stored in an error object and then raised as an exception. The error object contains all the information coming from the driver.

Name Parent Description
SpcmError (none) all the errors that are raised by the low-level driver are packed into objects of this class and handled through exceptions

Diagram

classDiagram class SpcmError

Notes

  • See the files regs.py and spcerr.py for an extensive list of all the register names and errors that are handled by the driver.
  • For more information, please have a look at our hardware specific user manuals.

See also

Supported devices

The following product families are supported:

 1"""
 2.. include:: ./README.md
 3
 4.. include:: ./SUPPORTED_DEVICES.md
 5"""
 6
 7__docformat__ = "numpy"
 8import numpy as np
 9
10# Units available
11from pint import UnitRegistry
12try:
13    import matplotlib.pyplot as plt
14    mpl = True
15except ImportError:
16    mpl = False
17units = UnitRegistry(autoconvert_offset_to_baseunit=True)
18units.define("sample = 1 = Sa = Sample = Samples = S")
19units.define("promille = 0.001 = ‰ = permille = perthousand = perthousands = ppt")
20units.define("fraction = 1 = frac = Frac = Fracs = Fraction = Fractions = Frac = Fracs")
21units.highZ = np.inf * units.ohm
22units.formatter.default_format = "~P" # see https://pint.readthedocs.io/en/stable/user/formatting.html
23if mpl:
24    units.setup_matplotlib(mpl)
25    units.mpl_formatter = "{:~P}" # see https://pint.readthedocs.io/en/stable/user/plotting.html
26__all__ = ["units"]
27
28# Import all registery entries and spectrum card errors into the module's name space
29from spcm_core import *
30
31# Import all the public classes into the module's namespace
32from .classes_device import Device
33from .classes_card import Card
34from .classes_error_exception import SpcmError, SpcmException, SpcmTimeout
35from .classes_sync import Sync
36from .classes_card_stack import CardStack
37from .classes_netbox import Netbox
38# Functionality
39from .classes_functionality import CardFunctionality
40from .classes_channels import Channels, Channel
41from .classes_clock import Clock
42from .classes_trigger import Trigger
43from .classes_multi_purpose_ios import MultiPurposeIO, MultiPurposeIOs
44from .classes_data_transfer import DataTransfer
45from .classes_multi import Multi
46from .classes_time_stamp import TimeStamp
47from .classes_sequence import Sequence
48from .classes_dds import DDS, DDSCore
49from .classes_dds_command_list import DDSCommandList
50from .classes_dds_command_queue import DDSCommandQueue
51from .classes_pulse_generators import PulseGenerator, PulseGenerators
52from .classes_block_average import BlockAverage
53from .classes_boxcar import Boxcar
54from .classes_scapp import SCAPPTransfer
55
56__all__ = [*__all__,
57    "Device", "Card", "Sync", "CardStack", "Netbox", "CardFunctionality", "Channels", "Channel", "Clock", "Trigger", "MultiPurposeIOs", "MultiPurposeIO",
58    "DataTransfer", "DDS", "DDSCore", "DDSCommandList", "DDSCommandQueue", "PulseGenerator", "PulseGenerators", "Multi", "TimeStamp", "Sequence", 
59    "BlockAverage", "Boxcar", "SpcmException", "SpcmTimeout", "SpcmError", "SCAPPTransfer"
60]
61
62# Versioning support using versioneer
63from . import _version
64__version__ = _version.get_versions()['version']
65
66# Writing spcm package version to log file
67try:
68    driver_version = int64(0)
69    spcm_dwGetParam_i64(None, SPC_GETDRVVERSION, byref(driver_version))
70    version_hex = driver_version.value
71    major = (version_hex & 0xFF000000) >> 24
72    minor = (version_hex & 0x00FF0000) >> 16
73    build = version_hex & 0x0000FFFF
74    # Available starting from build 21797
75    if build < 21797:
76        version_str = "v{}.{}.{}".format(major, minor, build)
77        raise OSError(f"Driver version build {version_str} does not support writing spcm version to log")
78    from importlib.metadata import version
79    version_tag = version('spcm')
80    version_str = bytes("Python package spcm v{}".format(version_tag), "utf-8")
81    version_ptr = create_string_buffer(version_str)
82    dwErr = spcm_dwSetParam_ptr(None, SPC_WRITE_TO_LOG, version_ptr, len(version_str))
83except OSError as e:
84    print(e)
units = <pint.registry.UnitRegistry object>
class Device:
 21class Device():
 22    """
 23    a class to control the low-level API interface of Spectrum Instrumentation devices
 24
 25    For more information about what setups are available, please have a look at the user manual
 26    for your specific device.
 27
 28    Parameters
 29    ----------
 30    device_identifier
 31        the identifying string that defines the used device
 32
 33    Raises
 34    ----------
 35    SpcmException
 36    SpcmTimeout
 37    """
 38
 39    # public
 40    device_identifier : str = ""
 41
 42    # private
 43    _kwargs : Dict[str, Union[int, float, str]] = {}
 44    _last_error = None
 45    _handle = None
 46    """the handle object used for the card connection"""
 47
 48    _str_len = 256
 49    _reraise : bool = False
 50    _throw_error : bool = True
 51    _verbose : bool = False
 52    _closed : bool = False
 53    """the indicator that indicated whether a connection is opened or closed is set to open (False)"""
 54
 55
 56    def __init__(self, device_identifier : str = "", handle = False, **kwargs) -> None:
 57        """Puts the device_identifier in the class parameter self.device_parameter
 58
 59        Parameters
 60        ----------
 61        device_identifier : str = ""
 62            an identifier string to connect to a specific device, for example:
 63
 64            * Local PCIe device '/dev/spcm0'
 65            * Remote 'TCPIP::192.168.1.10::inst0::INSTR'
 66        handle = False
 67            directly supply the object with an existing handle
 68        """
 69        self.device_identifier = device_identifier
 70        self._handle = handle
 71        self._kwargs = kwargs
 72        self._throw_error = kwargs.get("throw_error", True)
 73        self._verbose = kwargs.get("verbose", False)
 74    
 75    def __del__(self) -> None:
 76        """Destructor that closes the connection associated with the handle"""
 77        if not self._closed:
 78            self.stop()
 79            self._closed = True
 80            self.close(self._handle)
 81
 82    def __enter__(self) -> object:
 83        """
 84        Constructs a handle using the parameter `device_identifier`, when using the with statement
 85        
 86        Returns
 87        -------
 88        object
 89            The active card handle
 90        
 91        Raises
 92        ------
 93        SpcmException
 94        """
 95
 96        if self.device_identifier and not self._handle:
 97            self.open(self.device_identifier)
 98            if not self._handle and self._throw_error:
 99                error = SpcmError(text="{} not found...".format(self.device_identifier))
100                raise SpcmException(error)
101        return self
102    
103    def __exit__(self, exception : SpcmException = None, error_value : str = None, trace : types.TracebackType = None) -> None:
104        """
105        Handles the exiting of the with statement, when either no code is left or an exception is thrown before
106
107        Parameters
108        ----------
109        exception : SpcmException
110            Only this parameter is used and printed 
111        error_value : str
112        trace : types.TracebackType
113
114        Raises
115        ------
116        SpcmException
117        """
118        if self._verbose and exception:
119            self._print("Error type: {}".format(exception))
120            self._print("Error value: {}".format(error_value))
121            self._print("Traceback:")
122            traceback.print_tb(trace)
123        elif exception:
124            self._print("Error: {}".format(error_value))
125        self.stop(M2CMD_DATA_STOPDMA) # stop the card and the DMA transfer
126        self._closed = True
127        self.close(self._handle)
128        self._handle = None
129        if exception and self._reraise:
130            raise exception
131    
132    def handle(self) -> object:
133        """
134        Returns the handle used by the object to connect to the active card
135    
136        Class Parameters
137        ----------
138        self._handle
139    
140        Returns
141        -------
142        drv_handle
143            The active card handle
144        """
145        
146        return self._handle
147
148    # Check if a card was found
149    def __bool__(self) -> bool:
150        """
151        Check for a connection to the active card
152    
153        Class Parameters
154        ----------
155        self._handle
156    
157        Returns
158        -------
159        bool
160            True for an active connection and false otherwise
161            
162        Examples
163        -----------
164        >>> card = spcm.Card('/dev/spcm0')
165        >>> print(bool(card))
166        <<< True # if a card was found at '/dev/spcm0'
167        """
168        
169        return bool(self._handle)
170    
171    # High-level parameter functions, that use the low-level get and set function    
172    def drv_type(self) -> int:
173        """
174        Get the driver type of the currently used driver (see register `SPC_GETDRVTYPE` in the manual)
175    
176        Returns
177        -------
178        int
179            The driver type of the currently used driver
180        """
181
182        return self.get_i(SPC_GETDRVTYPE)
183
184    def drv_version(self) -> dict:
185        """
186        Get the version of the currently used driver. (see register `SPC_GETDRVVERSION` in the manual)
187    
188        Returns
189        -------
190        dict
191            version of the currently used driver
192              * "major" - the major version number,
193              * "minor" - the minor version number,
194              * "build" - the actual build
195        """
196        version_hex = self.get_i(SPC_GETDRVVERSION)
197        major = (version_hex & 0xFF000000) >> 24
198        minor = (version_hex & 0x00FF0000) >> 16
199        build = version_hex & 0x0000FFFF
200        version_dict = {"major": major, "minor": minor, "build": build}
201        return version_dict
202    
203    def kernel_version(self) -> dict:
204        """
205        Get the version of the currently used kernel. (see register `SPC_GETKERNELVERSION` in the manual)
206    
207        Returns
208        -------
209        dict 
210            version of the currently used driver
211              * "major" - the major version number,
212              * "minor" - the minor version number,
213              * "build" - the actual build
214        """
215        version_hex = self.get_i(SPC_GETKERNELVERSION)
216        major = (version_hex & 0xFF000000) >> 24
217        minor = (version_hex & 0x00FF0000) >> 16
218        build = version_hex & 0x0000FFFF
219        version_dict = {"major": major, "minor": minor, "build": build}
220        return version_dict
221    
222    def custom_modifications(self) -> dict:
223        """
224        Get the custom modifications of the currently used device. (see register `SPCM_CUSTOMMOD` in the manual)
225    
226        Returns
227        -------
228        dict
229            The custom modifications of the currently used device
230              * "starhub" - custom modifications to the starhub,
231              * "module" -  custom modification of the front-end module(s)
232              * "base" - custom modification of the base card
233        """
234
235        custom_mode = self.get_i(SPCM_CUSTOMMOD)
236        starhub = (custom_mode & SPCM_CUSTOMMOD_STARHUB_MASK) >> 16
237        module = (custom_mode & SPCM_CUSTOMMOD_MODULE_MASK) >> 8
238        base = custom_mode & SPCM_CUSTOMMOD_BASE_MASK
239        custom_dict = {"starhub": starhub, "module": module, "base": base}
240        return custom_dict
241    
242    def log_level(self, log_level : int = None) -> int:
243        """
244        Set the logging level of the driver
245    
246        Parameters
247        ----------
248        log_level : int
249            The logging level that is set for the driver
250        
251        Returns
252        -------
253        int
254            The logging level of the driver
255        """
256
257        if log_level is not None:
258            self.set_i(SPC_LOGDLLCALLS, log_level)
259        return self.get_i(SPC_LOGDLLCALLS)
260
261    def cmd(self, *args) -> None:
262        """
263        Execute spcm commands (see register `SPC_M2CMD` in the manual)
264    
265        Parameters
266        ----------
267        *args : int
268            The different command flags to be executed.
269        """
270
271        cmd = 0
272        for arg in args:
273            cmd |= arg
274        self.set_i(SPC_M2CMD, cmd)
275    
276    #@Decorators.unitize(units.ms, "timeout", int)
277    def timeout(self, timeout : int = None, return_unit = None) -> int:
278        """
279        Sets the timeout in ms (see register `SPC_TIMEOUT` in the manual)
280        
281        Parameters
282        ----------
283        timeout : int
284            The timeout in ms
285        
286        Returns
287        -------
288        int
289            returns the current timeout in ms
290        """
291
292        if timeout is not None:
293            timeout = UnitConversion.convert(timeout, units.ms, int)
294            self.set_i(SPC_TIMEOUT, timeout)
295        return_value = self.get_i(SPC_TIMEOUT)
296        if return_unit is not None: return_value = UnitConversion.to_unit(return_value, return_unit)
297        return return_value
298    
299    def start(self, *args) -> None:
300        """
301        Starts the connected card and enables triggering on the card (see command `M2CMD_CARD_START` in the manual)
302
303        Parameters
304        ----------
305        *args : int
306            flags that are send together with the start command
307        """
308
309        self.cmd(M2CMD_CARD_START, *args)
310    
311    def stop(self, *args : int) -> None:
312        """
313        Stops the connected card (see command `M2CMD_CARD_STOP` in the manual)
314
315        Parameters
316        ----------
317        *args : int
318            flags that are send together with the stop command (e.g. M2CMD_DATA_STOPDMA)
319        """
320
321        self.cmd(M2CMD_CARD_STOP, *args)
322    
323    def reset(self) -> None:
324        """
325        Resets the connected device (see command `M2CMD_CARD_RESET` in the manual)
326        """
327
328        self.cmd(M2CMD_CARD_RESET)
329
330    def write_setup(self, *args) -> None:
331        """
332        Writes of the configuration registers previously changed to the device (see command `M2CMD_CARD_WRITESETUP` in the manual)
333
334        Parameters
335        ----------
336        *args : int
337            flags that are set with the write command
338        """
339
340        self.cmd(M2CMD_CARD_WRITESETUP, *args)
341    
342    def register_list(self, register_list : List[dict[str, Union[int, float]]]) -> None:
343        """
344        Writes a list with dictionaries, where each dictionary corresponds to a command (see the user manual of your device for all the available registers)
345    
346        Parameters
347        ----------
348        register_list : List[dict[str, Union[int, float]]]
349            The list of commands that needs to written to the specific registers of the card.
350        """
351
352        c_astParams = (ST_LIST_PARAM * 1024)()
353        astParams = ctypes.cast(c_astParams, ctypes.POINTER(ST_LIST_PARAM))
354        for i, register in enumerate(register_list):
355            astParams[i].lReg = register["lReg"]
356            astParams[i].lType = register["lType"]
357            if register["lType"] == TYPE_INT64:
358                astParams[i].Value.llValue = register["llValue"]
359            elif register["lType"] == TYPE_DOUBLE:
360                astParams[i].Value.dValue = register["dValue"]
361        self.set_ptr(SPC_REGISTER_LIST, astParams, len(register_list) * ctypes.sizeof(ST_LIST_PARAM))
362
363    # Low-level get and set functions
364    def get_i(self, register : int) -> int:
365        """
366        Get the integer value of a specific register of the card (see the user manual of your device for all the available registers)
367    
368        Parameters
369        ----------
370        register : int
371            The specific register that will be read from.
372        
373        Returns
374        -------
375        int
376            The value as stored in the specific register
377        """
378
379        self._check_closed()
380        return_value = int64(0)
381        dwErr = spcm_dwGetParam_i64(self._handle, register, byref(return_value))
382        self._check_error(dwErr)
383        return return_value.value
384    get = get_i
385    """Alias of get_i"""
386    
387    def get_d(self, register : int) -> float:
388        """
389        Get the float value of a specific register of the card (see the user manual of your device for all the available registers)
390    
391        Parameters
392        ----------
393        register : int
394            The specific register that will be read from.
395        
396        Returns
397        -------
398        float
399            The value as stored in the specific register
400        """
401
402        self._check_closed()
403        return_value = c_double(0)
404        self._check_error(spcm_dwGetParam_d64(self._handle, register, byref(return_value)))
405        return return_value.value
406    
407    def get_str(self, register : int) -> str:
408        """
409        Get the string value of a specific register of the card (see the user manual of your device for all the available registers)
410    
411        Parameters
412        ----------
413        register : int
414            The specific register that will be read from.
415        
416        Returns
417        -------
418        str
419            The value as stored in the specific register
420        """
421
422        self._check_closed()
423        return_value = create_string_buffer(self._str_len)
424        self._check_error(spcm_dwGetParam_ptr(self._handle, register, byref(return_value), self._str_len))
425        return return_value.value.decode('utf-8')
426    
427    def set_i(self, register : int, value : int) -> None:
428        """
429        Write the value of a specific register to the card (see the user manual of your device for all the available registers)
430    
431        Parameters
432        ----------
433        register : int
434            The specific register that will be written.
435        value : int
436            The value that is written to the card.
437        """
438
439        self._check_closed()
440        self._check_error(spcm_dwSetParam_i64(self._handle, register, value))
441    
442    def set_d(self, register : int, value : float) -> None:
443        """
444        Write the value of a specific register to the card (see the user manual of your device for all the available registers)
445    
446        Parameters
447        ----------
448        register : int
449            The specific register that will be written.
450        value : float
451            The value that is written to the card.
452        """
453
454        self._check_closed()
455        self._check_error(spcm_dwSetParam_d64(self._handle, register, value))
456    
457    def set_ptr(self, register : int, reference : c_void_p, size : int) -> None:
458        """
459        Use a memory segment to write to a specific register of the card (see the user manual of your device for all the available registers)
460    
461        Parameters
462        ----------
463        register : int
464            The specific register that will be read from.
465        reference : c_void_p
466            pointer to the memory segment
467        size : int
468            size of the memory segment
469        
470        Returns
471        -------
472        int
473            The value as stored in the specific register
474        """
475
476        self._check_closed()
477        self._check_error(spcm_dwSetParam_ptr(self._handle, register, reference, size))
478
479    # Error handling and exception raising
480    def _check_error(self, dwErr : int):
481        """
482        Create an SpcmError object and check for the last error (see the appendix in the user manual of your device for all the possible error codes)
483    
484        Parameters
485        ----------
486        dwErr : int
487            The error value as returned from a direct driver call
488        
489        Raises
490        ------
491        SpcmException
492        SpcmTimeout
493        """
494
495        # pass
496        if dwErr not in [ERR_OK, ERR_TIMEOUT] and self._throw_error:
497            self.get_error_info()
498            raise SpcmException(self._last_error)
499        elif dwErr == ERR_TIMEOUT:
500            raise SpcmTimeout("A card timeout occured")
501
502    def get_error_info(self) -> SpcmError:
503        """
504        Create an SpcmError object and store it in an object parameter
505    
506        Returns
507        ----------
508        SpcmError
509            the Error object containing the last error
510        """
511
512        self._last_error = SpcmError(self._handle)
513        return self._last_error
514    
515    def _check_closed(self) -> None:
516        """
517        Check if a connection to the card exists and if not throw an error
518
519        Raises
520        ------
521        SpcmException
522        """
523        if self._closed:
524            error_text = "The connection to the card has been closed. Please reopen the connection before sending commands."
525            if self._throw_error:
526                raise SpcmException(text=error_text)
527            else:
528                self._print(error_text)
529    
530    def _print(self, text : str, verbose : bool = False, **kwargs) -> None:
531        """
532        Print information
533
534        Parameters
535        ----------
536        text : str
537            The text that is printed
538        verbose : bool
539            A boolean that indicates if the text should forced to be printed
540        **kwargs
541            Additional parameters that are passed to the print function
542    
543        """
544
545        if self._verbose or verbose:
546            print(text, **kwargs)
547
548    def open(self, device_identifier : str) -> None:
549        """
550        Open a connection to the card and return the handle (see the user manual of your specific device on how to find out the device_identifier string)
551    
552        Parameters
553        ----------
554        device_identifier : str
555            The card identifier string (e.g. '/dev/spcm0' for a local device or 'TCPIP::192.168.1.10::inst0::INSTR' for a remote device)
556        """
557
558        self._handle = spcm_hOpen(create_string_buffer(bytes(device_identifier, 'utf-8')))
559        self._closed = False
560    
561    @staticmethod
562    def close(handle) -> None:
563        """
564        Close a connection to the card using a handle
565    
566        Parameters
567        ----------
568        handle
569            the handle object used for the card connection that is closed
570        """
571
572        spcm_vClose(handle)

a class to control the low-level API interface of Spectrum Instrumentation devices

For more information about what setups are available, please have a look at the user manual for your specific device.

Parameters
  • device_identifier: the identifying string that defines the used device
Raises
  • SpcmException
  • SpcmTimeout
Device(device_identifier: str = '', handle=False, **kwargs)
56    def __init__(self, device_identifier : str = "", handle = False, **kwargs) -> None:
57        """Puts the device_identifier in the class parameter self.device_parameter
58
59        Parameters
60        ----------
61        device_identifier : str = ""
62            an identifier string to connect to a specific device, for example:
63
64            * Local PCIe device '/dev/spcm0'
65            * Remote 'TCPIP::192.168.1.10::inst0::INSTR'
66        handle = False
67            directly supply the object with an existing handle
68        """
69        self.device_identifier = device_identifier
70        self._handle = handle
71        self._kwargs = kwargs
72        self._throw_error = kwargs.get("throw_error", True)
73        self._verbose = kwargs.get("verbose", False)

Puts the device_identifier in the class parameter self.device_parameter

Parameters
  • device_identifier (str = ""): an identifier string to connect to a specific device, for example:

    • Local PCIe device '/dev/spcm0'
    • Remote 'TCPIP::192.168.1.10::inst0::INSTR'
  • handle = False: directly supply the object with an existing handle
device_identifier: str = ''
def handle(self) -> object:
132    def handle(self) -> object:
133        """
134        Returns the handle used by the object to connect to the active card
135    
136        Class Parameters
137        ----------
138        self._handle
139    
140        Returns
141        -------
142        drv_handle
143            The active card handle
144        """
145        
146        return self._handle

Returns the handle used by the object to connect to the active card

Class Parameters

self._handle

Returns
  • drv_handle: The active card handle
def drv_type(self) -> int:
172    def drv_type(self) -> int:
173        """
174        Get the driver type of the currently used driver (see register `SPC_GETDRVTYPE` in the manual)
175    
176        Returns
177        -------
178        int
179            The driver type of the currently used driver
180        """
181
182        return self.get_i(SPC_GETDRVTYPE)

Get the driver type of the currently used driver (see register SPC_GETDRVTYPE in the manual)

Returns
  • int: The driver type of the currently used driver
def drv_version(self) -> dict:
184    def drv_version(self) -> dict:
185        """
186        Get the version of the currently used driver. (see register `SPC_GETDRVVERSION` in the manual)
187    
188        Returns
189        -------
190        dict
191            version of the currently used driver
192              * "major" - the major version number,
193              * "minor" - the minor version number,
194              * "build" - the actual build
195        """
196        version_hex = self.get_i(SPC_GETDRVVERSION)
197        major = (version_hex & 0xFF000000) >> 24
198        minor = (version_hex & 0x00FF0000) >> 16
199        build = version_hex & 0x0000FFFF
200        version_dict = {"major": major, "minor": minor, "build": build}
201        return version_dict

Get the version of the currently used driver. (see register SPC_GETDRVVERSION in the manual)

Returns
  • dict: version of the currently used driver
    • "major" - the major version number,
    • "minor" - the minor version number,
    • "build" - the actual build
def kernel_version(self) -> dict:
203    def kernel_version(self) -> dict:
204        """
205        Get the version of the currently used kernel. (see register `SPC_GETKERNELVERSION` in the manual)
206    
207        Returns
208        -------
209        dict 
210            version of the currently used driver
211              * "major" - the major version number,
212              * "minor" - the minor version number,
213              * "build" - the actual build
214        """
215        version_hex = self.get_i(SPC_GETKERNELVERSION)
216        major = (version_hex & 0xFF000000) >> 24
217        minor = (version_hex & 0x00FF0000) >> 16
218        build = version_hex & 0x0000FFFF
219        version_dict = {"major": major, "minor": minor, "build": build}
220        return version_dict

Get the version of the currently used kernel. (see register SPC_GETKERNELVERSION in the manual)

Returns
  • dict: version of the currently used driver
    • "major" - the major version number,
    • "minor" - the minor version number,
    • "build" - the actual build
def custom_modifications(self) -> dict:
222    def custom_modifications(self) -> dict:
223        """
224        Get the custom modifications of the currently used device. (see register `SPCM_CUSTOMMOD` in the manual)
225    
226        Returns
227        -------
228        dict
229            The custom modifications of the currently used device
230              * "starhub" - custom modifications to the starhub,
231              * "module" -  custom modification of the front-end module(s)
232              * "base" - custom modification of the base card
233        """
234
235        custom_mode = self.get_i(SPCM_CUSTOMMOD)
236        starhub = (custom_mode & SPCM_CUSTOMMOD_STARHUB_MASK) >> 16
237        module = (custom_mode & SPCM_CUSTOMMOD_MODULE_MASK) >> 8
238        base = custom_mode & SPCM_CUSTOMMOD_BASE_MASK
239        custom_dict = {"starhub": starhub, "module": module, "base": base}
240        return custom_dict

Get the custom modifications of the currently used device. (see register SPCM_CUSTOMMOD in the manual)

Returns
  • dict: The custom modifications of the currently used device
    • "starhub" - custom modifications to the starhub,
    • "module" - custom modification of the front-end module(s)
    • "base" - custom modification of the base card
def log_level(self, log_level: int = None) -> int:
242    def log_level(self, log_level : int = None) -> int:
243        """
244        Set the logging level of the driver
245    
246        Parameters
247        ----------
248        log_level : int
249            The logging level that is set for the driver
250        
251        Returns
252        -------
253        int
254            The logging level of the driver
255        """
256
257        if log_level is not None:
258            self.set_i(SPC_LOGDLLCALLS, log_level)
259        return self.get_i(SPC_LOGDLLCALLS)

Set the logging level of the driver

Parameters
  • log_level (int): The logging level that is set for the driver
Returns
  • int: The logging level of the driver
def cmd(self, *args) -> None:
261    def cmd(self, *args) -> None:
262        """
263        Execute spcm commands (see register `SPC_M2CMD` in the manual)
264    
265        Parameters
266        ----------
267        *args : int
268            The different command flags to be executed.
269        """
270
271        cmd = 0
272        for arg in args:
273            cmd |= arg
274        self.set_i(SPC_M2CMD, cmd)

Execute spcm commands (see register SPC_M2CMD in the manual)

Parameters
  • *args (int): The different command flags to be executed.
def timeout(self, timeout: int = None, return_unit=None) -> int:
277    def timeout(self, timeout : int = None, return_unit = None) -> int:
278        """
279        Sets the timeout in ms (see register `SPC_TIMEOUT` in the manual)
280        
281        Parameters
282        ----------
283        timeout : int
284            The timeout in ms
285        
286        Returns
287        -------
288        int
289            returns the current timeout in ms
290        """
291
292        if timeout is not None:
293            timeout = UnitConversion.convert(timeout, units.ms, int)
294            self.set_i(SPC_TIMEOUT, timeout)
295        return_value = self.get_i(SPC_TIMEOUT)
296        if return_unit is not None: return_value = UnitConversion.to_unit(return_value, return_unit)
297        return return_value

Sets the timeout in ms (see register SPC_TIMEOUT in the manual)

Parameters
  • timeout (int): The timeout in ms
Returns
  • int: returns the current timeout in ms
def start(self, *args) -> None:
299    def start(self, *args) -> None:
300        """
301        Starts the connected card and enables triggering on the card (see command `M2CMD_CARD_START` in the manual)
302
303        Parameters
304        ----------
305        *args : int
306            flags that are send together with the start command
307        """
308
309        self.cmd(M2CMD_CARD_START, *args)

Starts the connected card and enables triggering on the card (see command M2CMD_CARD_START in the manual)

Parameters
  • *args (int): flags that are send together with the start command
def stop(self, *args: int) -> None:
311    def stop(self, *args : int) -> None:
312        """
313        Stops the connected card (see command `M2CMD_CARD_STOP` in the manual)
314
315        Parameters
316        ----------
317        *args : int
318            flags that are send together with the stop command (e.g. M2CMD_DATA_STOPDMA)
319        """
320
321        self.cmd(M2CMD_CARD_STOP, *args)

Stops the connected card (see command M2CMD_CARD_STOP in the manual)

Parameters
  • *args (int): flags that are send together with the stop command (e.g. M2CMD_DATA_STOPDMA)
def reset(self) -> None:
323    def reset(self) -> None:
324        """
325        Resets the connected device (see command `M2CMD_CARD_RESET` in the manual)
326        """
327
328        self.cmd(M2CMD_CARD_RESET)

Resets the connected device (see command M2CMD_CARD_RESET in the manual)

def write_setup(self, *args) -> None:
330    def write_setup(self, *args) -> None:
331        """
332        Writes of the configuration registers previously changed to the device (see command `M2CMD_CARD_WRITESETUP` in the manual)
333
334        Parameters
335        ----------
336        *args : int
337            flags that are set with the write command
338        """
339
340        self.cmd(M2CMD_CARD_WRITESETUP, *args)

Writes of the configuration registers previously changed to the device (see command M2CMD_CARD_WRITESETUP in the manual)

Parameters
  • *args (int): flags that are set with the write command
def register_list(self, register_list: List[dict[str, Union[int, float]]]) -> None:
342    def register_list(self, register_list : List[dict[str, Union[int, float]]]) -> None:
343        """
344        Writes a list with dictionaries, where each dictionary corresponds to a command (see the user manual of your device for all the available registers)
345    
346        Parameters
347        ----------
348        register_list : List[dict[str, Union[int, float]]]
349            The list of commands that needs to written to the specific registers of the card.
350        """
351
352        c_astParams = (ST_LIST_PARAM * 1024)()
353        astParams = ctypes.cast(c_astParams, ctypes.POINTER(ST_LIST_PARAM))
354        for i, register in enumerate(register_list):
355            astParams[i].lReg = register["lReg"]
356            astParams[i].lType = register["lType"]
357            if register["lType"] == TYPE_INT64:
358                astParams[i].Value.llValue = register["llValue"]
359            elif register["lType"] == TYPE_DOUBLE:
360                astParams[i].Value.dValue = register["dValue"]
361        self.set_ptr(SPC_REGISTER_LIST, astParams, len(register_list) * ctypes.sizeof(ST_LIST_PARAM))

Writes a list with dictionaries, where each dictionary corresponds to a command (see the user manual of your device for all the available registers)

Parameters
  • register_list (List[dict[str, Union[int, float]]]): The list of commands that needs to written to the specific registers of the card.
def get_i(self, register: int) -> int:
364    def get_i(self, register : int) -> int:
365        """
366        Get the integer value of a specific register of the card (see the user manual of your device for all the available registers)
367    
368        Parameters
369        ----------
370        register : int
371            The specific register that will be read from.
372        
373        Returns
374        -------
375        int
376            The value as stored in the specific register
377        """
378
379        self._check_closed()
380        return_value = int64(0)
381        dwErr = spcm_dwGetParam_i64(self._handle, register, byref(return_value))
382        self._check_error(dwErr)
383        return return_value.value

Get the integer value of a specific register of the card (see the user manual of your device for all the available registers)

Parameters
  • register (int): The specific register that will be read from.
Returns
  • int: The value as stored in the specific register
def get(self, register: int) -> int:
364    def get_i(self, register : int) -> int:
365        """
366        Get the integer value of a specific register of the card (see the user manual of your device for all the available registers)
367    
368        Parameters
369        ----------
370        register : int
371            The specific register that will be read from.
372        
373        Returns
374        -------
375        int
376            The value as stored in the specific register
377        """
378
379        self._check_closed()
380        return_value = int64(0)
381        dwErr = spcm_dwGetParam_i64(self._handle, register, byref(return_value))
382        self._check_error(dwErr)
383        return return_value.value

Alias of get_i

def get_d(self, register: int) -> float:
387    def get_d(self, register : int) -> float:
388        """
389        Get the float value of a specific register of the card (see the user manual of your device for all the available registers)
390    
391        Parameters
392        ----------
393        register : int
394            The specific register that will be read from.
395        
396        Returns
397        -------
398        float
399            The value as stored in the specific register
400        """
401
402        self._check_closed()
403        return_value = c_double(0)
404        self._check_error(spcm_dwGetParam_d64(self._handle, register, byref(return_value)))
405        return return_value.value

Get the float value of a specific register of the card (see the user manual of your device for all the available registers)

Parameters
  • register (int): The specific register that will be read from.
Returns
  • float: The value as stored in the specific register
def get_str(self, register: int) -> str:
407    def get_str(self, register : int) -> str:
408        """
409        Get the string value of a specific register of the card (see the user manual of your device for all the available registers)
410    
411        Parameters
412        ----------
413        register : int
414            The specific register that will be read from.
415        
416        Returns
417        -------
418        str
419            The value as stored in the specific register
420        """
421
422        self._check_closed()
423        return_value = create_string_buffer(self._str_len)
424        self._check_error(spcm_dwGetParam_ptr(self._handle, register, byref(return_value), self._str_len))
425        return return_value.value.decode('utf-8')

Get the string value of a specific register of the card (see the user manual of your device for all the available registers)

Parameters
  • register (int): The specific register that will be read from.
Returns
  • str: The value as stored in the specific register
def set_i(self, register: int, value: int) -> None:
427    def set_i(self, register : int, value : int) -> None:
428        """
429        Write the value of a specific register to the card (see the user manual of your device for all the available registers)
430    
431        Parameters
432        ----------
433        register : int
434            The specific register that will be written.
435        value : int
436            The value that is written to the card.
437        """
438
439        self._check_closed()
440        self._check_error(spcm_dwSetParam_i64(self._handle, register, value))

Write the value of a specific register to the card (see the user manual of your device for all the available registers)

Parameters
  • register (int): The specific register that will be written.
  • value (int): The value that is written to the card.
def set_d(self, register: int, value: float) -> None:
442    def set_d(self, register : int, value : float) -> None:
443        """
444        Write the value of a specific register to the card (see the user manual of your device for all the available registers)
445    
446        Parameters
447        ----------
448        register : int
449            The specific register that will be written.
450        value : float
451            The value that is written to the card.
452        """
453
454        self._check_closed()
455        self._check_error(spcm_dwSetParam_d64(self._handle, register, value))

Write the value of a specific register to the card (see the user manual of your device for all the available registers)

Parameters
  • register (int): The specific register that will be written.
  • value (float): The value that is written to the card.
def set_ptr(self, register: int, reference: ctypes.c_void_p, size: int) -> None:
457    def set_ptr(self, register : int, reference : c_void_p, size : int) -> None:
458        """
459        Use a memory segment to write to a specific register of the card (see the user manual of your device for all the available registers)
460    
461        Parameters
462        ----------
463        register : int
464            The specific register that will be read from.
465        reference : c_void_p
466            pointer to the memory segment
467        size : int
468            size of the memory segment
469        
470        Returns
471        -------
472        int
473            The value as stored in the specific register
474        """
475
476        self._check_closed()
477        self._check_error(spcm_dwSetParam_ptr(self._handle, register, reference, size))

Use a memory segment to write to a specific register of the card (see the user manual of your device for all the available registers)

Parameters
  • register (int): The specific register that will be read from.
  • reference (c_void_p): pointer to the memory segment
  • size (int): size of the memory segment
Returns
  • int: The value as stored in the specific register
def get_error_info(self) -> SpcmError:
502    def get_error_info(self) -> SpcmError:
503        """
504        Create an SpcmError object and store it in an object parameter
505    
506        Returns
507        ----------
508        SpcmError
509            the Error object containing the last error
510        """
511
512        self._last_error = SpcmError(self._handle)
513        return self._last_error

Create an SpcmError object and store it in an object parameter

Returns
  • SpcmError: the Error object containing the last error
def open(self, device_identifier: str) -> None:
548    def open(self, device_identifier : str) -> None:
549        """
550        Open a connection to the card and return the handle (see the user manual of your specific device on how to find out the device_identifier string)
551    
552        Parameters
553        ----------
554        device_identifier : str
555            The card identifier string (e.g. '/dev/spcm0' for a local device or 'TCPIP::192.168.1.10::inst0::INSTR' for a remote device)
556        """
557
558        self._handle = spcm_hOpen(create_string_buffer(bytes(device_identifier, 'utf-8')))
559        self._closed = False

Open a connection to the card and return the handle (see the user manual of your specific device on how to find out the device_identifier string)

Parameters
  • device_identifier (str): The card identifier string (e.g. '/dev/spcm0' for a local device or 'TCPIP::192.168.1.10::inst0::INSTR' for a remote device)
@staticmethod
def close(handle) -> None:
561    @staticmethod
562    def close(handle) -> None:
563        """
564        Close a connection to the card using a handle
565    
566        Parameters
567        ----------
568        handle
569            the handle object used for the card connection that is closed
570        """
571
572        spcm_vClose(handle)

Close a connection to the card using a handle

Parameters
  • handle: the handle object used for the card connection that is closed
class Card(spcm.Device):
 15class Card(Device):
 16    """
 17    a high-level class to control Spectrum Instrumentation cards
 18
 19    For more information about what setups are available, please have a look at the user manual
 20    for your specific card.
 21
 22    """
 23
 24    _std_device_identifier : str = "/dev/spcm{}"
 25    _max_cards : int = 64
 26    
 27    _function_type : int = 0
 28    _card_type : int = 0
 29    _max_sample_value : int = 0
 30
 31    def __enter__(self) -> 'Card':
 32        """
 33        Context manager entry function
 34
 35        Returns
 36        -------
 37        Card
 38            The card object
 39        
 40        Raises
 41        ------
 42        SpcmException
 43        """
 44        super().__enter__()
 45
 46        # keyword arguments
 47        card_type = self._kwargs.get("card_type", 0)
 48        serial_number = self._kwargs.get("serial_number", 0)
 49
 50        if self.device_identifier == "":
 51            # No device identifier was given, so we need to find the first card
 52            self._handle = self.find(card_type=card_type, serial_number=serial_number)
 53            if not self._handle:
 54                if card_type:
 55                    raise SpcmException(text="No card found of right type")
 56                elif serial_number:
 57                    raise SpcmException(text="No card found with serial number: {}".format(serial_number))
 58        elif self._handle:
 59            if card_type != 0 and self.function_type() != card_type:
 60                raise SpcmException(text="The card with the given device identifier is not the correct type")
 61            elif serial_number != 0 and self.sn() != serial_number:
 62                raise SpcmException(text="The card with the given device identifier does not have the correct serial number")
 63        
 64        # Check python, driver and kernel version
 65        if self._verbose:
 66            print("Python version: {} on {}".format (platform.python_version(), platform.system()))
 67            print("Driver version: {major}.{minor}.{build}".format(**self.drv_version()))
 68            print("Kernel version: {major}.{minor}.{build}".format(**self.kernel_version()))
 69            if self._handle:
 70                print("Found '{}': {} sn {:05d}".format(self.device_identifier, self.product_name(), self.sn()))
 71
 72        # Get the function type of the card
 73        self._function_type = self.get_i(SPC_FNCTYPE)
 74        self._card_type = self.get_i(SPC_PCITYP)
 75        self._features = self.get_i(SPC_PCIFEATURES)
 76        self._max_sample_value = self.get_i(SPC_MIINST_MAXADCVALUE)
 77        
 78        return self
 79    
 80    def __str__(self) -> str:
 81        """
 82        String representation of the card
 83
 84        Returns
 85        -------
 86        str
 87            String representation of the card
 88        """
 89        return "Card: {} sn {:05d}".format(self.product_name(), self.sn())
 90    __repr__ = __str__
 91    
 92    def find(self, card_type : int = 0, serial_number : int = 0) -> Union[bool, int]:
 93        """
 94        Find first card that is connected to the computer, with either the given card type or serial number
 95
 96        Parameters
 97        ----------
 98        card_type : int = 0
 99            The function type of the card that needs to be found
100        serial_number : int = 0
101            The serial number of the card that needs to be found
102
103        """
104        for nr in range(self._max_cards):
105            device_identifier = self._std_device_identifier.format(nr)
106            handle = spcm_hOpen(ctypes.create_string_buffer(bytes(device_identifier, 'utf-8')))
107            if handle:
108                self.device_identifier = device_identifier
109                return_value = ctypes.c_int64()
110                spcm_dwGetParam_i64(handle, SPC_FNCTYPE, ctypes.byref(return_value))
111                function_type = return_value.value
112                spcm_dwGetParam_i64(handle, SPC_PCISERIALNO, ctypes.byref(return_value))
113                sn = return_value.value
114                if card_type != 0 and (card_type & function_type) == function_type:
115                    return handle
116                elif sn != 0 and sn == serial_number:
117                    return handle
118                elif serial_number == 0 and card_type == 0:
119                    return handle
120                spcm_vClose(handle)
121        return False
122
123                
124    # High-level parameter functions, that use the low-level get and set function
125    def status(self) -> int:
126        """
127        Get the status of the card (see register `SPC_M2STATUS` in the manual)
128    
129        Returns
130        -------
131        int
132            The status of the card
133        """
134
135        return self.get_i(SPC_M2STATUS)
136    
137    def card_type(self) -> int:
138        """
139        Get the card type of the card (see register `SPC_PCITYP` in the manual)
140    
141        Returns
142        -------
143        int
144            The card type of the card
145        """
146
147        return self._card_type
148    
149    def series(self) -> int:
150        """
151        Get the series of the card (see register `SPC_PCITYP` and `TYP_SERIESMASK` in the manual)
152    
153        Returns
154        -------
155        int
156            The series of the card
157        """
158
159        return self.card_type() & TYP_SERIESMASK
160
161    def function_type(self) -> int:
162        """
163        Gives information about what type of card it is. (see register `SPC_FNCTYPE` in the manual)
164    
165        Returns
166        -------
167        int
168            The function type of the card
169
170            * SPCM_TYPE_AI = 1h - Analog input card (analog acquisition; the M2i.4028 and M2i.4038 also return this value)
171            * SPCM_TYPE_AO = 2h - Analog output card (arbitrary waveform generators)
172            * SPCM_TYPE_DI = 4h - Digital input card (logic analyzer card)
173            * SPCM_TYPE_DO = 8h - Digital output card (pattern generators)
174            * SPCM_TYPE_DIO = 10h - Digital I/O (input/output) card, where the direction is software selectable.
175        """
176
177        return self._function_type
178
179    def features(self) -> int:
180        """
181        Get the features of the card (see register `SPC_PCIFEATURES` in the manual)
182    
183        Returns
184        -------
185        int
186            The features of the card
187        """
188
189        return self._features
190    
191    def starhub_card(self) -> bool:
192        """
193        Check if the card is a starhub card (see register `SPC_PCIFEATURES` in the manual)
194    
195        Returns
196        -------
197        bool
198            True if the card is the card that carriers a starhub, False otherwise
199        """
200
201        return bool(self._features & SPCM_FEAT_STARHUBXX_MASK)
202    
203    def num_modules(self) -> int:
204        """
205        Get the number of modules of the card (see register `SPC_MIINST_MODULES` in the manual)
206    
207        Returns
208        -------
209        int
210            The number of modules of the card
211        """
212
213        return self.get_i(SPC_MIINST_MODULES)
214
215    def channels_per_module(self) -> int:
216        """
217        Get the number of channels per module of the card (see register `SPC_MIINST_CHPERMODULE` in the manual)
218    
219        Returns
220        -------
221        int
222            The number of channels per module of the card
223        """
224
225        return self.get_i(SPC_MIINST_CHPERMODULE)
226    
227    def num_channels(self) -> int:
228        """
229        Get the number of channels of the card (= SPC_MIINST_MODULES * SPC_MIINST_CHPERMODULE)
230    
231        Returns
232        -------
233        int
234            The number of channels of the card
235        """
236
237        return self.num_modules() * self.channels_per_module()
238
239    def card_mode(self, card_mode : int = None) -> int:
240        """
241        Set the card mode of the connected card (see register `SPC_CARDMODE` in the manual)
242        
243        Parameters
244        ----------
245        card_mode : int
246            the mode that the card needs to operate in
247
248        Returns
249        -------
250        int
251            the mode that the card operates in
252        """
253        
254        if card_mode is not None:
255            self.set_i(SPC_CARDMODE, card_mode)
256        return self.get_i(SPC_CARDMODE)
257
258    def product_name(self) -> str:
259        """
260        Get the product name of the card (see register `SPC_PCITYP` in the manual)
261    
262        Returns
263        -------
264        str
265            The product name of the connected card (e.g. M4i.6631-x8)
266        """
267
268        return self.get_str(SPC_PCITYP)
269    
270    def sn(self) -> int:
271        """
272        Get the serial number of a product (see register `SPC_PCISERIALNO` in the manual)
273    
274        Returns
275        -------
276        int
277            The serial number of the connected card (e.g. 12345)
278        """
279
280        return self.get_i(SPC_PCISERIALNO)
281
282    def active_channels(self) -> int:
283        """
284        Get the number of channels of the card (see register `SPC_CHCOUNT` in the manual)
285    
286        Returns
287        -------
288        int
289            The number of channels of the card
290        """
291
292        return self.get_i(SPC_CHCOUNT)
293
294    def bits_per_sample(self) -> int:
295        """
296        Get the number of bits per sample of the card (see register `SPC_MIINST_BITSPERSAMPLE` in the manual)
297    
298        Returns
299        -------
300        int
301            The number of bits per sample of the card
302        """
303
304        return self.get_i(SPC_MIINST_BITSPERSAMPLE)
305    
306    def bytes_per_sample(self) -> int:
307        """
308        Get the number of bytes per sample
309
310        Returns
311        -------
312        int
313            number of bytes per sample
314        """
315        return self.get_i(SPC_MIINST_BYTESPERSAMPLE)
316    
317    def max_sample_value(self) -> int:
318        """
319        Get the maximum ADC value of the card (see register `SPC_MIINST_MAXADCVALUE` in the manual)
320
321        Returns
322        -------
323        int
324            The maximum ADC value of the card
325        """
326
327        return self._max_sample_value
328    
329    def loops(self, loops : int = None) -> int:
330        """
331        Set the number of times the memory is replayed. If set to zero the generation will run continuously until it is 
332        stopped by the user.  (see register `SPC_LOOPS` in the manual)
333        
334        Parameters
335        ----------
336        loops : int
337            the number of loops that the card should perform
338        """
339
340        if loops is not None:
341            self.set_i(SPC_LOOPS, loops)
342        return self.get_i(SPC_LOOPS)

a high-level class to control Spectrum Instrumentation cards

For more information about what setups are available, please have a look at the user manual for your specific card.

def find(self, card_type: int = 0, serial_number: int = 0) -> Union[bool, int]:
 92    def find(self, card_type : int = 0, serial_number : int = 0) -> Union[bool, int]:
 93        """
 94        Find first card that is connected to the computer, with either the given card type or serial number
 95
 96        Parameters
 97        ----------
 98        card_type : int = 0
 99            The function type of the card that needs to be found
100        serial_number : int = 0
101            The serial number of the card that needs to be found
102
103        """
104        for nr in range(self._max_cards):
105            device_identifier = self._std_device_identifier.format(nr)
106            handle = spcm_hOpen(ctypes.create_string_buffer(bytes(device_identifier, 'utf-8')))
107            if handle:
108                self.device_identifier = device_identifier
109                return_value = ctypes.c_int64()
110                spcm_dwGetParam_i64(handle, SPC_FNCTYPE, ctypes.byref(return_value))
111                function_type = return_value.value
112                spcm_dwGetParam_i64(handle, SPC_PCISERIALNO, ctypes.byref(return_value))
113                sn = return_value.value
114                if card_type != 0 and (card_type & function_type) == function_type:
115                    return handle
116                elif sn != 0 and sn == serial_number:
117                    return handle
118                elif serial_number == 0 and card_type == 0:
119                    return handle
120                spcm_vClose(handle)
121        return False

Find first card that is connected to the computer, with either the given card type or serial number

Parameters
  • card_type (int = 0): The function type of the card that needs to be found
  • serial_number (int = 0): The serial number of the card that needs to be found
def status(self) -> int:
125    def status(self) -> int:
126        """
127        Get the status of the card (see register `SPC_M2STATUS` in the manual)
128    
129        Returns
130        -------
131        int
132            The status of the card
133        """
134
135        return self.get_i(SPC_M2STATUS)

Get the status of the card (see register SPC_M2STATUS in the manual)

Returns
  • int: The status of the card
def card_type(self) -> int:
137    def card_type(self) -> int:
138        """
139        Get the card type of the card (see register `SPC_PCITYP` in the manual)
140    
141        Returns
142        -------
143        int
144            The card type of the card
145        """
146
147        return self._card_type

Get the card type of the card (see register SPC_PCITYP in the manual)

Returns
  • int: The card type of the card
def series(self) -> int:
149    def series(self) -> int:
150        """
151        Get the series of the card (see register `SPC_PCITYP` and `TYP_SERIESMASK` in the manual)
152    
153        Returns
154        -------
155        int
156            The series of the card
157        """
158
159        return self.card_type() & TYP_SERIESMASK

Get the series of the card (see register SPC_PCITYP and TYP_SERIESMASK in the manual)

Returns
  • int: The series of the card
def function_type(self) -> int:
161    def function_type(self) -> int:
162        """
163        Gives information about what type of card it is. (see register `SPC_FNCTYPE` in the manual)
164    
165        Returns
166        -------
167        int
168            The function type of the card
169
170            * SPCM_TYPE_AI = 1h - Analog input card (analog acquisition; the M2i.4028 and M2i.4038 also return this value)
171            * SPCM_TYPE_AO = 2h - Analog output card (arbitrary waveform generators)
172            * SPCM_TYPE_DI = 4h - Digital input card (logic analyzer card)
173            * SPCM_TYPE_DO = 8h - Digital output card (pattern generators)
174            * SPCM_TYPE_DIO = 10h - Digital I/O (input/output) card, where the direction is software selectable.
175        """
176
177        return self._function_type

Gives information about what type of card it is. (see register SPC_FNCTYPE in the manual)

Returns
  • int: The function type of the card

    • SPCM_TYPE_AI = 1h - Analog input card (analog acquisition; the M2i.4028 and M2i.4038 also return this value)
    • SPCM_TYPE_AO = 2h - Analog output card (arbitrary waveform generators)
    • SPCM_TYPE_DI = 4h - Digital input card (logic analyzer card)
    • SPCM_TYPE_DO = 8h - Digital output card (pattern generators)
    • SPCM_TYPE_DIO = 10h - Digital I/O (input/output) card, where the direction is software selectable.
def features(self) -> int:
179    def features(self) -> int:
180        """
181        Get the features of the card (see register `SPC_PCIFEATURES` in the manual)
182    
183        Returns
184        -------
185        int
186            The features of the card
187        """
188
189        return self._features

Get the features of the card (see register SPC_PCIFEATURES in the manual)

Returns
  • int: The features of the card
def starhub_card(self) -> bool:
191    def starhub_card(self) -> bool:
192        """
193        Check if the card is a starhub card (see register `SPC_PCIFEATURES` in the manual)
194    
195        Returns
196        -------
197        bool
198            True if the card is the card that carriers a starhub, False otherwise
199        """
200
201        return bool(self._features & SPCM_FEAT_STARHUBXX_MASK)

Check if the card is a starhub card (see register SPC_PCIFEATURES in the manual)

Returns
  • bool: True if the card is the card that carriers a starhub, False otherwise
def num_modules(self) -> int:
203    def num_modules(self) -> int:
204        """
205        Get the number of modules of the card (see register `SPC_MIINST_MODULES` in the manual)
206    
207        Returns
208        -------
209        int
210            The number of modules of the card
211        """
212
213        return self.get_i(SPC_MIINST_MODULES)

Get the number of modules of the card (see register SPC_MIINST_MODULES in the manual)

Returns
  • int: The number of modules of the card
def channels_per_module(self) -> int:
215    def channels_per_module(self) -> int:
216        """
217        Get the number of channels per module of the card (see register `SPC_MIINST_CHPERMODULE` in the manual)
218    
219        Returns
220        -------
221        int
222            The number of channels per module of the card
223        """
224
225        return self.get_i(SPC_MIINST_CHPERMODULE)

Get the number of channels per module of the card (see register SPC_MIINST_CHPERMODULE in the manual)

Returns
  • int: The number of channels per module of the card
def num_channels(self) -> int:
227    def num_channels(self) -> int:
228        """
229        Get the number of channels of the card (= SPC_MIINST_MODULES * SPC_MIINST_CHPERMODULE)
230    
231        Returns
232        -------
233        int
234            The number of channels of the card
235        """
236
237        return self.num_modules() * self.channels_per_module()

Get the number of channels of the card (= SPC_MIINST_MODULES * SPC_MIINST_CHPERMODULE)

Returns
  • int: The number of channels of the card
def card_mode(self, card_mode: int = None) -> int:
239    def card_mode(self, card_mode : int = None) -> int:
240        """
241        Set the card mode of the connected card (see register `SPC_CARDMODE` in the manual)
242        
243        Parameters
244        ----------
245        card_mode : int
246            the mode that the card needs to operate in
247
248        Returns
249        -------
250        int
251            the mode that the card operates in
252        """
253        
254        if card_mode is not None:
255            self.set_i(SPC_CARDMODE, card_mode)
256        return self.get_i(SPC_CARDMODE)

Set the card mode of the connected card (see register SPC_CARDMODE in the manual)

Parameters
  • card_mode (int): the mode that the card needs to operate in
Returns
  • int: the mode that the card operates in
def product_name(self) -> str:
258    def product_name(self) -> str:
259        """
260        Get the product name of the card (see register `SPC_PCITYP` in the manual)
261    
262        Returns
263        -------
264        str
265            The product name of the connected card (e.g. M4i.6631-x8)
266        """
267
268        return self.get_str(SPC_PCITYP)

Get the product name of the card (see register SPC_PCITYP in the manual)

Returns
  • str: The product name of the connected card (e.g. M4i.6631-x8)
def sn(self) -> int:
270    def sn(self) -> int:
271        """
272        Get the serial number of a product (see register `SPC_PCISERIALNO` in the manual)
273    
274        Returns
275        -------
276        int
277            The serial number of the connected card (e.g. 12345)
278        """
279
280        return self.get_i(SPC_PCISERIALNO)

Get the serial number of a product (see register SPC_PCISERIALNO in the manual)

Returns
  • int: The serial number of the connected card (e.g. 12345)
def active_channels(self) -> int:
282    def active_channels(self) -> int:
283        """
284        Get the number of channels of the card (see register `SPC_CHCOUNT` in the manual)
285    
286        Returns
287        -------
288        int
289            The number of channels of the card
290        """
291
292        return self.get_i(SPC_CHCOUNT)

Get the number of channels of the card (see register SPC_CHCOUNT in the manual)

Returns
  • int: The number of channels of the card
def bits_per_sample(self) -> int:
294    def bits_per_sample(self) -> int:
295        """
296        Get the number of bits per sample of the card (see register `SPC_MIINST_BITSPERSAMPLE` in the manual)
297    
298        Returns
299        -------
300        int
301            The number of bits per sample of the card
302        """
303
304        return self.get_i(SPC_MIINST_BITSPERSAMPLE)

Get the number of bits per sample of the card (see register SPC_MIINST_BITSPERSAMPLE in the manual)

Returns
  • int: The number of bits per sample of the card
def bytes_per_sample(self) -> int:
306    def bytes_per_sample(self) -> int:
307        """
308        Get the number of bytes per sample
309
310        Returns
311        -------
312        int
313            number of bytes per sample
314        """
315        return self.get_i(SPC_MIINST_BYTESPERSAMPLE)

Get the number of bytes per sample

Returns
  • int: number of bytes per sample
def max_sample_value(self) -> int:
317    def max_sample_value(self) -> int:
318        """
319        Get the maximum ADC value of the card (see register `SPC_MIINST_MAXADCVALUE` in the manual)
320
321        Returns
322        -------
323        int
324            The maximum ADC value of the card
325        """
326
327        return self._max_sample_value

Get the maximum ADC value of the card (see register SPC_MIINST_MAXADCVALUE in the manual)

Returns
  • int: The maximum ADC value of the card
def loops(self, loops: int = None) -> int:
329    def loops(self, loops : int = None) -> int:
330        """
331        Set the number of times the memory is replayed. If set to zero the generation will run continuously until it is 
332        stopped by the user.  (see register `SPC_LOOPS` in the manual)
333        
334        Parameters
335        ----------
336        loops : int
337            the number of loops that the card should perform
338        """
339
340        if loops is not None:
341            self.set_i(SPC_LOOPS, loops)
342        return self.get_i(SPC_LOOPS)

Set the number of times the memory is replayed. If set to zero the generation will run continuously until it is stopped by the user. (see register SPC_LOOPS in the manual)

Parameters
  • loops (int): the number of loops that the card should perform
class Sync(spcm.Device):
  8class Sync(Device):
  9    """a class to control Spectrum Instrumentation Starhub synchronization devices
 10
 11    For more information about what setups are available, please have a look at the user manual
 12    for your specific Starhub.
 13    
 14    Exceptions
 15    ----------
 16    SpcmException
 17    SpcmTimeout
 18    """
 19
 20    def enable(self, enable : int = None) -> int:
 21        """
 22        Enable or disable the Starthub (see register 'SPC_SYNC_ENABLEMASK' in chapter `Star-Hub` in the manual)
 23
 24        Parameters
 25        ----------
 26        enable : int or bool
 27            enable or disable the Starthub
 28        """
 29
 30        if enable is not None:
 31            enable_mask = 0
 32            if isinstance(enable, bool):
 33                num_cards = self.sync_count()
 34                enable_mask = ((1 << num_cards) - 1) if enable else 0
 35            elif isinstance(enable, int):
 36                enable_mask = enable
 37            else:
 38                raise ValueError("The enable parameter must be a boolean or an integer")
 39            self.set_i(SPC_SYNC_ENABLEMASK, enable_mask)
 40        return self.get_i(SPC_SYNC_ENABLEMASK)
 41    
 42    def num_connectors(self) -> int:
 43        """
 44        Number of connectors that the Star-Hub offers at max. (see register 'SPC_SYNC_READ_NUMCONNECTORS' in chapter `Star-Hub` in the manual)
 45
 46        Returns
 47        -------
 48        int
 49            number of connectors on the StarHub
 50        """
 51
 52        return self.get_i(SPC_SYNC_READ_NUMCONNECTORS)
 53    
 54    def sync_count(self) -> int:
 55        """
 56        Number of cards that are connected to this Star-Hub (see register 'SPC_SYNC_READ_SYNCCOUNT' in chapter `Star-Hub` in the manual)
 57
 58        Returns
 59        -------
 60        int
 61            number of synchronized cards
 62        """
 63
 64        return self.get_i(SPC_SYNC_READ_SYNCCOUNT)
 65    
 66    def card_index(self, index) -> int:
 67        """
 68        Index of the card that is connected to the Star-Hub at the given local index (see register 'SPC_SYNC_READ_CARDIDX0' in chapter `Star-Hub` in the manual)
 69
 70        Parameters
 71        ----------
 72        index : int
 73            connector index
 74
 75        Returns
 76        -------
 77        int
 78            card index
 79        """
 80
 81        return self.get_i(SPC_SYNC_READ_CARDIDX0 + index)
 82    
 83    def cable_connection(self, index) -> int:
 84        """
 85        Returns the index of the cable connection that is used for the logical connection `index`. (see register 'SPC_SYNC_READ_CABLECON0' in chapter `Star-Hub` in the manual)
 86        The cable connections can be seen printed on the PCB of the star-hub. Use these cable connection information in case 
 87        that there are hardware failures with the star-hub cabeling.
 88
 89        Parameters
 90        ----------
 91        index : int
 92            connector index
 93
 94        Returns
 95        -------
 96        int
 97            cable index
 98        """
 99
100        return self.get_i(SPC_SYNC_READ_CABLECON0 + index)

a class to control Spectrum Instrumentation Starhub synchronization devices

For more information about what setups are available, please have a look at the user manual for your specific Starhub.

Exceptions

SpcmException SpcmTimeout

def enable(self, enable: int = None) -> int:
20    def enable(self, enable : int = None) -> int:
21        """
22        Enable or disable the Starthub (see register 'SPC_SYNC_ENABLEMASK' in chapter `Star-Hub` in the manual)
23
24        Parameters
25        ----------
26        enable : int or bool
27            enable or disable the Starthub
28        """
29
30        if enable is not None:
31            enable_mask = 0
32            if isinstance(enable, bool):
33                num_cards = self.sync_count()
34                enable_mask = ((1 << num_cards) - 1) if enable else 0
35            elif isinstance(enable, int):
36                enable_mask = enable
37            else:
38                raise ValueError("The enable parameter must be a boolean or an integer")
39            self.set_i(SPC_SYNC_ENABLEMASK, enable_mask)
40        return self.get_i(SPC_SYNC_ENABLEMASK)

Enable or disable the Starthub (see register 'SPC_SYNC_ENABLEMASK' in chapter Star-Hub in the manual)

Parameters
  • enable (int or bool): enable or disable the Starthub
def num_connectors(self) -> int:
42    def num_connectors(self) -> int:
43        """
44        Number of connectors that the Star-Hub offers at max. (see register 'SPC_SYNC_READ_NUMCONNECTORS' in chapter `Star-Hub` in the manual)
45
46        Returns
47        -------
48        int
49            number of connectors on the StarHub
50        """
51
52        return self.get_i(SPC_SYNC_READ_NUMCONNECTORS)

Number of connectors that the Star-Hub offers at max. (see register 'SPC_SYNC_READ_NUMCONNECTORS' in chapter Star-Hub in the manual)

Returns
  • int: number of connectors on the StarHub
def sync_count(self) -> int:
54    def sync_count(self) -> int:
55        """
56        Number of cards that are connected to this Star-Hub (see register 'SPC_SYNC_READ_SYNCCOUNT' in chapter `Star-Hub` in the manual)
57
58        Returns
59        -------
60        int
61            number of synchronized cards
62        """
63
64        return self.get_i(SPC_SYNC_READ_SYNCCOUNT)

Number of cards that are connected to this Star-Hub (see register 'SPC_SYNC_READ_SYNCCOUNT' in chapter Star-Hub in the manual)

Returns
  • int: number of synchronized cards
def card_index(self, index) -> int:
66    def card_index(self, index) -> int:
67        """
68        Index of the card that is connected to the Star-Hub at the given local index (see register 'SPC_SYNC_READ_CARDIDX0' in chapter `Star-Hub` in the manual)
69
70        Parameters
71        ----------
72        index : int
73            connector index
74
75        Returns
76        -------
77        int
78            card index
79        """
80
81        return self.get_i(SPC_SYNC_READ_CARDIDX0 + index)

Index of the card that is connected to the Star-Hub at the given local index (see register 'SPC_SYNC_READ_CARDIDX0' in chapter Star-Hub in the manual)

Parameters
  • index (int): connector index
Returns
  • int: card index
def cable_connection(self, index) -> int:
 83    def cable_connection(self, index) -> int:
 84        """
 85        Returns the index of the cable connection that is used for the logical connection `index`. (see register 'SPC_SYNC_READ_CABLECON0' in chapter `Star-Hub` in the manual)
 86        The cable connections can be seen printed on the PCB of the star-hub. Use these cable connection information in case 
 87        that there are hardware failures with the star-hub cabeling.
 88
 89        Parameters
 90        ----------
 91        index : int
 92            connector index
 93
 94        Returns
 95        -------
 96        int
 97            cable index
 98        """
 99
100        return self.get_i(SPC_SYNC_READ_CABLECON0 + index)

Returns the index of the cable connection that is used for the logical connection index. (see register 'SPC_SYNC_READ_CABLECON0' in chapter Star-Hub in the manual) The cable connections can be seen printed on the PCB of the star-hub. Use these cable connection information in case that there are hardware failures with the star-hub cabeling.

Parameters
  • index (int): connector index
Returns
  • int: cable index
class CardStack(contextlib.ExitStack):
 13class CardStack(ExitStack):
 14    """
 15    A context manager object for handling multiple Card objects with or without a Sync object
 16 
 17    Parameters
 18    ----------
 19    cards : list[Card]
 20        a list of card objects that is managed by the context manager
 21    sync : Sync
 22        an object for handling the synchronization of cards
 23    sync_card : Card
 24        a card object that is used for synchronization
 25    sync_id : int
 26        the index of the sync card in the list of cards
 27    is_synced : bool
 28        a boolean that indicates if the cards are synchronized
 29    """
 30
 31    cards : list[Card] = []
 32    sync : Sync = None
 33    sync_card : Card = None
 34    sync_id : int = -1
 35    is_synced : bool = False
 36
 37    def __init__(self, card_identifiers : list[str] = [], sync_identifier : str = "", find_sync_card : bool = False) -> None:
 38        """
 39        Initialize the CardStack object with a list of card identifiers and a sync identifier
 40
 41        Parameters
 42        ----------
 43        card_identifiers : list[str] = []
 44            a list of strings that represent the VISA strings of the cards
 45        sync_identifier : str = ""
 46            a string that represents the VISA string of the sync card
 47        find_sync : bool = False
 48            a boolean that indicates if the sync card should be found automatically
 49        """
 50
 51        super().__init__()
 52        # Handle card objects
 53        self.cards = [self.enter_context(Card(identifier)) for identifier in card_identifiers]
 54        if find_sync_card:
 55            for id, card in enumerate(self.cards):
 56                if card.starhub_card():
 57                    self.sync_card = card
 58                    self.sync_id = id
 59                    self.is_synced = True
 60                    break
 61        if sync_identifier and (not find_sync_card or self.is_synced):
 62            self.sync = self.enter_context(Sync(sync_identifier))
 63            self.is_synced = bool(self.sync)
 64    
 65    def __bool__(self) -> bool:
 66        """Checks if all defined cards are connected"""
 67        connected = True
 68        for card in self.cards:
 69            connected &= bool(card)
 70        return connected
 71    
 72    def synched(self):
 73        """Checks if the sync card is connected
 74        """
 75        return bool(self.is_synched)
 76    
 77    def start(self, *args) -> None:
 78        """
 79        Start all cards
 80
 81        Parameters
 82        ----------
 83        args : list
 84            a list of arguments that will be passed to the start method of the cards
 85        """
 86        
 87        if self.sync:
 88            self.sync.start(*args)
 89        else:
 90            for card in self.cards:
 91                card.start(*args)
 92    
 93    def stop(self, *args) -> None:
 94        """
 95        Stop all cards
 96
 97        Parameters
 98        ----------
 99        args : list
100            a list of arguments that will be passed to the stop method of the cards
101        """
102        
103        if self.sync:
104            self.sync.stop(*args)
105        else:
106            for card in self.cards:
107                card.stop(*args)
108    
109    def reset(self, *args) -> None:
110        """
111        Reset all cards
112
113        Parameters
114        ----------
115        args : list
116            a list of arguments that will be passed to the reset method of the cards
117        """
118        
119        if self.sync:
120            self.sync.reset(*args)
121        else:
122            for card in self.cards:
123                card.reset(*args)
124    
125    def force_trigger(self, *args) -> None:
126        """
127        Force trigger on all cards
128
129        Parameters
130        ----------
131        args : list
132            a list of arguments that will be passed with the force trigger command for the cards
133        """
134        
135        # TODO: the force trigger needs to be correctly implemented in the driver
136        if self.sync_card:
137            self.sync_card.cmd(M2CMD_CARD_FORCETRIGGER, *args)
138        elif self.sync:
139            # self.sync.cmd(M2CMD_CARD_FORCETRIGGER, *args)
140            self.cards[0].cmd(M2CMD_CARD_FORCETRIGGER, *args)
141        else:
142            for card in self.cards:
143                card.cmd(M2CMD_CARD_FORCETRIGGER, *args)
144
145    def sync_enable(self, enable : int = True) -> int:
146        """
147        Enable synchronization on all cards
148
149        Parameters
150        ----------
151        enable : int or bool
152            a boolean or integer mask to enable or disable the synchronization of different channels
153        
154        Returns
155        -------
156        int
157            the mask of the enabled channels
158        
159        Raises
160        ------
161        ValueError
162            The enable parameter must be a boolean or an integer
163        SpcmException
164            No sync card avaliable to enable synchronization on the cards
165        """
166        
167        if self.sync:
168            return self.sync.enable(enable)
169        else:
170            raise SpcmException("No sync card avaliable to enable synchronization on the cards")
171    
172    
173    @staticmethod
174    def id_to_ip(device_identifier : str) -> str:
175        """
176        Returns the IP address of the Netbox using the device identifier
177
178        Parameters
179        ----------
180        device_identifier : str
181            The device identifier of the Netbox
182
183        Returns
184        -------
185        str
186            The IP address of the Netbox
187        """
188        ip = device_identifier
189        ip = ip[ip.find('::') + 2:]
190        ip = ip[:ip.find ('::')]
191        return ip
192    
193    @staticmethod
194    def discover(max_num_remote_cards : int = 50, max_visa_string_len : int = 256, max_idn_string_len : int = 256, timeout_ms : int = 5000) -> dict[list[str]]:
195        """
196        Do a discovery of the cards connected through a network
197
198        Parameters
199        ----------
200        max_num_remote_cards : int = 50
201            the maximum number of remote cards that can be discovered
202        max_visa_string_len : int = 256
203            the maximum length of the VISA string
204        max_idn_string_len : int = 256
205            the maximum length of the IDN string
206        timeout_ms : int = 5000
207            the timeout in milliseconds for the discovery process
208
209        Returns
210        -------
211        CardStack
212            a stack object with all the discovered cards
213        
214        Raises
215        ------
216        SpcmException
217            No Spectrum devices found
218        """
219
220        visa = (spcm_core.c_char_p * max_num_remote_cards)()
221        for i in range(max_num_remote_cards):
222            visa[i] = spcm_core.cast(spcm_core.create_string_buffer(max_visa_string_len), spcm_core.c_char_p)
223        spcm_core.spcm_dwDiscovery (visa, spcm_core.uint32(max_num_remote_cards), spcm_core.uint32(max_visa_string_len), spcm_core.uint32(timeout_ms))
224
225        # ----- check from which manufacturer the devices are -----
226        idn = (spcm_core.c_char_p * max_num_remote_cards)()
227        for i in range(max_num_remote_cards):
228            idn[i] = spcm_core.cast(spcm_core.create_string_buffer(max_idn_string_len), spcm_core.c_char_p)
229        spcm_core.spcm_dwSendIDNRequest (idn, spcm_core.uint32(max_num_remote_cards), spcm_core.uint32(max_idn_string_len))
230
231        # ----- store VISA strings for all discovered cards and open them afterwards -----
232        list_spectrum_devices = {}
233        for (id, visa) in zip(idn, visa):
234            if not id:
235                break
236
237            if id.decode('utf-8').startswith("Spectrum GmbH,"):
238                ip = __class__.id_to_ip(visa.decode("utf-8"))
239                if ip in list_spectrum_devices:
240                    list_spectrum_devices[ip].append(visa.decode("utf-8"))
241                else:
242                    list_spectrum_devices[ip] = [visa.decode("utf-8")]
243        
244        if not list_spectrum_devices:
245            raise SpcmException("No Spectrum devices found")
246        
247        return list_spectrum_devices

A context manager object for handling multiple Card objects with or without a Sync object

Parameters
  • cards (list[Card]): a list of card objects that is managed by the context manager
  • sync (Sync): an object for handling the synchronization of cards
  • sync_card (Card): a card object that is used for synchronization
  • sync_id (int): the index of the sync card in the list of cards
  • is_synced (bool): a boolean that indicates if the cards are synchronized
CardStack( card_identifiers: list[str] = [], sync_identifier: str = '', find_sync_card: bool = False)
37    def __init__(self, card_identifiers : list[str] = [], sync_identifier : str = "", find_sync_card : bool = False) -> None:
38        """
39        Initialize the CardStack object with a list of card identifiers and a sync identifier
40
41        Parameters
42        ----------
43        card_identifiers : list[str] = []
44            a list of strings that represent the VISA strings of the cards
45        sync_identifier : str = ""
46            a string that represents the VISA string of the sync card
47        find_sync : bool = False
48            a boolean that indicates if the sync card should be found automatically
49        """
50
51        super().__init__()
52        # Handle card objects
53        self.cards = [self.enter_context(Card(identifier)) for identifier in card_identifiers]
54        if find_sync_card:
55            for id, card in enumerate(self.cards):
56                if card.starhub_card():
57                    self.sync_card = card
58                    self.sync_id = id
59                    self.is_synced = True
60                    break
61        if sync_identifier and (not find_sync_card or self.is_synced):
62            self.sync = self.enter_context(Sync(sync_identifier))
63            self.is_synced = bool(self.sync)

Initialize the CardStack object with a list of card identifiers and a sync identifier

Parameters
  • card_identifiers (list[str] = []): a list of strings that represent the VISA strings of the cards
  • sync_identifier (str = ""): a string that represents the VISA string of the sync card
  • find_sync (bool = False): a boolean that indicates if the sync card should be found automatically
cards: list[Card] = []
sync: Sync = None
sync_card: Card = None
sync_id: int = -1
is_synced: bool = False
def synched(self):
72    def synched(self):
73        """Checks if the sync card is connected
74        """
75        return bool(self.is_synched)

Checks if the sync card is connected

def start(self, *args) -> None:
77    def start(self, *args) -> None:
78        """
79        Start all cards
80
81        Parameters
82        ----------
83        args : list
84            a list of arguments that will be passed to the start method of the cards
85        """
86        
87        if self.sync:
88            self.sync.start(*args)
89        else:
90            for card in self.cards:
91                card.start(*args)

Start all cards

Parameters
  • args (list): a list of arguments that will be passed to the start method of the cards
def stop(self, *args) -> None:
 93    def stop(self, *args) -> None:
 94        """
 95        Stop all cards
 96
 97        Parameters
 98        ----------
 99        args : list
100            a list of arguments that will be passed to the stop method of the cards
101        """
102        
103        if self.sync:
104            self.sync.stop(*args)
105        else:
106            for card in self.cards:
107                card.stop(*args)

Stop all cards

Parameters
  • args (list): a list of arguments that will be passed to the stop method of the cards
def reset(self, *args) -> None:
109    def reset(self, *args) -> None:
110        """
111        Reset all cards
112
113        Parameters
114        ----------
115        args : list
116            a list of arguments that will be passed to the reset method of the cards
117        """
118        
119        if self.sync:
120            self.sync.reset(*args)
121        else:
122            for card in self.cards:
123                card.reset(*args)

Reset all cards

Parameters
  • args (list): a list of arguments that will be passed to the reset method of the cards
def force_trigger(self, *args) -> None:
125    def force_trigger(self, *args) -> None:
126        """
127        Force trigger on all cards
128
129        Parameters
130        ----------
131        args : list
132            a list of arguments that will be passed with the force trigger command for the cards
133        """
134        
135        # TODO: the force trigger needs to be correctly implemented in the driver
136        if self.sync_card:
137            self.sync_card.cmd(M2CMD_CARD_FORCETRIGGER, *args)
138        elif self.sync:
139            # self.sync.cmd(M2CMD_CARD_FORCETRIGGER, *args)
140            self.cards[0].cmd(M2CMD_CARD_FORCETRIGGER, *args)
141        else:
142            for card in self.cards:
143                card.cmd(M2CMD_CARD_FORCETRIGGER, *args)

Force trigger on all cards

Parameters
  • args (list): a list of arguments that will be passed with the force trigger command for the cards
def sync_enable(self, enable: int = True) -> int:
145    def sync_enable(self, enable : int = True) -> int:
146        """
147        Enable synchronization on all cards
148
149        Parameters
150        ----------
151        enable : int or bool
152            a boolean or integer mask to enable or disable the synchronization of different channels
153        
154        Returns
155        -------
156        int
157            the mask of the enabled channels
158        
159        Raises
160        ------
161        ValueError
162            The enable parameter must be a boolean or an integer
163        SpcmException
164            No sync card avaliable to enable synchronization on the cards
165        """
166        
167        if self.sync:
168            return self.sync.enable(enable)
169        else:
170            raise SpcmException("No sync card avaliable to enable synchronization on the cards")

Enable synchronization on all cards

Parameters
  • enable (int or bool): a boolean or integer mask to enable or disable the synchronization of different channels
Returns
  • int: the mask of the enabled channels
Raises
  • ValueError: The enable parameter must be a boolean or an integer
  • SpcmException: No sync card avaliable to enable synchronization on the cards
@staticmethod
def id_to_ip(device_identifier: str) -> str:
173    @staticmethod
174    def id_to_ip(device_identifier : str) -> str:
175        """
176        Returns the IP address of the Netbox using the device identifier
177
178        Parameters
179        ----------
180        device_identifier : str
181            The device identifier of the Netbox
182
183        Returns
184        -------
185        str
186            The IP address of the Netbox
187        """
188        ip = device_identifier
189        ip = ip[ip.find('::') + 2:]
190        ip = ip[:ip.find ('::')]
191        return ip

Returns the IP address of the Netbox using the device identifier

Parameters
  • device_identifier (str): The device identifier of the Netbox
Returns
  • str: The IP address of the Netbox
@staticmethod
def discover( max_num_remote_cards: int = 50, max_visa_string_len: int = 256, max_idn_string_len: int = 256, timeout_ms: int = 5000) -> dict[list[str]]:
193    @staticmethod
194    def discover(max_num_remote_cards : int = 50, max_visa_string_len : int = 256, max_idn_string_len : int = 256, timeout_ms : int = 5000) -> dict[list[str]]:
195        """
196        Do a discovery of the cards connected through a network
197
198        Parameters
199        ----------
200        max_num_remote_cards : int = 50
201            the maximum number of remote cards that can be discovered
202        max_visa_string_len : int = 256
203            the maximum length of the VISA string
204        max_idn_string_len : int = 256
205            the maximum length of the IDN string
206        timeout_ms : int = 5000
207            the timeout in milliseconds for the discovery process
208
209        Returns
210        -------
211        CardStack
212            a stack object with all the discovered cards
213        
214        Raises
215        ------
216        SpcmException
217            No Spectrum devices found
218        """
219
220        visa = (spcm_core.c_char_p * max_num_remote_cards)()
221        for i in range(max_num_remote_cards):
222            visa[i] = spcm_core.cast(spcm_core.create_string_buffer(max_visa_string_len), spcm_core.c_char_p)
223        spcm_core.spcm_dwDiscovery (visa, spcm_core.uint32(max_num_remote_cards), spcm_core.uint32(max_visa_string_len), spcm_core.uint32(timeout_ms))
224
225        # ----- check from which manufacturer the devices are -----
226        idn = (spcm_core.c_char_p * max_num_remote_cards)()
227        for i in range(max_num_remote_cards):
228            idn[i] = spcm_core.cast(spcm_core.create_string_buffer(max_idn_string_len), spcm_core.c_char_p)
229        spcm_core.spcm_dwSendIDNRequest (idn, spcm_core.uint32(max_num_remote_cards), spcm_core.uint32(max_idn_string_len))
230
231        # ----- store VISA strings for all discovered cards and open them afterwards -----
232        list_spectrum_devices = {}
233        for (id, visa) in zip(idn, visa):
234            if not id:
235                break
236
237            if id.decode('utf-8').startswith("Spectrum GmbH,"):
238                ip = __class__.id_to_ip(visa.decode("utf-8"))
239                if ip in list_spectrum_devices:
240                    list_spectrum_devices[ip].append(visa.decode("utf-8"))
241                else:
242                    list_spectrum_devices[ip] = [visa.decode("utf-8")]
243        
244        if not list_spectrum_devices:
245            raise SpcmException("No Spectrum devices found")
246        
247        return list_spectrum_devices

Do a discovery of the cards connected through a network

Parameters
  • max_num_remote_cards (int = 50): the maximum number of remote cards that can be discovered
  • max_visa_string_len (int = 256): the maximum length of the VISA string
  • max_idn_string_len (int = 256): the maximum length of the IDN string
  • timeout_ms (int = 5000): the timeout in milliseconds for the discovery process
Returns
  • CardStack: a stack object with all the discovered cards
Raises
  • SpcmException: No Spectrum devices found
class Netbox(spcm.CardStack):
  9class Netbox(CardStack):
 10    """
 11    A hardware class that controls a Netbox device
 12 
 13    Parameters
 14    ----------
 15    netbox_card : Card
 16        a card object that is the main card in the Netbox
 17    netbox_number : int
 18        the index of the netbox card in the list of cards
 19    is_netbox : bool
 20        a boolean that indicates if the card is a Netbox
 21    
 22    """
 23    netbox_card : Card = None
 24    netbox_number : int = -1
 25    is_netbox : bool = False
 26
 27    def __init__(self, card_identifiers : list[str] = [], sync_identifier : str = "", find_sync : bool = False, **kwargs) -> None:
 28        """
 29        Initialize the Netbox object with a list of card identifiers and a sync identifier
 30
 31        Parameters
 32        ----------
 33        card_identifiers : list[str] = []
 34            a list of strings that represent the VISA strings of the cards
 35        sync_identifier : str = ""
 36            a string that represents the VISA string of the sync card
 37        find_sync : bool = False
 38            a boolean that indicates if the sync card should be found automatically
 39        """
 40
 41        super().__init__(card_identifiers, sync_identifier, find_sync, **kwargs)
 42
 43        for id, card in enumerate(self.cards):
 44            netbox_type = card.get_i(SPC_NETBOX_TYPE)
 45            if netbox_type != 0:
 46                self.netbox_card = card
 47                self.netbox_number = id
 48                self.is_netbox = True
 49                break
 50
 51    def __bool__(self) -> bool:
 52        """
 53        Checks if the Netbox is connected and returns true if the connection is alive
 54
 55        Returns
 56        -------
 57        bool
 58            True if the Netbox is connected
 59        """
 60
 61        return self.is_netbox
 62    
 63    def __str__(self) -> str:
 64        """
 65        Returns the string representation of the Netbox
 66
 67        Returns
 68        -------
 69        str
 70            The string representation of the Netbox
 71        """
 72
 73        netbox_type = self.type()
 74        netbox_str = "DN{series:x}.{family:x}{speed:x}-{channel:d}".format(**netbox_type)
 75        return f"Netbox: {netbox_str} at {self.ip()} sn {self.sn():05d}"
 76        
 77    def type(self) -> dict[int, int, int, int]:
 78        """
 79        Returns the type of the Netbox (see register 'SPC_NETBOX_TYPE' in chapter `Netbox` in the manual)
 80
 81        Returns
 82        -------
 83        dict[int, int, int, int]
 84            A dictionary with the series, family, speed and number of channels of the Netbox
 85        """
 86
 87        netbox_type = self.netbox_card.get_i(SPC_NETBOX_TYPE)
 88        netbox_series = (netbox_type & NETBOX_SERIES_MASK) >> 24
 89        netbox_family = (netbox_type & NETBOX_FAMILY_MASK) >> 16
 90        netbox_speed = (netbox_type & NETBOX_SPEED_MASK) >> 8
 91        netbox_channel = (netbox_type & NETBOX_CHANNEL_MASK)
 92        return {"series" : netbox_series, "family" : netbox_family, "speed" : netbox_speed, "channel" : netbox_channel}
 93
 94    def ip(self) -> str:
 95        """
 96        Returns the IP address of the Netbox using the device identifier of the netbox_card
 97
 98        Returns
 99        -------
100        str
101            The IP address of the Netbox
102        """
103        
104        return self.id_to_ip(self.netbox_card.device_identifier)
105
106    def sn(self) -> int:
107        """
108        Returns the serial number of the Netbox (see register 'SPC_NETBOX_SERIALNO' in chapter `Netbox` in the manual)
109
110        Returns
111        -------
112        int
113            The serial number of the Netbox
114        """
115
116        return self.netbox_card.get_i(SPC_NETBOX_SERIALNO)
117    
118    def production_date(self) -> int:
119        """
120        Returns the production date of the Netbox (see register 'SPC_NETBOX_PRODUCTIONDATE' in chapter `Netbox` in the manual)
121
122        Returns
123        -------
124        int
125            The production date of the Netbox
126        """
127
128        return self.netbox_card.get_i(SPC_NETBOX_PRODUCTIONDATE)
129    
130    def hw_version(self) -> int:
131        """
132        Returns the hardware version of the Netbox (see register 'SPC_NETBOX_HWVERSION' in chapter `Netbox` in the manual)
133
134        Returns
135        -------
136        int
137            The hardware version of the Netbox
138        """
139
140        return self.netbox_card.get_i(SPC_NETBOX_HWVERSION)
141    
142    def sw_version(self) -> int:
143        """
144        Returns the software version of the Netbox (see register 'SPC_NETBOX_SWVERSION' in chapter `Netbox` in the manual)
145
146        Returns
147        -------
148        int
149            The software version of the Netbox
150        """
151
152        return self.netbox_card.get_i(SPC_NETBOX_SWVERSION)
153
154    def features(self) -> int:
155        """
156        Returns the features of the Netbox (see register 'SPC_NETBOX_FEATURES' in chapter `Netbox` in the manual)
157
158        Returns
159        -------
160        int
161            The features of the Netbox
162        """
163        
164        return self.netbox_card.get_i(SPC_NETBOX_FEATURES)
165    
166    def custom(self) -> int:
167        """
168        Returns the custom code of the Netbox (see register 'SPC_NETBOX_CUSTOM' in chapter `Netbox` in the manual)
169
170        Returns
171        -------
172        int
173            The custom of the Netbox
174        """
175        return self.netbox_card.get_i(SPC_NETBOX_CUSTOM)
176
177    def wake_on_lan(self, mac : int):
178        """
179        Set the wake on lan for the Netbox (see register 'SPC_NETBOX_WAKEONLAN' in chapter `Netbox` in the manual)
180
181        Parameters
182        ----------
183        mac : int
184            The mac addresse of the Netbox to wake on lan
185        """
186        self.netbox_card.set_i(SPC_NETBOX_WAKEONLAN, mac)
187
188    def mac_address(self) -> int:
189        """
190        Returns the mac address of the Netbox (see register 'SPC_NETBOX_MACADDRESS' in chapter `Netbox` in the manual)
191
192        Returns
193        -------
194        int
195            The mac address of the Netbox
196        """
197        return self.netbox_card.get_i(SPC_NETBOX_MACADDRESS)
198    
199    def temperature(self) -> int:
200        """
201        Returns the temperature of the Netbox (see register 'SPC_NETBOX_TEMPERATURE' in chapter `Netbox` in the manual)
202
203        Returns
204        -------
205        int
206            The temperature of the Netbox
207        """
208        return self.netbox_card.get_i(SPC_NETBOX_TEMPERATURE)
209    
210    def shutdown(self):
211        """
212        Shutdown the Netbox (see register 'SPC_NETBOX_SHUTDOWN' in chapter `Netbox` in the manual)
213        """
214        self.netbox_card.set_i(SPC_NETBOX_SHUTDOWN, 0)
215
216    def restart(self):
217        """
218        Restart the Netbox (see register 'SPC_NETBOX_RESTART' in chapter `Netbox` in the manual)
219        """
220        self.netbox_card.set_i(SPC_NETBOX_RESTART, 0)
221    
222    def fan_speed(self, id : int) -> int:
223        """
224        Returns the fan speed of the Netbox (see register 'SPC_NETBOX_FANSPEED' in chapter `Netbox` in the manual)
225
226        Returns
227        -------
228        int
229            The fan speed of the Netbox
230        """
231        return self.netbox_card.get_i(SPC_NETBOX_FANSPEED0 + id)

A hardware class that controls a Netbox device

Parameters
  • netbox_card (Card): a card object that is the main card in the Netbox
  • netbox_number (int): the index of the netbox card in the list of cards
  • is_netbox (bool): a boolean that indicates if the card is a Netbox
Netbox( card_identifiers: list[str] = [], sync_identifier: str = '', find_sync: bool = False, **kwargs)
27    def __init__(self, card_identifiers : list[str] = [], sync_identifier : str = "", find_sync : bool = False, **kwargs) -> None:
28        """
29        Initialize the Netbox object with a list of card identifiers and a sync identifier
30
31        Parameters
32        ----------
33        card_identifiers : list[str] = []
34            a list of strings that represent the VISA strings of the cards
35        sync_identifier : str = ""
36            a string that represents the VISA string of the sync card
37        find_sync : bool = False
38            a boolean that indicates if the sync card should be found automatically
39        """
40
41        super().__init__(card_identifiers, sync_identifier, find_sync, **kwargs)
42
43        for id, card in enumerate(self.cards):
44            netbox_type = card.get_i(SPC_NETBOX_TYPE)
45            if netbox_type != 0:
46                self.netbox_card = card
47                self.netbox_number = id
48                self.is_netbox = True
49                break

Initialize the Netbox object with a list of card identifiers and a sync identifier

Parameters
  • card_identifiers (list[str] = []): a list of strings that represent the VISA strings of the cards
  • sync_identifier (str = ""): a string that represents the VISA string of the sync card
  • find_sync (bool = False): a boolean that indicates if the sync card should be found automatically
netbox_card: Card = None
netbox_number: int = -1
is_netbox: bool = False
def type(self) -> dict[int, int, int, int]:
77    def type(self) -> dict[int, int, int, int]:
78        """
79        Returns the type of the Netbox (see register 'SPC_NETBOX_TYPE' in chapter `Netbox` in the manual)
80
81        Returns
82        -------
83        dict[int, int, int, int]
84            A dictionary with the series, family, speed and number of channels of the Netbox
85        """
86
87        netbox_type = self.netbox_card.get_i(SPC_NETBOX_TYPE)
88        netbox_series = (netbox_type & NETBOX_SERIES_MASK) >> 24
89        netbox_family = (netbox_type & NETBOX_FAMILY_MASK) >> 16
90        netbox_speed = (netbox_type & NETBOX_SPEED_MASK) >> 8
91        netbox_channel = (netbox_type & NETBOX_CHANNEL_MASK)
92        return {"series" : netbox_series, "family" : netbox_family, "speed" : netbox_speed, "channel" : netbox_channel}

Returns the type of the Netbox (see register 'SPC_NETBOX_TYPE' in chapter Netbox in the manual)

Returns
  • dict[int, int, int, int]: A dictionary with the series, family, speed and number of channels of the Netbox
def ip(self) -> str:
 94    def ip(self) -> str:
 95        """
 96        Returns the IP address of the Netbox using the device identifier of the netbox_card
 97
 98        Returns
 99        -------
100        str
101            The IP address of the Netbox
102        """
103        
104        return self.id_to_ip(self.netbox_card.device_identifier)

Returns the IP address of the Netbox using the device identifier of the netbox_card

Returns
  • str: The IP address of the Netbox
def sn(self) -> int:
106    def sn(self) -> int:
107        """
108        Returns the serial number of the Netbox (see register 'SPC_NETBOX_SERIALNO' in chapter `Netbox` in the manual)
109
110        Returns
111        -------
112        int
113            The serial number of the Netbox
114        """
115
116        return self.netbox_card.get_i(SPC_NETBOX_SERIALNO)

Returns the serial number of the Netbox (see register 'SPC_NETBOX_SERIALNO' in chapter Netbox in the manual)

Returns
  • int: The serial number of the Netbox
def production_date(self) -> int:
118    def production_date(self) -> int:
119        """
120        Returns the production date of the Netbox (see register 'SPC_NETBOX_PRODUCTIONDATE' in chapter `Netbox` in the manual)
121
122        Returns
123        -------
124        int
125            The production date of the Netbox
126        """
127
128        return self.netbox_card.get_i(SPC_NETBOX_PRODUCTIONDATE)

Returns the production date of the Netbox (see register 'SPC_NETBOX_PRODUCTIONDATE' in chapter Netbox in the manual)

Returns
  • int: The production date of the Netbox
def hw_version(self) -> int:
130    def hw_version(self) -> int:
131        """
132        Returns the hardware version of the Netbox (see register 'SPC_NETBOX_HWVERSION' in chapter `Netbox` in the manual)
133
134        Returns
135        -------
136        int
137            The hardware version of the Netbox
138        """
139
140        return self.netbox_card.get_i(SPC_NETBOX_HWVERSION)

Returns the hardware version of the Netbox (see register 'SPC_NETBOX_HWVERSION' in chapter Netbox in the manual)

Returns
  • int: The hardware version of the Netbox
def sw_version(self) -> int:
142    def sw_version(self) -> int:
143        """
144        Returns the software version of the Netbox (see register 'SPC_NETBOX_SWVERSION' in chapter `Netbox` in the manual)
145
146        Returns
147        -------
148        int
149            The software version of the Netbox
150        """
151
152        return self.netbox_card.get_i(SPC_NETBOX_SWVERSION)

Returns the software version of the Netbox (see register 'SPC_NETBOX_SWVERSION' in chapter Netbox in the manual)

Returns
  • int: The software version of the Netbox
def features(self) -> int:
154    def features(self) -> int:
155        """
156        Returns the features of the Netbox (see register 'SPC_NETBOX_FEATURES' in chapter `Netbox` in the manual)
157
158        Returns
159        -------
160        int
161            The features of the Netbox
162        """
163        
164        return self.netbox_card.get_i(SPC_NETBOX_FEATURES)

Returns the features of the Netbox (see register 'SPC_NETBOX_FEATURES' in chapter Netbox in the manual)

Returns
  • int: The features of the Netbox
def custom(self) -> int:
166    def custom(self) -> int:
167        """
168        Returns the custom code of the Netbox (see register 'SPC_NETBOX_CUSTOM' in chapter `Netbox` in the manual)
169
170        Returns
171        -------
172        int
173            The custom of the Netbox
174        """
175        return self.netbox_card.get_i(SPC_NETBOX_CUSTOM)

Returns the custom code of the Netbox (see register 'SPC_NETBOX_CUSTOM' in chapter Netbox in the manual)

Returns
  • int: The custom of the Netbox
def wake_on_lan(self, mac: int):
177    def wake_on_lan(self, mac : int):
178        """
179        Set the wake on lan for the Netbox (see register 'SPC_NETBOX_WAKEONLAN' in chapter `Netbox` in the manual)
180
181        Parameters
182        ----------
183        mac : int
184            The mac addresse of the Netbox to wake on lan
185        """
186        self.netbox_card.set_i(SPC_NETBOX_WAKEONLAN, mac)

Set the wake on lan for the Netbox (see register 'SPC_NETBOX_WAKEONLAN' in chapter Netbox in the manual)

Parameters
  • mac (int): The mac addresse of the Netbox to wake on lan
def mac_address(self) -> int:
188    def mac_address(self) -> int:
189        """
190        Returns the mac address of the Netbox (see register 'SPC_NETBOX_MACADDRESS' in chapter `Netbox` in the manual)
191
192        Returns
193        -------
194        int
195            The mac address of the Netbox
196        """
197        return self.netbox_card.get_i(SPC_NETBOX_MACADDRESS)

Returns the mac address of the Netbox (see register 'SPC_NETBOX_MACADDRESS' in chapter Netbox in the manual)

Returns
  • int: The mac address of the Netbox
def temperature(self) -> int:
199    def temperature(self) -> int:
200        """
201        Returns the temperature of the Netbox (see register 'SPC_NETBOX_TEMPERATURE' in chapter `Netbox` in the manual)
202
203        Returns
204        -------
205        int
206            The temperature of the Netbox
207        """
208        return self.netbox_card.get_i(SPC_NETBOX_TEMPERATURE)

Returns the temperature of the Netbox (see register 'SPC_NETBOX_TEMPERATURE' in chapter Netbox in the manual)

Returns
  • int: The temperature of the Netbox
def shutdown(self):
210    def shutdown(self):
211        """
212        Shutdown the Netbox (see register 'SPC_NETBOX_SHUTDOWN' in chapter `Netbox` in the manual)
213        """
214        self.netbox_card.set_i(SPC_NETBOX_SHUTDOWN, 0)

Shutdown the Netbox (see register 'SPC_NETBOX_SHUTDOWN' in chapter Netbox in the manual)

def restart(self):
216    def restart(self):
217        """
218        Restart the Netbox (see register 'SPC_NETBOX_RESTART' in chapter `Netbox` in the manual)
219        """
220        self.netbox_card.set_i(SPC_NETBOX_RESTART, 0)

Restart the Netbox (see register 'SPC_NETBOX_RESTART' in chapter Netbox in the manual)

def fan_speed(self, id: int) -> int:
222    def fan_speed(self, id : int) -> int:
223        """
224        Returns the fan speed of the Netbox (see register 'SPC_NETBOX_FANSPEED' in chapter `Netbox` in the manual)
225
226        Returns
227        -------
228        int
229            The fan speed of the Netbox
230        """
231        return self.netbox_card.get_i(SPC_NETBOX_FANSPEED0 + id)

Returns the fan speed of the Netbox (see register 'SPC_NETBOX_FANSPEED' in chapter Netbox in the manual)

Returns
  • int: The fan speed of the Netbox
class CardFunctionality:
 6class CardFunctionality:
 7    """
 8    A prototype class for card specific functionality that needs it's own namespace
 9    """
10    card : Card
11    function_type = 0
12
13    def __init__(self, card : Card, *args, **kwargs) -> None:
14        """
15        Takes a Card object that is used by the functionality
16
17        Parameters
18        ----------
19        card : Card
20            a Card object on which the functionality works
21        """
22        self.card = card
23        self.function_type = self.card.function_type()
24    
25    
26    # Check if a card was found
27    def __bool__(self) -> bool:
28        """
29        Check for a connection to the active card
30    
31        Returns
32        -------
33        bool
34            True for an active connection and false otherwise
35        
36        """
37        
38        return bool(self.card)

A prototype class for card specific functionality that needs it's own namespace

CardFunctionality(card: Card, *args, **kwargs)
13    def __init__(self, card : Card, *args, **kwargs) -> None:
14        """
15        Takes a Card object that is used by the functionality
16
17        Parameters
18        ----------
19        card : Card
20            a Card object on which the functionality works
21        """
22        self.card = card
23        self.function_type = self.card.function_type()

Takes a Card object that is used by the functionality

Parameters
  • card (Card): a Card object on which the functionality works
card: Card
function_type = 0
class Channels:
494class Channels:
495    """
496    a higher-level abstraction of the CardFunctionality class to implement the Card's channel settings
497    """
498    
499    cards : list[Card] = []
500    channels : list[Channel] = []
501    num_channels : list[int] = []
502
503    def __init__(self, card : Card = None, card_enable : int = None, stack : CardStack = None, stack_enable : list[int] = None) -> None:
504        """
505        Constructor of the Channels class
506
507        Parameters
508        ----------
509        card : Card = None
510            The card to be used
511        card_enable : int = None
512            The bitmask to enable specific channels
513        stack : CardStack = None
514            The card stack to be used
515        stack_enable : list[int] = None
516            The list of bitmasks to enable specific channels
517
518        Raises
519        ------
520        SpcmException
521            No card or card stack provided
522        """
523
524        self.cards = []
525        self.channels = []
526        self.num_channels = []
527        if card is not None:
528            self.cards.append(card)
529            if card_enable is not None:
530                self.channels_enable(enable_list=[card_enable])
531            else:
532                self.channels_enable(enable_all=True)
533        elif stack is not None:
534            self.cards = stack.cards
535            if stack_enable is not None:
536                self.channels_enable(enable_list=stack_enable)
537            else:
538                self.channels_enable(enable_all=True)
539        else:
540            raise SpcmException(text="No card or card stack provided")
541
542    def __str__(self) -> str:
543        """
544        String representation of the Channels class
545    
546        Returns
547        -------
548        str
549            String representation of the Channels class
550        """
551        
552        return f"Channels()"
553    
554    __repr__ = __str__
555
556    def __iter__(self) -> "Channels":
557        """Define this class as an iterator"""
558        return self
559    
560    def __getitem__(self, index : int) -> Channel:
561        """
562        This method is called to access the channel by index
563
564        Parameters
565        ----------
566        index : int
567            The index of the channel
568        
569        Returns
570        -------
571        Channel
572            the channel at the specific index
573        """
574
575        
576        return self.channels[index]
577    
578    _channel_iterator_index = -1
579    def __next__(self) -> Channel:
580        """
581        This method is called when the next element is requested from the iterator
582
583        Returns
584        -------
585        Channel
586            the next available channel
587        
588        Raises
589        ------
590        StopIteration
591        """
592        self._channel_iterator_index += 1
593        if self._channel_iterator_index >= len(self.channels):
594            self._channel_iterator_index = -1
595            raise StopIteration
596        return self.channels[self._channel_iterator_index]
597    
598    def __len__(self) -> int:
599        """Returns the number of channels"""
600        return len(self.channels)
601    
602    def write_setup(self) -> None:
603        """Write the setup to the card"""
604        self.card.write_setup()
605    
606    def channels_enable(self, enable_list : list[int] = None, enable_all : bool = False) -> int:
607        """
608        Enables or disables the channels of all the available cards (see register `SPC_CHENABLE` in the manual)
609    
610        Parameters
611        ----------
612        enable_list : list[int] = None
613            A list of channels bitmasks to be enable or disable specific channels
614        enable_all : bool = False
615            Enable all the channels
616        
617        Returns
618        -------
619        int
620            A list with items that indicate for each card the number of channels that are enabled, or True to enable all channels.
621        """
622
623        self.channels = []
624        self.num_channels = []
625        num_channels = 0
626
627        if enable_all:
628            for card in self.cards:
629                num_channels = card.num_channels()
630                card.set_i(SPC_CHENABLE, (1 << num_channels) - 1)
631                num_channels = card.get_i(SPC_CHCOUNT)
632                self.num_channels.append(num_channels)
633                for i in range(num_channels):
634                    self.channels.append(Channel(i, i, card))
635        elif enable_list is not None:
636            for enable, card in zip(enable_list, self.cards):
637                card.set_i(SPC_CHENABLE, enable)
638                num_channels = card.get_i(SPC_CHCOUNT)
639                self.num_channels.append(num_channels)
640                counter = 0
641                for i in range(len(bin(enable))):
642                    if (enable >> i) & 1:
643                        self.channels.append(Channel(i, counter, card))
644                        counter += 1
645        return sum(self.num_channels)
646        
647    # def __getattribute__(self, name):
648    #     # print("Calling __getattr__: {}".format(name))
649    #     if hasattr(Channel, name):
650    #         def wrapper(*args, **kw):
651    #             for channel in self.channels:
652    #                 getattr(channel, name)(*args, **kw)
653    #         return wrapper
654    #     else:
655    #         return object.__getattribute__(self, name)
656
657    def enable(self, enable : bool) -> None:
658        """
659        Enables or disables the analog front-end of all channels of the card (see register `SPC_ENABLEOUT` in the manual)
660    
661        Parameters
662        ----------
663        enable : bool
664            Turn-on (True) or off (False) the spezific channel
665        """
666
667        for channel in self.channels:
668            channel.enable(enable)
669    enable_out = enable
670    
671    def path(self, value : int) -> None:
672        """
673        Sets the input path of the analog front-end of all channels of the card (see register `SPC_PATH` in the manual)
674    
675        Parameters
676        ----------
677        value : int
678            The input path of the specific channel
679        """
680
681        for channel in self.channels:
682            channel.path(value)
683    
684    def amp(self, value : int) -> None:
685        """
686        Sets the output/input range (amplitude) of the analog front-end of all channels of the card in mV (see register `SPC_AMP` in the manual)
687    
688        Parameters
689        ----------
690        value : int
691            The output range (amplitude) of all channels in millivolts
692        """
693
694        for channel in self.channels:
695            channel.amp(value)
696    
697    def offset(self, value : int) -> None:
698        """
699        Sets the offset of the analog front-end of all channels of the card in mV (see register `SPC_OFFSET` in the manual)
700    
701        Parameters
702        ----------
703        value : int
704            The offset of all channels in millivolts
705        """
706
707        for channel in self.channels:
708            channel.offset(value)
709
710    def termination(self, value : int) -> None:
711        """
712        Sets the termination of the analog front-end of all channels of the card (see register `SPC_50OHM` in the manual)
713    
714        Parameters
715        ----------
716        value : int
717            The termination of all channels
718        """
719
720        for channel in self.channels:
721            channel.termination(value)
722    
723    def coupling(self, value : int) -> None:
724        """
725        Sets the coupling of the analog front-end of all channels of the card (see register `SPC_ACDC` in the manual)
726    
727        Parameters
728        ----------
729        value : int
730            The coupling of all channels
731        """
732
733        for channel in self.channels:
734            channel.coupling(value)
735    
736    def coupling_offset_compensation(self, value : int) -> None:
737        """
738        Sets the coupling offset compensation of the analog front-end of all channels of the card (see register `SPC_ACDC_OFFS_COMPENSATION` in the manual)
739    
740        Parameters
741        ----------
742        value : int
743            The coupling offset compensation of all channels
744        """
745
746        for channel in self.channels:
747            channel.coupling_offset_compensation(value)
748    
749    def filter(self, value : int) -> None:
750        """
751        Sets the filter of the analog front-end of all channels of the card (see register `SPC_FILTER` in the manual)
752    
753        Parameters
754        ----------
755        value : int
756            The filter of all channels
757        """
758
759        for channel in self.channels:
760            channel.filter(value)
761    
762    def stop_level(self, value : int) -> None:
763        """
764        Usually the used outputs of the analog generation boards are set to zero level after replay. 
765        This is in most cases adequate. In some cases it can be necessary to hold the last sample,
766        to output the maximum positive level or maximum negative level after replay. The stoplevel will 
767        stay on the defined level until the next output has been made. With this function
768        you can define the behavior after replay (see register `SPC_CH0_STOPLEVEL` in the manual)
769    
770        Parameters
771        ----------
772        value : int
773            The wanted stop behaviour:
774        """
775
776        for channel in self.channels:
777            channel.stop_level(value)
778
779    def custom_stop(self, value : int) -> None:
780        """
781        Allows to define a 16bit wide custom level per channel for the analog output to enter in pauses. The sample format is 
782        exactly the same as during replay, as described in the „sample format“ section.
783        When synchronous digital bits are replayed along, the custom level must include these as well and therefore allows to 
784        set a custom level for each multi-purpose line separately. (see register `SPC_CH0_CUSTOM_STOP` in the manual)
785    
786        Parameters
787        ----------
788        value : int
789            The custom stop value
790        """
791    
792        for channel in self.channels:
793            channel.custom_stop(value)
794    
795    def output_load(self, value : pint.Quantity) -> None:
796        """
797        Sets the electrical load of the user system connect the channel of the card. This is important for the correct
798        calculation of the output power. Typically, the load would be 50 Ohms, but it can be different.
799
800        Parameters
801        ----------
802        value : pint.Quantity
803            The electrical load connected by the user to the specific channel
804        """
805        for channel in self.channels:
806            channel.output_load(value)
807    
808    def ch_mask(self) -> int:
809        """
810        Gets mask for the "or"- or "and"-mask
811
812        Returns
813        -------
814        int
815            The mask for the "or"- or "and"-mask
816        """
817
818        return sum([channel.ch_mask() for channel in self.channels])

a higher-level abstraction of the CardFunctionality class to implement the Card's channel settings

Channels( card: Card = None, card_enable: int = None, stack: CardStack = None, stack_enable: list[int] = None)
503    def __init__(self, card : Card = None, card_enable : int = None, stack : CardStack = None, stack_enable : list[int] = None) -> None:
504        """
505        Constructor of the Channels class
506
507        Parameters
508        ----------
509        card : Card = None
510            The card to be used
511        card_enable : int = None
512            The bitmask to enable specific channels
513        stack : CardStack = None
514            The card stack to be used
515        stack_enable : list[int] = None
516            The list of bitmasks to enable specific channels
517
518        Raises
519        ------
520        SpcmException
521            No card or card stack provided
522        """
523
524        self.cards = []
525        self.channels = []
526        self.num_channels = []
527        if card is not None:
528            self.cards.append(card)
529            if card_enable is not None:
530                self.channels_enable(enable_list=[card_enable])
531            else:
532                self.channels_enable(enable_all=True)
533        elif stack is not None:
534            self.cards = stack.cards
535            if stack_enable is not None:
536                self.channels_enable(enable_list=stack_enable)
537            else:
538                self.channels_enable(enable_all=True)
539        else:
540            raise SpcmException(text="No card or card stack provided")

Constructor of the Channels class

Parameters
  • card (Card = None): The card to be used
  • card_enable (int = None): The bitmask to enable specific channels
  • stack (CardStack = None): The card stack to be used
  • stack_enable (list[int] = None): The list of bitmasks to enable specific channels
Raises
  • SpcmException: No card or card stack provided
cards: list[Card] = []
channels: list[Channel] = []
num_channels: list[int] = []
def write_setup(self) -> None:
602    def write_setup(self) -> None:
603        """Write the setup to the card"""
604        self.card.write_setup()

Write the setup to the card

def channels_enable(self, enable_list: list[int] = None, enable_all: bool = False) -> int:
606    def channels_enable(self, enable_list : list[int] = None, enable_all : bool = False) -> int:
607        """
608        Enables or disables the channels of all the available cards (see register `SPC_CHENABLE` in the manual)
609    
610        Parameters
611        ----------
612        enable_list : list[int] = None
613            A list of channels bitmasks to be enable or disable specific channels
614        enable_all : bool = False
615            Enable all the channels
616        
617        Returns
618        -------
619        int
620            A list with items that indicate for each card the number of channels that are enabled, or True to enable all channels.
621        """
622
623        self.channels = []
624        self.num_channels = []
625        num_channels = 0
626
627        if enable_all:
628            for card in self.cards:
629                num_channels = card.num_channels()
630                card.set_i(SPC_CHENABLE, (1 << num_channels) - 1)
631                num_channels = card.get_i(SPC_CHCOUNT)
632                self.num_channels.append(num_channels)
633                for i in range(num_channels):
634                    self.channels.append(Channel(i, i, card))
635        elif enable_list is not None:
636            for enable, card in zip(enable_list, self.cards):
637                card.set_i(SPC_CHENABLE, enable)
638                num_channels = card.get_i(SPC_CHCOUNT)
639                self.num_channels.append(num_channels)
640                counter = 0
641                for i in range(len(bin(enable))):
642                    if (enable >> i) & 1:
643                        self.channels.append(Channel(i, counter, card))
644                        counter += 1
645        return sum(self.num_channels)

Enables or disables the channels of all the available cards (see register SPC_CHENABLE in the manual)

Parameters
  • enable_list (list[int] = None): A list of channels bitmasks to be enable or disable specific channels
  • enable_all (bool = False): Enable all the channels
Returns
  • int: A list with items that indicate for each card the number of channels that are enabled, or True to enable all channels.
def enable(self, enable: bool) -> None:
657    def enable(self, enable : bool) -> None:
658        """
659        Enables or disables the analog front-end of all channels of the card (see register `SPC_ENABLEOUT` in the manual)
660    
661        Parameters
662        ----------
663        enable : bool
664            Turn-on (True) or off (False) the spezific channel
665        """
666
667        for channel in self.channels:
668            channel.enable(enable)

Enables or disables the analog front-end of all channels of the card (see register SPC_ENABLEOUT in the manual)

Parameters
  • enable (bool): Turn-on (True) or off (False) the spezific channel
def enable_out(self, enable: bool) -> None:
657    def enable(self, enable : bool) -> None:
658        """
659        Enables or disables the analog front-end of all channels of the card (see register `SPC_ENABLEOUT` in the manual)
660    
661        Parameters
662        ----------
663        enable : bool
664            Turn-on (True) or off (False) the spezific channel
665        """
666
667        for channel in self.channels:
668            channel.enable(enable)

Enables or disables the analog front-end of all channels of the card (see register SPC_ENABLEOUT in the manual)

Parameters
  • enable (bool): Turn-on (True) or off (False) the spezific channel
def path(self, value: int) -> None:
671    def path(self, value : int) -> None:
672        """
673        Sets the input path of the analog front-end of all channels of the card (see register `SPC_PATH` in the manual)
674    
675        Parameters
676        ----------
677        value : int
678            The input path of the specific channel
679        """
680
681        for channel in self.channels:
682            channel.path(value)

Sets the input path of the analog front-end of all channels of the card (see register SPC_PATH in the manual)

Parameters
  • value (int): The input path of the specific channel
def amp(self, value: int) -> None:
684    def amp(self, value : int) -> None:
685        """
686        Sets the output/input range (amplitude) of the analog front-end of all channels of the card in mV (see register `SPC_AMP` in the manual)
687    
688        Parameters
689        ----------
690        value : int
691            The output range (amplitude) of all channels in millivolts
692        """
693
694        for channel in self.channels:
695            channel.amp(value)

Sets the output/input range (amplitude) of the analog front-end of all channels of the card in mV (see register SPC_AMP in the manual)

Parameters
  • value (int): The output range (amplitude) of all channels in millivolts
def offset(self, value: int) -> None:
697    def offset(self, value : int) -> None:
698        """
699        Sets the offset of the analog front-end of all channels of the card in mV (see register `SPC_OFFSET` in the manual)
700    
701        Parameters
702        ----------
703        value : int
704            The offset of all channels in millivolts
705        """
706
707        for channel in self.channels:
708            channel.offset(value)

Sets the offset of the analog front-end of all channels of the card in mV (see register SPC_OFFSET in the manual)

Parameters
  • value (int): The offset of all channels in millivolts
def termination(self, value: int) -> None:
710    def termination(self, value : int) -> None:
711        """
712        Sets the termination of the analog front-end of all channels of the card (see register `SPC_50OHM` in the manual)
713    
714        Parameters
715        ----------
716        value : int
717            The termination of all channels
718        """
719
720        for channel in self.channels:
721            channel.termination(value)

Sets the termination of the analog front-end of all channels of the card (see register SPC_50OHM in the manual)

Parameters
  • value (int): The termination of all channels
def coupling(self, value: int) -> None:
723    def coupling(self, value : int) -> None:
724        """
725        Sets the coupling of the analog front-end of all channels of the card (see register `SPC_ACDC` in the manual)
726    
727        Parameters
728        ----------
729        value : int
730            The coupling of all channels
731        """
732
733        for channel in self.channels:
734            channel.coupling(value)

Sets the coupling of the analog front-end of all channels of the card (see register SPC_ACDC in the manual)

Parameters
  • value (int): The coupling of all channels
def coupling_offset_compensation(self, value: int) -> None:
736    def coupling_offset_compensation(self, value : int) -> None:
737        """
738        Sets the coupling offset compensation of the analog front-end of all channels of the card (see register `SPC_ACDC_OFFS_COMPENSATION` in the manual)
739    
740        Parameters
741        ----------
742        value : int
743            The coupling offset compensation of all channels
744        """
745
746        for channel in self.channels:
747            channel.coupling_offset_compensation(value)

Sets the coupling offset compensation of the analog front-end of all channels of the card (see register SPC_ACDC_OFFS_COMPENSATION in the manual)

Parameters
  • value (int): The coupling offset compensation of all channels
def filter(self, value: int) -> None:
749    def filter(self, value : int) -> None:
750        """
751        Sets the filter of the analog front-end of all channels of the card (see register `SPC_FILTER` in the manual)
752    
753        Parameters
754        ----------
755        value : int
756            The filter of all channels
757        """
758
759        for channel in self.channels:
760            channel.filter(value)

Sets the filter of the analog front-end of all channels of the card (see register SPC_FILTER in the manual)

Parameters
  • value (int): The filter of all channels
def stop_level(self, value: int) -> None:
762    def stop_level(self, value : int) -> None:
763        """
764        Usually the used outputs of the analog generation boards are set to zero level after replay. 
765        This is in most cases adequate. In some cases it can be necessary to hold the last sample,
766        to output the maximum positive level or maximum negative level after replay. The stoplevel will 
767        stay on the defined level until the next output has been made. With this function
768        you can define the behavior after replay (see register `SPC_CH0_STOPLEVEL` in the manual)
769    
770        Parameters
771        ----------
772        value : int
773            The wanted stop behaviour:
774        """
775
776        for channel in self.channels:
777            channel.stop_level(value)

Usually the used outputs of the analog generation boards are set to zero level after replay. This is in most cases adequate. In some cases it can be necessary to hold the last sample, to output the maximum positive level or maximum negative level after replay. The stoplevel will stay on the defined level until the next output has been made. With this function you can define the behavior after replay (see register SPC_CH0_STOPLEVEL in the manual)

Parameters
  • value (int): The wanted stop behaviour:
def custom_stop(self, value: int) -> None:
779    def custom_stop(self, value : int) -> None:
780        """
781        Allows to define a 16bit wide custom level per channel for the analog output to enter in pauses. The sample format is 
782        exactly the same as during replay, as described in the „sample format“ section.
783        When synchronous digital bits are replayed along, the custom level must include these as well and therefore allows to 
784        set a custom level for each multi-purpose line separately. (see register `SPC_CH0_CUSTOM_STOP` in the manual)
785    
786        Parameters
787        ----------
788        value : int
789            The custom stop value
790        """
791    
792        for channel in self.channels:
793            channel.custom_stop(value)

Allows to define a 16bit wide custom level per channel for the analog output to enter in pauses. The sample format is exactly the same as during replay, as described in the „sample format“ section. When synchronous digital bits are replayed along, the custom level must include these as well and therefore allows to set a custom level for each multi-purpose line separately. (see register SPC_CH0_CUSTOM_STOP in the manual)

Parameters
  • value (int): The custom stop value
def output_load(self, value: pint.registry.Quantity) -> None:
795    def output_load(self, value : pint.Quantity) -> None:
796        """
797        Sets the electrical load of the user system connect the channel of the card. This is important for the correct
798        calculation of the output power. Typically, the load would be 50 Ohms, but it can be different.
799
800        Parameters
801        ----------
802        value : pint.Quantity
803            The electrical load connected by the user to the specific channel
804        """
805        for channel in self.channels:
806            channel.output_load(value)

Sets the electrical load of the user system connect the channel of the card. This is important for the correct calculation of the output power. Typically, the load would be 50 Ohms, but it can be different.

Parameters
  • value (pint.Quantity): The electrical load connected by the user to the specific channel
def ch_mask(self) -> int:
808    def ch_mask(self) -> int:
809        """
810        Gets mask for the "or"- or "and"-mask
811
812        Returns
813        -------
814        int
815            The mask for the "or"- or "and"-mask
816        """
817
818        return sum([channel.ch_mask() for channel in self.channels])

Gets mask for the "or"- or "and"-mask

Returns
  • int: The mask for the "or"- or "and"-mask
class Channel:
 18class Channel:
 19    """A class to represent a channel of a card only used inside the Channels class in the list of channels"""
 20
 21    card : Card = None
 22    index : int = 0
 23    data_index : int = 0
 24
 25    _conversion_amp : pint.Quantity = None
 26    _conversion_offset : pint.Quantity = None
 27    _output_load : pint.Quantity = None
 28    _series_impedance : pint.Quantity = None
 29
 30    def __init__(self, index : int, data_index : int, card : Card) -> None:
 31        """
 32        Constructor of the Channel class
 33    
 34        Parameters
 35        ----------
 36        index : int
 37            The index of the channel
 38        card : Card
 39            The card of the channel
 40        """
 41
 42        self.card = card
 43        self.index = index
 44        self.data_index = data_index
 45        self._conversion_amp = None
 46        self._conversion_offset = 0 * units.percent
 47        self._output_load = 50 * units.ohm
 48        self._series_impedance = 50 * units.ohm
 49    
 50    def __str__(self) -> str:
 51        """
 52        String representation of the Channel class
 53    
 54        Returns
 55        -------
 56        str
 57            String representation of the Channel class
 58        """
 59        
 60        return f"Channel {self.index}"
 61    
 62    __repr__ = __str__
 63
 64    def __int__(self) -> int:
 65        """
 66        The Channel object acts like an int and returns the index of the channel and can also be used as the index in an array
 67
 68        Returns
 69        -------
 70        int
 71            The index of the channel
 72        """
 73        return self.data_index
 74    __index__ = __int__
 75    
 76    def __add__(self, other):
 77        """
 78        The Channel object again acts like an int and returns the index of the channel plus the other value
 79        
 80        Parameters
 81        ----------
 82        other : int or float
 83            The value to be added to the index of the channel
 84        
 85        Returns
 86        -------
 87        int or float
 88            The index of the channel plus the other value
 89        """
 90        return self.index + other
 91    
 92    def enable(self, enable : bool = None) -> bool:
 93        """
 94        Enables the analog front-end of the channel of the card (see register `SPC_ENABLEOUT` in the manual)
 95    
 96        Parameters
 97        ----------
 98        enable : bool
 99            Turn-on (True) or off (False) the spezific channel
100
101        Returns
102        -------
103        bool
104            The enable state of the specific channel
105        """
106
107        if enable is not None:
108            self.card.set_i(SPC_ENABLEOUT0 + (SPC_ENABLEOUT1 - SPC_ENABLEOUT0) * self.index, int(enable))
109        return bool(self.card.get_i(SPC_ENABLEOUT0 + (SPC_ENABLEOUT1 - SPC_ENABLEOUT0) * self.index))
110    enable_out = enable
111    
112    def path(self, value : int = None) -> int:
113        """
114        Sets the input path of the channel of the card (see register `SPC_PATH0` in the manual)
115    
116        Parameters
117        ----------
118        value : int
119            The input path of the specific channel
120        
121        Returns
122        -------
123        int
124            The input path of the specific channel
125        """
126
127        if value is not None:
128            self.card.set_i(SPC_PATH0 + (SPC_PATH1 - SPC_PATH0) * self.index, value)
129        return self.card.get_i(SPC_PATH0 + (SPC_PATH1 - SPC_PATH0) * self.index)
130    
131    def amp(self, value : int = None, return_unit = None) -> int:
132        """
133        Sets the output/input range (amplitude) of the analog front-end of the channel of the card in mV (see register `SPC_AMP` in the manual)
134    
135        Parameters
136        ----------
137        value : int
138            The output range (amplitude) of the specific channel in millivolts
139        unit : pint.Unit = None
140            The unit of the return value
141            
142        Returns
143        -------
144        int | pint.Quantity
145            The output range (amplitude) of the specific channel in millivolts or the unit specified
146        """
147
148        if value is not None:
149            if isinstance(value, pint.Quantity):
150                value = self.voltage_conversion(value)
151            self._conversion_amp = UnitConversion.force_unit(value, units.mV)
152            value = UnitConversion.convert(value, units.mV, int)
153            self.card.set_i(SPC_AMP0 + (SPC_AMP1 - SPC_AMP0) * self.index, value)
154        value = self.card.get_i(SPC_AMP0 + (SPC_AMP1 - SPC_AMP0) * self.index)
155        value = UnitConversion.to_unit(value * units.mV, return_unit)
156        return value
157
158    def offset(self, value : int = None, return_unit = None) -> int:
159        """
160        Sets the offset of the analog front-end of the channel of the card in % of the full range o rmV (see register `SPC_OFFS0` in the manual)
161        If the value is given and has a unit, then this unit is converted to the unit of the card (mV or %)
162    
163        Parameters
164        ----------
165        value : int | pint.Quantity = None
166            The offset of the specific channel as integer in % or as a Quantity in % or mV
167        unit : pint.Unit = None
168            The unit of the return value
169            
170        Returns
171        -------
172        int | pint.Quantity
173            The offset of the specific channel in % or the unit specified by return_unit
174        """
175
176        # Analog in cards are programmed in percent of the full range and analog output cards in mV (in the M2p, M4i/x and M5i families)
177        card_unit = 1
178        fnc_type = self.card.function_type()
179        if fnc_type == SPCM_TYPE_AI:
180            card_unit = units.percent
181        elif fnc_type == SPCM_TYPE_AO:
182            card_unit = units.mV
183
184        if value is not None:
185            # The user gives a value as a Quantity
186            if isinstance(value, pint.Quantity):
187                if fnc_type == SPCM_TYPE_AO:
188                    # The card expects a value in mV
189                    if value.check('[]'):
190                        # Convert from percent to mV
191                        value = (value * self._conversion_amp).to(card_unit)
192                    else:
193                        value = value.to(card_unit)
194                elif fnc_type == SPCM_TYPE_AI:
195                    # The card expects a value in percent
196                    if value.check('[electric_potential]'):
197                        # Convert from mV to percent
198                        value = (value / self._conversion_amp).to(card_unit)
199                    else:
200                        value = value.to(card_unit)
201            else:
202                # Value is given as a number
203                pass
204
205            value = UnitConversion.convert(value, card_unit, int)
206            self.card.set_i(SPC_OFFS0 + (SPC_OFFS1 - SPC_OFFS0) * self.index, value)
207        
208        return_value = self.card.get_i(SPC_OFFS0 + (SPC_OFFS1 - SPC_OFFS0) * self.index)
209        # Turn the return value into a quantity
210        return_quantity = UnitConversion.to_unit(return_value, return_unit)
211        # Save the conversion offset to be able to convert the data to a quantity with the correct unit
212        self._conversion_offset = UnitConversion.force_unit(return_value, card_unit)
213        return return_quantity
214    
215    def convert_data(self, data : npt.NDArray, return_unit : pint.Unit = units.mV) -> npt.NDArray:
216        """
217        Converts the data to the correct unit in units of electrical potential
218        
219        Parameters
220        ----------
221        data : numpy.ndarray
222            The data to be converted
223        return_unit : pint.Unit = None
224            The unit of the return value
225            
226        Returns
227        -------
228        numpy.ndarray
229            The converted data in units of electrical potential
230        """
231
232        max_value = self.card.max_sample_value()
233        if self._conversion_offset.check('[]'):
234            return_data = (data / max_value - self._conversion_offset) * self._conversion_amp
235        else:
236            return_data = (data / max_value) * self._conversion_amp - self._conversion_offset
237        return_data = UnitConversion.to_unit(return_data, return_unit)
238        return return_data
239    
240    def reconvert_data(self, data : npt.NDArray) -> npt.NDArray:
241        """
242        Convert data with units back to integer values in units of electrical potential
243        
244        Parameters
245        ----------
246        data : numpy.ndarray
247            The data to be reconverted
248            
249        Returns
250        -------
251        numpy.ndarray
252            The reconverted data as integer in mV
253        """
254
255        if self._conversion_offset.check('[]'):
256            return_data = int((data / self._conversion_amp + self._conversion_offset) * self.card.max_sample_value())
257        else:
258            return_data = int(((data + self._conversion_offset) / self._conversion_amp) * self.card.max_sample_value())
259        return return_data
260
261    def termination(self, value : int) -> None:
262        """
263        Sets the termination of the analog front-end of the channel of the card (see register `SPC_50OHM0` in the manual)
264    
265        Parameters
266        ----------
267        value : int | bool
268            The termination of the specific channel
269        """
270
271        self.card.set_i(SPC_50OHM0 + (SPC_50OHM1 - SPC_50OHM0) * self.index, int(value))
272
273    def get_termination(self) -> int:
274        """
275        Gets the termination of the analog front-end of the channel of the card (see register `SPC_50OHM0` in the manual)
276            
277        Returns
278        -------
279        int
280            The termination of the specific channel
281        """
282
283        return self.card.get_i(SPC_50OHM0 + (SPC_50OHM1 - SPC_50OHM0) * self.index)
284    
285    def coupling(self, value : int = None) -> int:
286        """
287        Sets the coupling of the analog front-end of the channel of the card (see register `SPC_ACDC0` in the manual)
288    
289        Parameters
290        ----------
291        value : int
292            The coupling of the specific channel
293            
294        Returns
295        -------
296        int
297            The coupling of the specific channel
298        """
299
300        if value is not None:
301            self.card.set_i(SPC_ACDC0 + (SPC_ACDC1 - SPC_ACDC0) * self.index, value)
302        return self.card.get_i(SPC_ACDC0 + (SPC_ACDC1 - SPC_ACDC0) * self.index)
303    
304    def coupling_offset_compensation(self, value : int = None) -> int:
305        """
306        Enables or disables the coupling offset compensation of the analog front-end of the channel of the card (see register `SPC_ACDC_OFFS_COMPENSATION0` in the manual)
307    
308        Parameters
309        ----------
310        value : int
311            Enables the coupling offset compensation of the specific channel
312            
313        Returns
314        -------
315        int
316            return if the coupling offset compensation of the specific channel is enabled ("1") or disabled ("0")
317        """
318
319        if value is not None:
320            self.card.set_i(SPC_ACDC_OFFS_COMPENSATION0 + (SPC_ACDC_OFFS_COMPENSATION1 - SPC_ACDC_OFFS_COMPENSATION0) * self.index, value)
321        return self.card.get_i(SPC_ACDC_OFFS_COMPENSATION0 + (SPC_ACDC_OFFS_COMPENSATION1 - SPC_ACDC_OFFS_COMPENSATION0) * self.index)
322    
323    def filter(self, value : int = None) -> int:
324        """
325        Sets the filter of the analog front-end of the channel of the card (see register `SPC_FILTER0` in the manual)
326    
327        Parameters
328        ----------
329        value : int
330            The filter of the specific channel
331            
332        Returns
333        -------
334        int
335            The filter of the specific channel
336        """
337
338        if value is not None:
339            self.card.set_i(SPC_FILTER0 + (SPC_FILTER1 - SPC_FILTER0) * self.index, value)
340        return self.card.get_i(SPC_FILTER0 + (SPC_FILTER1 - SPC_FILTER0) * self.index)
341    
342    def stop_level(self, value : int = None) -> int:
343        """
344        Usually the used outputs of the analog generation boards are set to zero level after replay. 
345        This is in most cases adequate. In some cases it can be necessary to hold the last sample,
346        to output the maximum positive level or maximum negative level after replay. The stoplevel will 
347        stay on the defined level until the next output has been made. With this function
348        you can define the behavior after replay (see register `SPC_CH0_STOPLEVEL` in the manual)
349    
350        Parameters
351        ----------
352        value : int
353            The wanted stop behaviour
354
355        Returns
356        -------
357        int
358            The stop behaviour of the specific channel
359        """
360
361        if value is not None:
362            self.card.set_i(SPC_CH0_STOPLEVEL + self.index * (SPC_CH1_STOPLEVEL - SPC_CH0_STOPLEVEL), value)
363        return self.card.get_i(SPC_CH0_STOPLEVEL + self.index * (SPC_CH1_STOPLEVEL - SPC_CH0_STOPLEVEL))
364
365    def custom_stop(self, value : int = None) -> int:
366        """
367        Allows to define a 16bit wide custom level per channel for the analog output to enter in pauses. The sample format is 
368        exactly the same as during replay, as described in the „sample format“ section.
369        When synchronous digital bits are replayed along, the custom level must include these as well and therefore allows to 
370        set a custom level for each multi-purpose line separately. (see register `SPC_CH0_CUSTOM_STOP` in the manual)
371    
372        Parameters
373        ----------
374        value : int
375            The custom stop value
376
377        Returns
378        -------
379        int
380            The custom stop value of the specific channel
381        
382        TODO: change this to a specific unit?
383        """
384
385        if value is not None:
386            self.card.set_i(SPC_CH0_CUSTOM_STOP + self.index * (SPC_CH1_CUSTOM_STOP - SPC_CH0_CUSTOM_STOP), value)
387        return self.card.get_i(SPC_CH0_CUSTOM_STOP + self.index * (SPC_CH1_CUSTOM_STOP - SPC_CH0_CUSTOM_STOP))
388    
389    def ch_mask(self) -> int:
390        """
391        Gets mask for the "or"- or "and"-mask
392
393        Returns
394        -------
395        int
396            The mask for the "or"- or "and"-mask
397        """
398
399        return 1 << self.index
400    
401    def output_load(self, value : pint.Quantity = None) -> pint.Quantity:
402        """
403        Sets the electrical load of the user system connect the channel of the card. This is important for the correct
404        calculation of the output power. Typically, the load would be 50 Ohms, but it can be different.
405
406        Parameters
407        ----------
408        value : pint.Quantity
409            The electrical load connected by the user to the specific channel
410
411        Returns
412        -------
413        pint.Quantity
414            The electrical load connected by the user to the specific channel
415        """
416        if value is not None:
417            self._output_load = value
418        return self._output_load
419    
420    def voltage_conversion(self, value : pint.Quantity) -> pint.Quantity:
421        """
422        Convert the voltage that is needed at a certain output load to the voltage setting of the card if the load would be 50 Ohm
423
424        Parameters
425        ----------
426        value : pint.Quantity
427            The voltage that is needed at a certain output load
428
429        Returns
430        -------
431        pint.Quantity
432            The corresponding voltage at an output load of 50 Ohm
433        """
434
435        # The two at the end is because the value expected by the card is defined for a 50 Ohm load
436        if self._output_load == np.inf * units.ohm:
437            return value / 2
438        return value / (self._output_load / (self._output_load + self._series_impedance)) / 2
439
440    def to_amplitude_fraction(self, value) -> float:
441        """
442        Convert the voltage, percentage or power to percentage of the full range of the card
443
444        Parameters
445        ----------
446        value : pint.Quantity | float
447            The voltage that should be outputted at a certain output load
448
449        Returns
450        -------
451        float
452            The corresponding fraction of the full range of the card
453        """
454
455        if isinstance(value, units.Quantity) and value.check("[power]"):
456            # U_pk = U_rms * sqrt(2)
457            value = np.sqrt(2 * value.to('mW') * self._output_load) / self._conversion_amp * 100 * units.percent
458        elif isinstance(value, units.Quantity) and value.check("[electric_potential]"):
459            # value in U_pk
460            value = self.voltage_conversion(value) / self._conversion_amp * 100 * units.percent
461        value = UnitConversion.convert(value, units.fraction, float, rounding=None)
462        return value
463    
464    def from_amplitude_fraction(self, fraction, return_unit : pint.Quantity = None) -> pint.Quantity:
465        """
466        Convert the percentage of the full range to voltage, percentage or power
467
468        Parameters
469        ----------
470        fraction : float
471            The percentage of the full range of the card
472        return_unit : pint.Quantity
473            The unit of the return value
474
475        Returns
476        -------
477        pint.Quantity
478            The corresponding voltage, percentage or power
479        """
480
481        return_value = fraction
482        if isinstance(return_unit, units.Unit) and (1*return_unit).check("[power]"):
483            return_value = (np.power(self._conversion_amp * fraction, 2) / self._output_load / 2).to(return_unit)
484            # U_pk = U_rms * sqrt(2)
485        elif isinstance(return_unit, units.Unit) and (1*return_unit).check("[electric_potential]"):
486            return_value = (self._conversion_amp * fraction / 100).to(return_unit)
487            # value in U_pk
488            # value = self.voltage_conversion(value) / self._conversion_amp * 100 * units.percent
489        elif isinstance(return_unit, units.Unit) and (1*return_unit).check("[]"):
490            return_value = UnitConversion.force_unit(fraction, return_unit)
491        return return_value

A class to represent a channel of a card only used inside the Channels class in the list of channels

Channel(index: int, data_index: int, card: Card)
30    def __init__(self, index : int, data_index : int, card : Card) -> None:
31        """
32        Constructor of the Channel class
33    
34        Parameters
35        ----------
36        index : int
37            The index of the channel
38        card : Card
39            The card of the channel
40        """
41
42        self.card = card
43        self.index = index
44        self.data_index = data_index
45        self._conversion_amp = None
46        self._conversion_offset = 0 * units.percent
47        self._output_load = 50 * units.ohm
48        self._series_impedance = 50 * units.ohm

Constructor of the Channel class

Parameters
  • index (int): The index of the channel
  • card (Card): The card of the channel
card: Card = None
index: int = 0
data_index: int = 0
def enable(self, enable: bool = None) -> bool:
 92    def enable(self, enable : bool = None) -> bool:
 93        """
 94        Enables the analog front-end of the channel of the card (see register `SPC_ENABLEOUT` in the manual)
 95    
 96        Parameters
 97        ----------
 98        enable : bool
 99            Turn-on (True) or off (False) the spezific channel
100
101        Returns
102        -------
103        bool
104            The enable state of the specific channel
105        """
106
107        if enable is not None:
108            self.card.set_i(SPC_ENABLEOUT0 + (SPC_ENABLEOUT1 - SPC_ENABLEOUT0) * self.index, int(enable))
109        return bool(self.card.get_i(SPC_ENABLEOUT0 + (SPC_ENABLEOUT1 - SPC_ENABLEOUT0) * self.index))

Enables the analog front-end of the channel of the card (see register SPC_ENABLEOUT in the manual)

Parameters
  • enable (bool): Turn-on (True) or off (False) the spezific channel
Returns
  • bool: The enable state of the specific channel
def enable_out(self, enable: bool = None) -> bool:
 92    def enable(self, enable : bool = None) -> bool:
 93        """
 94        Enables the analog front-end of the channel of the card (see register `SPC_ENABLEOUT` in the manual)
 95    
 96        Parameters
 97        ----------
 98        enable : bool
 99            Turn-on (True) or off (False) the spezific channel
100
101        Returns
102        -------
103        bool
104            The enable state of the specific channel
105        """
106
107        if enable is not None:
108            self.card.set_i(SPC_ENABLEOUT0 + (SPC_ENABLEOUT1 - SPC_ENABLEOUT0) * self.index, int(enable))
109        return bool(self.card.get_i(SPC_ENABLEOUT0 + (SPC_ENABLEOUT1 - SPC_ENABLEOUT0) * self.index))

Enables the analog front-end of the channel of the card (see register SPC_ENABLEOUT in the manual)

Parameters
  • enable (bool): Turn-on (True) or off (False) the spezific channel
Returns
  • bool: The enable state of the specific channel
def path(self, value: int = None) -> int:
112    def path(self, value : int = None) -> int:
113        """
114        Sets the input path of the channel of the card (see register `SPC_PATH0` in the manual)
115    
116        Parameters
117        ----------
118        value : int
119            The input path of the specific channel
120        
121        Returns
122        -------
123        int
124            The input path of the specific channel
125        """
126
127        if value is not None:
128            self.card.set_i(SPC_PATH0 + (SPC_PATH1 - SPC_PATH0) * self.index, value)
129        return self.card.get_i(SPC_PATH0 + (SPC_PATH1 - SPC_PATH0) * self.index)

Sets the input path of the channel of the card (see register SPC_PATH0 in the manual)

Parameters
  • value (int): The input path of the specific channel
Returns
  • int: The input path of the specific channel
def amp(self, value: int = None, return_unit=None) -> int:
131    def amp(self, value : int = None, return_unit = None) -> int:
132        """
133        Sets the output/input range (amplitude) of the analog front-end of the channel of the card in mV (see register `SPC_AMP` in the manual)
134    
135        Parameters
136        ----------
137        value : int
138            The output range (amplitude) of the specific channel in millivolts
139        unit : pint.Unit = None
140            The unit of the return value
141            
142        Returns
143        -------
144        int | pint.Quantity
145            The output range (amplitude) of the specific channel in millivolts or the unit specified
146        """
147
148        if value is not None:
149            if isinstance(value, pint.Quantity):
150                value = self.voltage_conversion(value)
151            self._conversion_amp = UnitConversion.force_unit(value, units.mV)
152            value = UnitConversion.convert(value, units.mV, int)
153            self.card.set_i(SPC_AMP0 + (SPC_AMP1 - SPC_AMP0) * self.index, value)
154        value = self.card.get_i(SPC_AMP0 + (SPC_AMP1 - SPC_AMP0) * self.index)
155        value = UnitConversion.to_unit(value * units.mV, return_unit)
156        return value

Sets the output/input range (amplitude) of the analog front-end of the channel of the card in mV (see register SPC_AMP in the manual)

Parameters
  • value (int): The output range (amplitude) of the specific channel in millivolts
  • unit (pint.Unit = None): The unit of the return value
Returns
  • int | pint.Quantity: The output range (amplitude) of the specific channel in millivolts or the unit specified
def offset(self, value: int = None, return_unit=None) -> int:
158    def offset(self, value : int = None, return_unit = None) -> int:
159        """
160        Sets the offset of the analog front-end of the channel of the card in % of the full range o rmV (see register `SPC_OFFS0` in the manual)
161        If the value is given and has a unit, then this unit is converted to the unit of the card (mV or %)
162    
163        Parameters
164        ----------
165        value : int | pint.Quantity = None
166            The offset of the specific channel as integer in % or as a Quantity in % or mV
167        unit : pint.Unit = None
168            The unit of the return value
169            
170        Returns
171        -------
172        int | pint.Quantity
173            The offset of the specific channel in % or the unit specified by return_unit
174        """
175
176        # Analog in cards are programmed in percent of the full range and analog output cards in mV (in the M2p, M4i/x and M5i families)
177        card_unit = 1
178        fnc_type = self.card.function_type()
179        if fnc_type == SPCM_TYPE_AI:
180            card_unit = units.percent
181        elif fnc_type == SPCM_TYPE_AO:
182            card_unit = units.mV
183
184        if value is not None:
185            # The user gives a value as a Quantity
186            if isinstance(value, pint.Quantity):
187                if fnc_type == SPCM_TYPE_AO:
188                    # The card expects a value in mV
189                    if value.check('[]'):
190                        # Convert from percent to mV
191                        value = (value * self._conversion_amp).to(card_unit)
192                    else:
193                        value = value.to(card_unit)
194                elif fnc_type == SPCM_TYPE_AI:
195                    # The card expects a value in percent
196                    if value.check('[electric_potential]'):
197                        # Convert from mV to percent
198                        value = (value / self._conversion_amp).to(card_unit)
199                    else:
200                        value = value.to(card_unit)
201            else:
202                # Value is given as a number
203                pass
204
205            value = UnitConversion.convert(value, card_unit, int)
206            self.card.set_i(SPC_OFFS0 + (SPC_OFFS1 - SPC_OFFS0) * self.index, value)
207        
208        return_value = self.card.get_i(SPC_OFFS0 + (SPC_OFFS1 - SPC_OFFS0) * self.index)
209        # Turn the return value into a quantity
210        return_quantity = UnitConversion.to_unit(return_value, return_unit)
211        # Save the conversion offset to be able to convert the data to a quantity with the correct unit
212        self._conversion_offset = UnitConversion.force_unit(return_value, card_unit)
213        return return_quantity

Sets the offset of the analog front-end of the channel of the card in % of the full range o rmV (see register SPC_OFFS0 in the manual) If the value is given and has a unit, then this unit is converted to the unit of the card (mV or %)

Parameters
  • value (int | pint.Quantity = None): The offset of the specific channel as integer in % or as a Quantity in % or mV
  • unit (pint.Unit = None): The unit of the return value
Returns
  • int | pint.Quantity: The offset of the specific channel in % or the unit specified by return_unit
def convert_data( self, data: numpy.ndarray[typing.Any, numpy.dtype[+_ScalarType_co]], return_unit: pint.registry.Unit = <Unit('millivolt')>) -> numpy.ndarray[typing.Any, numpy.dtype[+_ScalarType_co]]:
215    def convert_data(self, data : npt.NDArray, return_unit : pint.Unit = units.mV) -> npt.NDArray:
216        """
217        Converts the data to the correct unit in units of electrical potential
218        
219        Parameters
220        ----------
221        data : numpy.ndarray
222            The data to be converted
223        return_unit : pint.Unit = None
224            The unit of the return value
225            
226        Returns
227        -------
228        numpy.ndarray
229            The converted data in units of electrical potential
230        """
231
232        max_value = self.card.max_sample_value()
233        if self._conversion_offset.check('[]'):
234            return_data = (data / max_value - self._conversion_offset) * self._conversion_amp
235        else:
236            return_data = (data / max_value) * self._conversion_amp - self._conversion_offset
237        return_data = UnitConversion.to_unit(return_data, return_unit)
238        return return_data

Converts the data to the correct unit in units of electrical potential

Parameters
  • data (numpy.ndarray): The data to be converted
  • return_unit (pint.Unit = None): The unit of the return value
Returns
  • numpy.ndarray: The converted data in units of electrical potential
def reconvert_data( self, data: numpy.ndarray[typing.Any, numpy.dtype[+_ScalarType_co]]) -> numpy.ndarray[typing.Any, numpy.dtype[+_ScalarType_co]]:
240    def reconvert_data(self, data : npt.NDArray) -> npt.NDArray:
241        """
242        Convert data with units back to integer values in units of electrical potential
243        
244        Parameters
245        ----------
246        data : numpy.ndarray
247            The data to be reconverted
248            
249        Returns
250        -------
251        numpy.ndarray
252            The reconverted data as integer in mV
253        """
254
255        if self._conversion_offset.check('[]'):
256            return_data = int((data / self._conversion_amp + self._conversion_offset) * self.card.max_sample_value())
257        else:
258            return_data = int(((data + self._conversion_offset) / self._conversion_amp) * self.card.max_sample_value())
259        return return_data

Convert data with units back to integer values in units of electrical potential

Parameters
  • data (numpy.ndarray): The data to be reconverted
Returns
  • numpy.ndarray: The reconverted data as integer in mV
def termination(self, value: int) -> None:
261    def termination(self, value : int) -> None:
262        """
263        Sets the termination of the analog front-end of the channel of the card (see register `SPC_50OHM0` in the manual)
264    
265        Parameters
266        ----------
267        value : int | bool
268            The termination of the specific channel
269        """
270
271        self.card.set_i(SPC_50OHM0 + (SPC_50OHM1 - SPC_50OHM0) * self.index, int(value))

Sets the termination of the analog front-end of the channel of the card (see register SPC_50OHM0 in the manual)

Parameters
  • value (int | bool): The termination of the specific channel
def get_termination(self) -> int:
273    def get_termination(self) -> int:
274        """
275        Gets the termination of the analog front-end of the channel of the card (see register `SPC_50OHM0` in the manual)
276            
277        Returns
278        -------
279        int
280            The termination of the specific channel
281        """
282
283        return self.card.get_i(SPC_50OHM0 + (SPC_50OHM1 - SPC_50OHM0) * self.index)

Gets the termination of the analog front-end of the channel of the card (see register SPC_50OHM0 in the manual)

Returns
  • int: The termination of the specific channel
def coupling(self, value: int = None) -> int:
285    def coupling(self, value : int = None) -> int:
286        """
287        Sets the coupling of the analog front-end of the channel of the card (see register `SPC_ACDC0` in the manual)
288    
289        Parameters
290        ----------
291        value : int
292            The coupling of the specific channel
293            
294        Returns
295        -------
296        int
297            The coupling of the specific channel
298        """
299
300        if value is not None:
301            self.card.set_i(SPC_ACDC0 + (SPC_ACDC1 - SPC_ACDC0) * self.index, value)
302        return self.card.get_i(SPC_ACDC0 + (SPC_ACDC1 - SPC_ACDC0) * self.index)

Sets the coupling of the analog front-end of the channel of the card (see register SPC_ACDC0 in the manual)

Parameters
  • value (int): The coupling of the specific channel
Returns
  • int: The coupling of the specific channel
def coupling_offset_compensation(self, value: int = None) -> int:
304    def coupling_offset_compensation(self, value : int = None) -> int:
305        """
306        Enables or disables the coupling offset compensation of the analog front-end of the channel of the card (see register `SPC_ACDC_OFFS_COMPENSATION0` in the manual)
307    
308        Parameters
309        ----------
310        value : int
311            Enables the coupling offset compensation of the specific channel
312            
313        Returns
314        -------
315        int
316            return if the coupling offset compensation of the specific channel is enabled ("1") or disabled ("0")
317        """
318
319        if value is not None:
320            self.card.set_i(SPC_ACDC_OFFS_COMPENSATION0 + (SPC_ACDC_OFFS_COMPENSATION1 - SPC_ACDC_OFFS_COMPENSATION0) * self.index, value)
321        return self.card.get_i(SPC_ACDC_OFFS_COMPENSATION0 + (SPC_ACDC_OFFS_COMPENSATION1 - SPC_ACDC_OFFS_COMPENSATION0) * self.index)

Enables or disables the coupling offset compensation of the analog front-end of the channel of the card (see register SPC_ACDC_OFFS_COMPENSATION0 in the manual)

Parameters
  • value (int): Enables the coupling offset compensation of the specific channel
Returns
  • int: return if the coupling offset compensation of the specific channel is enabled ("1") or disabled ("0")
def filter(self, value: int = None) -> int:
323    def filter(self, value : int = None) -> int:
324        """
325        Sets the filter of the analog front-end of the channel of the card (see register `SPC_FILTER0` in the manual)
326    
327        Parameters
328        ----------
329        value : int
330            The filter of the specific channel
331            
332        Returns
333        -------
334        int
335            The filter of the specific channel
336        """
337
338        if value is not None:
339            self.card.set_i(SPC_FILTER0 + (SPC_FILTER1 - SPC_FILTER0) * self.index, value)
340        return self.card.get_i(SPC_FILTER0 + (SPC_FILTER1 - SPC_FILTER0) * self.index)

Sets the filter of the analog front-end of the channel of the card (see register SPC_FILTER0 in the manual)

Parameters
  • value (int): The filter of the specific channel
Returns
  • int: The filter of the specific channel
def stop_level(self, value: int = None) -> int:
342    def stop_level(self, value : int = None) -> int:
343        """
344        Usually the used outputs of the analog generation boards are set to zero level after replay. 
345        This is in most cases adequate. In some cases it can be necessary to hold the last sample,
346        to output the maximum positive level or maximum negative level after replay. The stoplevel will 
347        stay on the defined level until the next output has been made. With this function
348        you can define the behavior after replay (see register `SPC_CH0_STOPLEVEL` in the manual)
349    
350        Parameters
351        ----------
352        value : int
353            The wanted stop behaviour
354
355        Returns
356        -------
357        int
358            The stop behaviour of the specific channel
359        """
360
361        if value is not None:
362            self.card.set_i(SPC_CH0_STOPLEVEL + self.index * (SPC_CH1_STOPLEVEL - SPC_CH0_STOPLEVEL), value)
363        return self.card.get_i(SPC_CH0_STOPLEVEL + self.index * (SPC_CH1_STOPLEVEL - SPC_CH0_STOPLEVEL))

Usually the used outputs of the analog generation boards are set to zero level after replay. This is in most cases adequate. In some cases it can be necessary to hold the last sample, to output the maximum positive level or maximum negative level after replay. The stoplevel will stay on the defined level until the next output has been made. With this function you can define the behavior after replay (see register SPC_CH0_STOPLEVEL in the manual)

Parameters
  • value (int): The wanted stop behaviour
Returns
  • int: The stop behaviour of the specific channel
def custom_stop(self, value: int = None) -> int:
365    def custom_stop(self, value : int = None) -> int:
366        """
367        Allows to define a 16bit wide custom level per channel for the analog output to enter in pauses. The sample format is 
368        exactly the same as during replay, as described in the „sample format“ section.
369        When synchronous digital bits are replayed along, the custom level must include these as well and therefore allows to 
370        set a custom level for each multi-purpose line separately. (see register `SPC_CH0_CUSTOM_STOP` in the manual)
371    
372        Parameters
373        ----------
374        value : int
375            The custom stop value
376
377        Returns
378        -------
379        int
380            The custom stop value of the specific channel
381        
382        TODO: change this to a specific unit?
383        """
384
385        if value is not None:
386            self.card.set_i(SPC_CH0_CUSTOM_STOP + self.index * (SPC_CH1_CUSTOM_STOP - SPC_CH0_CUSTOM_STOP), value)
387        return self.card.get_i(SPC_CH0_CUSTOM_STOP + self.index * (SPC_CH1_CUSTOM_STOP - SPC_CH0_CUSTOM_STOP))

Allows to define a 16bit wide custom level per channel for the analog output to enter in pauses. The sample format is exactly the same as during replay, as described in the „sample format“ section. When synchronous digital bits are replayed along, the custom level must include these as well and therefore allows to set a custom level for each multi-purpose line separately. (see register SPC_CH0_CUSTOM_STOP in the manual)

Parameters
  • value (int): The custom stop value
Returns
  • int: The custom stop value of the specific channel
  • TODO (change this to a specific unit?):
def ch_mask(self) -> int:
389    def ch_mask(self) -> int:
390        """
391        Gets mask for the "or"- or "and"-mask
392
393        Returns
394        -------
395        int
396            The mask for the "or"- or "and"-mask
397        """
398
399        return 1 << self.index

Gets mask for the "or"- or "and"-mask

Returns
  • int: The mask for the "or"- or "and"-mask
def output_load(self, value: pint.registry.Quantity = None) -> pint.registry.Quantity:
401    def output_load(self, value : pint.Quantity = None) -> pint.Quantity:
402        """
403        Sets the electrical load of the user system connect the channel of the card. This is important for the correct
404        calculation of the output power. Typically, the load would be 50 Ohms, but it can be different.
405
406        Parameters
407        ----------
408        value : pint.Quantity
409            The electrical load connected by the user to the specific channel
410
411        Returns
412        -------
413        pint.Quantity
414            The electrical load connected by the user to the specific channel
415        """
416        if value is not None:
417            self._output_load = value
418        return self._output_load

Sets the electrical load of the user system connect the channel of the card. This is important for the correct calculation of the output power. Typically, the load would be 50 Ohms, but it can be different.

Parameters
  • value (pint.Quantity): The electrical load connected by the user to the specific channel
Returns
  • pint.Quantity: The electrical load connected by the user to the specific channel
def voltage_conversion(self, value: pint.registry.Quantity) -> pint.registry.Quantity:
420    def voltage_conversion(self, value : pint.Quantity) -> pint.Quantity:
421        """
422        Convert the voltage that is needed at a certain output load to the voltage setting of the card if the load would be 50 Ohm
423
424        Parameters
425        ----------
426        value : pint.Quantity
427            The voltage that is needed at a certain output load
428
429        Returns
430        -------
431        pint.Quantity
432            The corresponding voltage at an output load of 50 Ohm
433        """
434
435        # The two at the end is because the value expected by the card is defined for a 50 Ohm load
436        if self._output_load == np.inf * units.ohm:
437            return value / 2
438        return value / (self._output_load / (self._output_load + self._series_impedance)) / 2

Convert the voltage that is needed at a certain output load to the voltage setting of the card if the load would be 50 Ohm

Parameters
  • value (pint.Quantity): The voltage that is needed at a certain output load
Returns
  • pint.Quantity: The corresponding voltage at an output load of 50 Ohm
def to_amplitude_fraction(self, value) -> float:
440    def to_amplitude_fraction(self, value) -> float:
441        """
442        Convert the voltage, percentage or power to percentage of the full range of the card
443
444        Parameters
445        ----------
446        value : pint.Quantity | float
447            The voltage that should be outputted at a certain output load
448
449        Returns
450        -------
451        float
452            The corresponding fraction of the full range of the card
453        """
454
455        if isinstance(value, units.Quantity) and value.check("[power]"):
456            # U_pk = U_rms * sqrt(2)
457            value = np.sqrt(2 * value.to('mW') * self._output_load) / self._conversion_amp * 100 * units.percent
458        elif isinstance(value, units.Quantity) and value.check("[electric_potential]"):
459            # value in U_pk
460            value = self.voltage_conversion(value) / self._conversion_amp * 100 * units.percent
461        value = UnitConversion.convert(value, units.fraction, float, rounding=None)
462        return value

Convert the voltage, percentage or power to percentage of the full range of the card

Parameters
  • value (pint.Quantity | float): The voltage that should be outputted at a certain output load
Returns
  • float: The corresponding fraction of the full range of the card
def from_amplitude_fraction( self, fraction, return_unit: pint.registry.Quantity = None) -> pint.registry.Quantity:
464    def from_amplitude_fraction(self, fraction, return_unit : pint.Quantity = None) -> pint.Quantity:
465        """
466        Convert the percentage of the full range to voltage, percentage or power
467
468        Parameters
469        ----------
470        fraction : float
471            The percentage of the full range of the card
472        return_unit : pint.Quantity
473            The unit of the return value
474
475        Returns
476        -------
477        pint.Quantity
478            The corresponding voltage, percentage or power
479        """
480
481        return_value = fraction
482        if isinstance(return_unit, units.Unit) and (1*return_unit).check("[power]"):
483            return_value = (np.power(self._conversion_amp * fraction, 2) / self._output_load / 2).to(return_unit)
484            # U_pk = U_rms * sqrt(2)
485        elif isinstance(return_unit, units.Unit) and (1*return_unit).check("[electric_potential]"):
486            return_value = (self._conversion_amp * fraction / 100).to(return_unit)
487            # value in U_pk
488            # value = self.voltage_conversion(value) / self._conversion_amp * 100 * units.percent
489        elif isinstance(return_unit, units.Unit) and (1*return_unit).check("[]"):
490            return_value = UnitConversion.force_unit(fraction, return_unit)
491        return return_value

Convert the percentage of the full range to voltage, percentage or power

Parameters
  • fraction (float): The percentage of the full range of the card
  • return_unit (pint.Quantity): The unit of the return value
Returns
  • pint.Quantity: The corresponding voltage, percentage or power
class Clock(spcm.CardFunctionality):
 12class Clock(CardFunctionality):
 13    """a higher-level abstraction of the CardFunctionality class to implement the Card's clock engine"""
 14    
 15    def __str__(self) -> str:
 16        """
 17        String representation of the Clock class
 18    
 19        Returns
 20        -------
 21        str
 22            String representation of the Clock class
 23        """
 24        
 25        return f"Clock(card={self.card})"
 26    
 27    __repr__ = __str__
 28    
 29    def write_setup(self) -> None:
 30        """Write the setup to the card"""
 31        self.card.write_setup()
 32    
 33    
 34    def mode(self, mode : int = None) -> int:
 35        """
 36        Set the clock mode of the card (see register `SPC_CLOCKMODE` in the manual)
 37    
 38        Parameters
 39        ----------
 40        mode : int
 41            The clock mode of the card
 42        
 43        Returns
 44        -------
 45        int
 46            The clock mode of the card
 47        """
 48
 49        if mode is not None:
 50            self.card.set_i(SPC_CLOCKMODE, mode)
 51        return self.card.get_i(SPC_CLOCKMODE)
 52    
 53    def max_sample_rate(self, return_unit = None) -> int:
 54        """
 55        Returns the maximum sample rate of the active card (see register `SPC_MIINST_MAXADCLOCK` in the manual)
 56    
 57        Returns
 58        -------
 59        int
 60        """
 61        
 62        max_sr = self.card.get_i(SPC_MIINST_MAXADCLOCK)
 63        if return_unit is not None: max_sr = UnitConversion.to_unit(max_sr * units.Hz, return_unit)
 64        return max_sr
 65
 66    def sample_rate(self, sample_rate = 0, max : bool = False, return_unit = None) -> int:
 67        """
 68        Sets or gets the current sample rate of the handled card (see register `SPC_SAMPLERATE` in the manual)
 69
 70        Parameters
 71        ----------
 72        sample_rate : int | pint.Quantity = 0
 73            if the parameter sample_rate is given with the function call, then the card's sample rate is set to that value
 74        max : bool = False
 75            if max is True, the method sets the maximum sample rate of the card
 76        unit : pint.Unit = None
 77            the unit of the sample rate, by default None
 78    
 79        Returns
 80        -------
 81        int
 82            the current sample rate in Samples/s
 83        """
 84        
 85        if max: sample_rate = self.max_sample_rate()
 86        if sample_rate:
 87            if isinstance(sample_rate, units.Quantity) and sample_rate.check("[]"):
 88                max_sr = self.max_sample_rate()
 89                sample_rate = sample_rate.to_base_units().magnitude * max_sr
 90            sample_rate = UnitConversion.convert(sample_rate, units.Hz, int)
 91            self.card.set_i(SPC_SAMPLERATE, int(sample_rate))
 92        return_value = self.card.get_i(SPC_SAMPLERATE)
 93        if return_unit is not None: return_value = UnitConversion.to_unit(return_value * units.Hz, return_unit)
 94        return return_value
 95    
 96    def clock_output(self, clock_output : int = None) -> int:
 97        """
 98        Set the clock output of the card (see register `SPC_CLOCKOUT` in the manual)
 99        
100        Parameters
101        ----------
102        clock_output : int
103            the clock output of the card
104        
105        Returns
106        -------
107        int
108            the clock output of the card
109        """
110        
111        if clock_output is not None:
112            self.card.set_i(SPC_CLOCKOUT, int(clock_output))
113        return self.card.get_i(SPC_CLOCKOUT)
114    output = clock_output
115    
116    def reference_clock(self, reference_clock : int = None) -> int:
117        """
118        Set the reference clock of the card (see register `SPC_REFERENCECLOCK` in the manual)
119        
120        Parameters
121        ----------
122        reference_clock : int | pint.Quantity
123            the reference clock of the card in Hz
124        
125        Returns
126        -------
127        int
128            the reference clock of the card in Hz
129        """
130        
131        if reference_clock is not None:
132            reference_clock = UnitConversion.convert(reference_clock, units.Hz, int)
133            self.card.set_i(SPC_REFERENCECLOCK, reference_clock)
134        return self.card.get_i(SPC_REFERENCECLOCK)
135    
136    def termination(self, termination : int = None) -> int:
137        """
138        Set the termination for the clock input of the card (see register `SPC_CLOCK50OHM` in the manual)
139        
140        Parameters
141        ----------
142        termination : int | bool
143            the termination of the card
144        
145        Returns
146        -------
147        int
148            the termination of the card
149        """
150        
151        if termination is not None:
152            self.card.set_i(SPC_CLOCK50OHM, int(termination))
153        return self.card.get_i(SPC_CLOCK50OHM)
154    
155    def threshold(self, value : int = None, return_unit = None) -> int:
156        """
157        Set the clock threshold of the card (see register `SPC_CLOCKTHRESHOLD` in the manual)
158        
159        Parameters
160        ----------
161        value : int
162            the clock threshold of the card
163        return_unit : pint.Unit = None
164            the unit of the clock threshold
165        
166        Returns
167        -------
168        int | pint.Quantity
169            the clock threshold of the card
170        """
171        
172        if value is not None:
173            value = UnitConversion.convert(value, units.mV, int)
174            self.card.set_i(SPC_CLOCK_THRESHOLD, int(value))
175        value = self.card.get_i(SPC_CLOCK_THRESHOLD)
176        value = UnitConversion.to_unit(value * units.mV, return_unit)
177        return value
178    
179    def threshold_min(self, return_unit = None) -> int:
180        """
181        Returns the minimum clock threshold of the card (see register `SPC_CLOCK_AVAILTHRESHOLD_MIN` in the manual)
182
183        Parameters
184        ----------
185        return_unit : pint.Unit = None
186            the unit of the return clock threshold
187        
188        Returns
189        -------
190        int
191            the minimum clock threshold of the card
192        """
193        
194        value = self.card.get_i(SPC_CLOCK_AVAILTHRESHOLD_MIN)
195        value = UnitConversion.to_unit(value * units.mV, return_unit)
196        return value
197    
198    def threshold_max(self, return_unit = None) -> int:
199        """
200        Returns the maximum clock threshold of the card (see register `SPC_CLOCK_AVAILTHRESHOLD_MAX` in the manual)
201
202        Parameters
203        ----------
204        return_unit : pint.Unit = None
205            the unit of the return clock threshold
206        
207        Returns
208        -------
209        int
210            the maximum clock threshold of the card
211        """
212        
213        value = self.card.get_i(SPC_CLOCK_AVAILTHRESHOLD_MAX)
214        value = UnitConversion.to_unit(value * units.mV, return_unit)
215        return value
216    
217    def threshold_step(self, return_unit = None) -> int:
218        """
219        Returns the step of the clock threshold of the card (see register `SPC_CLOCK_AVAILTHRESHOLD_STEP` in the manual)
220
221        Parameters
222        ----------
223        return_unit : pint.Unit = None
224            the unit of the return clock threshold
225        
226        Returns
227        -------
228        int
229            the step of the clock threshold of the card
230        """
231        
232        value = self.card.get_i(SPC_CLOCK_AVAILTHRESHOLD_STEP)
233        value = UnitConversion.to_unit(value * units.mV, return_unit)
234        return value

a higher-level abstraction of the CardFunctionality class to implement the Card's clock engine

def write_setup(self) -> None:
29    def write_setup(self) -> None:
30        """Write the setup to the card"""
31        self.card.write_setup()

Write the setup to the card

def mode(self, mode: int = None) -> int:
34    def mode(self, mode : int = None) -> int:
35        """
36        Set the clock mode of the card (see register `SPC_CLOCKMODE` in the manual)
37    
38        Parameters
39        ----------
40        mode : int
41            The clock mode of the card
42        
43        Returns
44        -------
45        int
46            The clock mode of the card
47        """
48
49        if mode is not None:
50            self.card.set_i(SPC_CLOCKMODE, mode)
51        return self.card.get_i(SPC_CLOCKMODE)

Set the clock mode of the card (see register SPC_CLOCKMODE in the manual)

Parameters
  • mode (int): The clock mode of the card
Returns
  • int: The clock mode of the card
def max_sample_rate(self, return_unit=None) -> int:
53    def max_sample_rate(self, return_unit = None) -> int:
54        """
55        Returns the maximum sample rate of the active card (see register `SPC_MIINST_MAXADCLOCK` in the manual)
56    
57        Returns
58        -------
59        int
60        """
61        
62        max_sr = self.card.get_i(SPC_MIINST_MAXADCLOCK)
63        if return_unit is not None: max_sr = UnitConversion.to_unit(max_sr * units.Hz, return_unit)
64        return max_sr

Returns the maximum sample rate of the active card (see register SPC_MIINST_MAXADCLOCK in the manual)

Returns
  • int
def sample_rate(self, sample_rate=0, max: bool = False, return_unit=None) -> int:
66    def sample_rate(self, sample_rate = 0, max : bool = False, return_unit = None) -> int:
67        """
68        Sets or gets the current sample rate of the handled card (see register `SPC_SAMPLERATE` in the manual)
69
70        Parameters
71        ----------
72        sample_rate : int | pint.Quantity = 0
73            if the parameter sample_rate is given with the function call, then the card's sample rate is set to that value
74        max : bool = False
75            if max is True, the method sets the maximum sample rate of the card
76        unit : pint.Unit = None
77            the unit of the sample rate, by default None
78    
79        Returns
80        -------
81        int
82            the current sample rate in Samples/s
83        """
84        
85        if max: sample_rate = self.max_sample_rate()
86        if sample_rate:
87            if isinstance(sample_rate, units.Quantity) and sample_rate.check("[]"):
88                max_sr = self.max_sample_rate()
89                sample_rate = sample_rate.to_base_units().magnitude * max_sr
90            sample_rate = UnitConversion.convert(sample_rate, units.Hz, int)
91            self.card.set_i(SPC_SAMPLERATE, int(sample_rate))
92        return_value = self.card.get_i(SPC_SAMPLERATE)
93        if return_unit is not None: return_value = UnitConversion.to_unit(return_value * units.Hz, return_unit)
94        return return_value

Sets or gets the current sample rate of the handled card (see register SPC_SAMPLERATE in the manual)

Parameters
  • sample_rate (int | pint.Quantity = 0): if the parameter sample_rate is given with the function call, then the card's sample rate is set to that value
  • max (bool = False): if max is True, the method sets the maximum sample rate of the card
  • unit (pint.Unit = None): the unit of the sample rate, by default None
Returns
  • int: the current sample rate in Samples/s
def clock_output(self, clock_output: int = None) -> int:
 96    def clock_output(self, clock_output : int = None) -> int:
 97        """
 98        Set the clock output of the card (see register `SPC_CLOCKOUT` in the manual)
 99        
100        Parameters
101        ----------
102        clock_output : int
103            the clock output of the card
104        
105        Returns
106        -------
107        int
108            the clock output of the card
109        """
110        
111        if clock_output is not None:
112            self.card.set_i(SPC_CLOCKOUT, int(clock_output))
113        return self.card.get_i(SPC_CLOCKOUT)

Set the clock output of the card (see register SPC_CLOCKOUT in the manual)

Parameters
  • clock_output (int): the clock output of the card
Returns
  • int: the clock output of the card
def output(self, clock_output: int = None) -> int:
 96    def clock_output(self, clock_output : int = None) -> int:
 97        """
 98        Set the clock output of the card (see register `SPC_CLOCKOUT` in the manual)
 99        
100        Parameters
101        ----------
102        clock_output : int
103            the clock output of the card
104        
105        Returns
106        -------
107        int
108            the clock output of the card
109        """
110        
111        if clock_output is not None:
112            self.card.set_i(SPC_CLOCKOUT, int(clock_output))
113        return self.card.get_i(SPC_CLOCKOUT)

Set the clock output of the card (see register SPC_CLOCKOUT in the manual)

Parameters
  • clock_output (int): the clock output of the card
Returns
  • int: the clock output of the card
def reference_clock(self, reference_clock: int = None) -> int:
116    def reference_clock(self, reference_clock : int = None) -> int:
117        """
118        Set the reference clock of the card (see register `SPC_REFERENCECLOCK` in the manual)
119        
120        Parameters
121        ----------
122        reference_clock : int | pint.Quantity
123            the reference clock of the card in Hz
124        
125        Returns
126        -------
127        int
128            the reference clock of the card in Hz
129        """
130        
131        if reference_clock is not None:
132            reference_clock = UnitConversion.convert(reference_clock, units.Hz, int)
133            self.card.set_i(SPC_REFERENCECLOCK, reference_clock)
134        return self.card.get_i(SPC_REFERENCECLOCK)

Set the reference clock of the card (see register SPC_REFERENCECLOCK in the manual)

Parameters
  • reference_clock (int | pint.Quantity): the reference clock of the card in Hz
Returns
  • int: the reference clock of the card in Hz
def termination(self, termination: int = None) -> int:
136    def termination(self, termination : int = None) -> int:
137        """
138        Set the termination for the clock input of the card (see register `SPC_CLOCK50OHM` in the manual)
139        
140        Parameters
141        ----------
142        termination : int | bool
143            the termination of the card
144        
145        Returns
146        -------
147        int
148            the termination of the card
149        """
150        
151        if termination is not None:
152            self.card.set_i(SPC_CLOCK50OHM, int(termination))
153        return self.card.get_i(SPC_CLOCK50OHM)

Set the termination for the clock input of the card (see register SPC_CLOCK50OHM in the manual)

Parameters
  • termination (int | bool): the termination of the card
Returns
  • int: the termination of the card
def threshold(self, value: int = None, return_unit=None) -> int:
155    def threshold(self, value : int = None, return_unit = None) -> int:
156        """
157        Set the clock threshold of the card (see register `SPC_CLOCKTHRESHOLD` in the manual)
158        
159        Parameters
160        ----------
161        value : int
162            the clock threshold of the card
163        return_unit : pint.Unit = None
164            the unit of the clock threshold
165        
166        Returns
167        -------
168        int | pint.Quantity
169            the clock threshold of the card
170        """
171        
172        if value is not None:
173            value = UnitConversion.convert(value, units.mV, int)
174            self.card.set_i(SPC_CLOCK_THRESHOLD, int(value))
175        value = self.card.get_i(SPC_CLOCK_THRESHOLD)
176        value = UnitConversion.to_unit(value * units.mV, return_unit)
177        return value

Set the clock threshold of the card (see register SPC_CLOCKTHRESHOLD in the manual)

Parameters
  • value (int): the clock threshold of the card
  • return_unit (pint.Unit = None): the unit of the clock threshold
Returns
  • int | pint.Quantity: the clock threshold of the card
def threshold_min(self, return_unit=None) -> int:
179    def threshold_min(self, return_unit = None) -> int:
180        """
181        Returns the minimum clock threshold of the card (see register `SPC_CLOCK_AVAILTHRESHOLD_MIN` in the manual)
182
183        Parameters
184        ----------
185        return_unit : pint.Unit = None
186            the unit of the return clock threshold
187        
188        Returns
189        -------
190        int
191            the minimum clock threshold of the card
192        """
193        
194        value = self.card.get_i(SPC_CLOCK_AVAILTHRESHOLD_MIN)
195        value = UnitConversion.to_unit(value * units.mV, return_unit)
196        return value

Returns the minimum clock threshold of the card (see register SPC_CLOCK_AVAILTHRESHOLD_MIN in the manual)

Parameters
  • return_unit (pint.Unit = None): the unit of the return clock threshold
Returns
  • int: the minimum clock threshold of the card
def threshold_max(self, return_unit=None) -> int:
198    def threshold_max(self, return_unit = None) -> int:
199        """
200        Returns the maximum clock threshold of the card (see register `SPC_CLOCK_AVAILTHRESHOLD_MAX` in the manual)
201
202        Parameters
203        ----------
204        return_unit : pint.Unit = None
205            the unit of the return clock threshold
206        
207        Returns
208        -------
209        int
210            the maximum clock threshold of the card
211        """
212        
213        value = self.card.get_i(SPC_CLOCK_AVAILTHRESHOLD_MAX)
214        value = UnitConversion.to_unit(value * units.mV, return_unit)
215        return value

Returns the maximum clock threshold of the card (see register SPC_CLOCK_AVAILTHRESHOLD_MAX in the manual)

Parameters
  • return_unit (pint.Unit = None): the unit of the return clock threshold
Returns
  • int: the maximum clock threshold of the card
def threshold_step(self, return_unit=None) -> int:
217    def threshold_step(self, return_unit = None) -> int:
218        """
219        Returns the step of the clock threshold of the card (see register `SPC_CLOCK_AVAILTHRESHOLD_STEP` in the manual)
220
221        Parameters
222        ----------
223        return_unit : pint.Unit = None
224            the unit of the return clock threshold
225        
226        Returns
227        -------
228        int
229            the step of the clock threshold of the card
230        """
231        
232        value = self.card.get_i(SPC_CLOCK_AVAILTHRESHOLD_STEP)
233        value = UnitConversion.to_unit(value * units.mV, return_unit)
234        return value

Returns the step of the clock threshold of the card (see register SPC_CLOCK_AVAILTHRESHOLD_STEP in the manual)

Parameters
  • return_unit (pint.Unit = None): the unit of the return clock threshold
Returns
  • int: the step of the clock threshold of the card
class Trigger(spcm.CardFunctionality):
 16class Trigger(CardFunctionality):
 17    """a higher-level abstraction of the CardFunctionality class to implement the Card's Trigger engine"""
 18
 19    channels : Channels = None
 20
 21    def __init__(self, card : 'Card', **kwargs) -> None:
 22        """
 23        Constructor of the Trigger class
 24        
 25        Parameters
 26        ----------
 27        card : Card
 28            The card to use for the Trigger class
 29        """
 30
 31        super().__init__(card)
 32        self.channels = kwargs.get('channels', None)
 33    
 34    def __str__(self) -> str:
 35        """
 36        String representation of the Trigger class
 37    
 38        Returns
 39        -------
 40        str
 41            String representation of the Trigger class
 42        """
 43        
 44        return f"Trigger(card={self.card})"
 45    
 46    __repr__ = __str__
 47
 48    def enable(self) -> None:
 49        """Enables the trigger engine (see command 'M2CMD_CARD_ENABLETRIGGER' in chapter `Trigger` in the manual)"""
 50        self.card.cmd(M2CMD_CARD_ENABLETRIGGER)
 51    
 52    def disable(self) -> None:
 53        """Disables the trigger engine (see command 'M2CMD_CARD_DISABLETRIGGER' in chapter `Trigger` in the manual)"""
 54        self.card.cmd(M2CMD_CARD_DISABLETRIGGER)
 55    
 56    def force(self) -> None:
 57        """Forces a trigger event if the hardware is still waiting for a trigger event. (see command 'M2CMD_CARD_FORCETRIGGER' in chapter `Trigger` in the manual)"""
 58        self.card.cmd(M2CMD_CARD_FORCETRIGGER)
 59    
 60    def write_setup(self) -> None:
 61        """Write the trigger setup to the card"""
 62        self.card.write_setup()
 63    
 64    # OR Mask
 65    def or_mask(self, mask : int = None) -> int:
 66        """
 67        Set the OR mask for the trigger input lines (see register 'SPC_TRIG_ORMASK' in chapter `Trigger` in the manual)
 68        
 69        Parameters
 70        ----------
 71        mask : int
 72            The OR mask for the trigger input lines
 73        
 74        Returns
 75        -------
 76        int
 77            The OR mask for the trigger input lines
 78        """
 79
 80        if mask is not None:
 81            self.card.set_i(SPC_TRIG_ORMASK, mask)
 82        return self.card.get_i(SPC_TRIG_ORMASK)
 83
 84    # AND Mask
 85    def and_mask(self, mask : int = None) -> int:
 86        """
 87        Set the AND mask for the trigger input lines (see register 'SPC_TRIG_ANDMASK' in chapter `Trigger` in the manual)
 88        
 89        Parameters
 90        ----------
 91        mask : int
 92            The AND mask for the trigger input lines
 93        
 94        Returns
 95        -------
 96        int
 97            The AND mask for the trigger input lines
 98        """
 99
100        if mask is not None:
101            self.card.set_i(SPC_TRIG_ANDMASK, mask)
102        return self.card.get_i(SPC_TRIG_ANDMASK)
103
104    # Channel triggering
105    def ch_mode(self, channel, mode : int = None) -> int:
106        """
107        Set the mode for the trigger input lines (see register 'SPC_TRIG_CH0_MODE' in chapter `Trigger` in the manual)
108        
109        Parameters
110        ----------
111        channel : int | Channel
112            The channel to set the mode for
113        mode : int
114            The mode for the trigger input lines
115        
116        Returns
117        -------
118        int
119            The mode for the trigger input lines
120        
121        """
122
123        channel_index = int(channel)
124        if mode is not None:
125            self.card.set_i(SPC_TRIG_CH0_MODE + channel_index, mode)
126        return self.card.get_i(SPC_TRIG_CH0_MODE + channel_index)
127
128    def ch_level(self, channel : int, level_num : int, level_value = None, return_unit : pint.Unit = None) -> int:
129        """
130        Set the level for the trigger input lines (see register 'SPC_TRIG_CH0_LEVEL0' in chapter `Trigger` in the manual)
131        
132        Parameters
133        ----------
134        channel : int | Channel
135            The channel to set the level for
136        level_num : int
137            The level 0 or level 1
138        level_value : int | pint.Quantity | None
139            The level for the trigger input lines
140        
141        Returns
142        -------
143        int
144            The level for the trigger input lines
145        """
146
147        channel_index = int(channel)
148        # if a level value is given in the form of a quantity, convert it to the card's unit as a integer value
149        if isinstance(level_value, units.Quantity):
150            if isinstance(channel, Channel):
151                level_value = channel.reconvert_data(level_value)
152            elif self.channels and isinstance(self.channels[channel_index], Channel):
153                level_value = self.channels[channel_index].reconvert_data(level_value)
154            else:
155                raise ValueError("No channel information available to convert the trigger level value. Please provide a channel object or set the channel information in the Trigger object.")
156        
157        if isinstance(level_value, int):
158            self.card.set_i(SPC_TRIG_CH0_LEVEL0 + channel_index + 100 * level_num, level_value)
159
160        return_value = self.card.get_i(SPC_TRIG_CH0_LEVEL0 + channel_index + 100 * level_num)
161        # if a return unit is given, convert the value to the given unit if a channel object is available
162        if isinstance(return_unit, pint.Unit):
163            if isinstance(channel, Channel):
164                return_value = channel.convert_data(return_value, return_unit=return_unit)
165            elif self.channels and isinstance(self.channels[channel_index], Channel):
166                return_value = self.channels[channel_index].convert_data(return_value, return_unit=return_unit)
167            else:
168                raise ValueError("No channel information available to convert the returning trigger level value. Please provide a channel object or set the channel information in the Trigger object.")
169            
170        return return_value
171
172    def ch_level0(self, channel : int, level_value = None, return_unit : pint.Unit = None) -> int:
173        """
174        Set the level 0 for the trigger input lines (see register 'SPC_TRIG_CH0_LEVEL0' in chapter `Trigger` in the manual)
175        
176        Parameters
177        ----------
178        channel : int | Channel
179            The channel to set the level for
180        level_value : int | pint.Quantity | None
181            The level for the trigger input lines
182        
183        Returns
184        -------
185        int
186            The level for the trigger input lines
187        """
188
189        return self.ch_level(channel, 0, level_value, return_unit)
190    
191    def ch_level1(self, channel : int, level_value = None, return_unit : pint.Unit = None) -> int:
192        """
193        Set the level 1 for the trigger input lines (see register 'SPC_TRIG_CH0_LEVEL1' in chapter `Trigger` in the manual)
194        
195        Parameters
196        ----------
197        channel : int | Channel
198            The channel to set the level for
199        level_value : int | pint.Quantity | None
200            The level for the trigger input lines
201        
202        Returns
203        -------
204        int
205            The level for the trigger input lines
206        """
207
208        return self.ch_level(channel, 1, level_value, return_unit)
209
210    # Channel OR Mask0
211    def ch_or_mask0(self, mask : int = None) -> int:
212        """
213        Set the channel OR mask0 for the trigger input lines (see register 'SPC_TRIG_CH_ORMASK0' in chapter `Trigger` in the manual)
214        
215        Parameters
216        ----------
217        mask : int
218            The OR mask for the trigger input lines
219        
220        Returns
221        -------
222        int
223            The OR mask for the trigger input lines
224        """
225
226        if mask is not None:
227            self.card.set_i(SPC_TRIG_CH_ORMASK0, mask)
228        return self.card.get_i(SPC_TRIG_CH_ORMASK0)
229    
230    # Channel AND Mask0
231    def ch_and_mask0(self, mask : int = None) -> int:
232        """
233        Set the AND mask0 for the trigger input lines (see register 'SPC_TRIG_CH_ANDMASK0' in chapter `Trigger` in the manual)
234        
235        Parameters
236        ----------
237        mask : int
238            The AND mask0 for the trigger input lines
239        
240        Returns
241        -------
242        int
243            The AND mask0 for the trigger input lines
244        """
245
246        if mask is not None:
247            self.card.set_i(SPC_TRIG_CH_ANDMASK0, mask)
248        return self.card.get_i(SPC_TRIG_CH_ANDMASK0)
249    
250    # Delay
251    def delay(self, delay : int = None, return_unit : pint.Unit = None) -> int:
252        """
253        Set the delay for the trigger input lines in number of sample clocks (see register 'SPC_TRIG_DELAY' in chapter `Trigger` in the manual)
254        
255        Parameters
256        ----------
257        delay : int | pint.Quantity
258            The delay for the trigger input lines
259        return_unit : pint.Unit
260            The unit to return the value in
261
262        Returns
263        -------
264        int | pint.Quantity
265            The delay for the trigger input lines
266
267        NOTE
268        ----
269        different cards have different step sizes for the delay. 
270        If a delay with unit is given, this function takes the value, 
271        calculates the integer value and rounds to the nearest allowed delay value
272        """
273
274        sr = self.card.get_i(SPC_SAMPLERATE) * units.Hz
275        if delay is not None:
276            if isinstance(delay, units.Quantity):
277                delay_step = self.card.get_i(SPC_TRIG_AVAILDELAY_STEP)
278                delay = np.rint(int(delay * sr) / delay_step).astype(np.int64) * delay_step
279            self.card.set_i(SPC_TRIG_DELAY, delay)
280        return_value = self.card.get_i(SPC_TRIG_DELAY)
281        if return_unit is not None: 
282            return_value = UnitConversion.to_unit(return_value / sr, return_unit)
283        return return_value
284    
285    def trigger_counter(self) -> int:
286        """
287        Get the number of trigger events since acquisition start (see register 'SPC_TRIGGERCOUNTER' in chapter `Trigger` in the manual)
288        
289        Returns
290        -------
291        int
292            The trigger counter
293        """
294
295        return self.card.get_i(SPC_TRIGGERCOUNTER)
296    
297    # Main external window trigger (ext0/Trg0)
298    def ext0_mode(self, mode : int = None) -> int:
299        """
300        Set the mode for the main external window trigger (ext0/Trg0) (see register 'SPC_TRIG_EXT0_MODE' in chapter `Trigger` in the manual)
301        
302        Parameters
303        ----------
304        mode : int
305            The mode for the main external window trigger (ext0/Trg0)
306        
307        Returns
308        -------
309        int
310            The mode for the main external window trigger (ext0/Trg0)
311        """
312
313        if mode is not None:
314            self.card.set_i(SPC_TRIG_EXT0_MODE, mode)
315        return self.card.get_i(SPC_TRIG_EXT0_MODE)
316    
317    # Trigger termination
318    def termination(self, termination : int = None) -> int:
319        """
320        Set the trigger termination (see register 'SPC_TRIG_TERM' in chapter `Trigger` in the manual)
321        
322        Parameters
323        ----------
324        termination : int
325            The trigger termination: a „1“ sets the 50 Ohm termination for external trigger signals. A „0“ sets the high impedance termination
326        
327        Returns
328        -------
329        int
330            The trigger termination: a „1“ sets the 50 Ohm termination for external trigger signals. A „0“ sets the high impedance termination
331        """
332
333        if termination is not None:
334            self.card.set_i(SPC_TRIG_TERM, termination)
335        return self.card.get_i(SPC_TRIG_TERM)
336    
337    # Trigger input coupling
338    def ext0_coupling(self, coupling : int = None) -> int:
339        """
340        Set the trigger input coupling (see hardware manual register name 'SPC_TRIG_EXT0_ACDC')
341        
342        Parameters
343        ----------
344        coupling : int
345            The trigger input coupling: COUPLING_DC enables DC coupling, COUPLING_AC enables AC coupling for the external trigger 
346            input (AC coupling is the default).
347
348        Returns
349        -------
350        int
351            The trigger input coupling: COUPLING_DC enables DC coupling, COUPLING_AC enables AC coupling for the external trigger 
352            input (AC coupling is the default).
353        """
354
355        if coupling is not None:
356            self.card.set_i(SPC_TRIG_EXT0_ACDC, coupling)
357        return self.card.get_i(SPC_TRIG_EXT0_ACDC)
358    
359    # ext1 trigger mode
360    def ext1_mode(self, mode : int = None) -> int:
361        """
362        Set the mode for the ext1 trigger (see register 'SPC_TRIG_EXT1_MODE' in chapter `Trigger` in the manual)
363        
364        Parameters
365        ----------
366        mode : int
367            The mode for the ext1 trigger
368        
369        Returns
370        -------
371        int
372            The mode for the ext1 trigger
373        """
374
375        if mode is not None:
376            self.card.set_i(SPC_TRIG_EXT1_MODE, mode)
377        return self.card.get_i(SPC_TRIG_EXT1_MODE)
378    
379    # Trigger level
380    def ext0_level0(self, level = None, return_unit = None) -> int:
381        """
382        Set the trigger level 0 for the ext0 trigger (see register 'SPC_TRIG_EXT0_LEVEL0' in chapter `Trigger` in the manual)
383        
384        Parameters
385        ----------
386        level : int
387            The trigger level 0 for the ext0 trigger in mV
388        return_unit : pint.Unit
389            The unit to return the value in
390        
391        Returns
392        -------
393        int | pint.Quantity
394            The trigger level 0 for the ext0 trigger in mV or in the specified unit
395        """
396
397        if level is not None:
398            level = UnitConversion.convert(level, units.mV, int)
399            self.card.set_i(SPC_TRIG_EXT0_LEVEL0, level)
400        return_value = self.card.get_i(SPC_TRIG_EXT0_LEVEL0)
401        if return_unit is not None: return UnitConversion.to_unit(return_value * units.mV, return_unit)
402        return return_value
403    
404    def ext0_level1(self, level = None, return_unit = None) -> int:
405        """
406        Set the trigger level 1 for the ext0 trigger (see register 'SPC_TRIG_EXT0_LEVEL1' in chapter `Trigger` in the manual)
407        
408        Parameters
409        ----------
410        level : int
411            The trigger level for the ext0 trigger in mV
412        return_unit : pint.Unit
413            The unit to return the value in
414        
415        Returns
416        -------
417        int | pint.Quantity
418            The trigger level for the ext0 trigger in mV or in the specified unit
419        """
420
421        if level is not None:
422            level = UnitConversion.convert(level, units.mV, int)
423            self.card.set_i(SPC_TRIG_EXT0_LEVEL1, level)
424        return_value = self.card.get_i(SPC_TRIG_EXT0_LEVEL1)
425        if return_unit is not None: return UnitConversion.to_unit(return_value * units.mV, return_unit)
426        return return_value
427    
428    def ext1_level0(self, level = None, return_unit = None) -> int:
429        """
430        Set the trigger level 0 for the ext1 trigger (see register 'SPC_TRIG_EXT1_LEVEL0' in chapter `Trigger` in the manual)
431        
432        Parameters
433        ----------
434        level : int
435            The trigger level 0 for the ext1 trigger in mV
436        return_unit : pint.Unit
437            The unit to return the value in
438        
439        Returns
440        -------
441        int | pint.Quantity
442            The trigger level 0 for the ext1 trigger in mV or in the specified unit
443        """
444
445        if level is not None:
446            level = UnitConversion.convert(level, units.mV, int)
447            self.card.set_i(SPC_TRIG_EXT1_LEVEL0, level)
448        return_value = self.card.get_i(SPC_TRIG_EXT1_LEVEL0)
449        if return_unit is not None: return UnitConversion.to_unit(return_value * units.mV, return_unit)
450        return return_value

a higher-level abstraction of the CardFunctionality class to implement the Card's Trigger engine

Trigger(card: Card, **kwargs)
21    def __init__(self, card : 'Card', **kwargs) -> None:
22        """
23        Constructor of the Trigger class
24        
25        Parameters
26        ----------
27        card : Card
28            The card to use for the Trigger class
29        """
30
31        super().__init__(card)
32        self.channels = kwargs.get('channels', None)

Constructor of the Trigger class

Parameters
  • card (Card): The card to use for the Trigger class
channels: Channels = None
def enable(self) -> None:
48    def enable(self) -> None:
49        """Enables the trigger engine (see command 'M2CMD_CARD_ENABLETRIGGER' in chapter `Trigger` in the manual)"""
50        self.card.cmd(M2CMD_CARD_ENABLETRIGGER)

Enables the trigger engine (see command 'M2CMD_CARD_ENABLETRIGGER' in chapter Trigger in the manual)

def disable(self) -> None:
52    def disable(self) -> None:
53        """Disables the trigger engine (see command 'M2CMD_CARD_DISABLETRIGGER' in chapter `Trigger` in the manual)"""
54        self.card.cmd(M2CMD_CARD_DISABLETRIGGER)

Disables the trigger engine (see command 'M2CMD_CARD_DISABLETRIGGER' in chapter Trigger in the manual)

def force(self) -> None:
56    def force(self) -> None:
57        """Forces a trigger event if the hardware is still waiting for a trigger event. (see command 'M2CMD_CARD_FORCETRIGGER' in chapter `Trigger` in the manual)"""
58        self.card.cmd(M2CMD_CARD_FORCETRIGGER)

Forces a trigger event if the hardware is still waiting for a trigger event. (see command 'M2CMD_CARD_FORCETRIGGER' in chapter Trigger in the manual)

def write_setup(self) -> None:
60    def write_setup(self) -> None:
61        """Write the trigger setup to the card"""
62        self.card.write_setup()

Write the trigger setup to the card

def or_mask(self, mask: int = None) -> int:
65    def or_mask(self, mask : int = None) -> int:
66        """
67        Set the OR mask for the trigger input lines (see register 'SPC_TRIG_ORMASK' in chapter `Trigger` in the manual)
68        
69        Parameters
70        ----------
71        mask : int
72            The OR mask for the trigger input lines
73        
74        Returns
75        -------
76        int
77            The OR mask for the trigger input lines
78        """
79
80        if mask is not None:
81            self.card.set_i(SPC_TRIG_ORMASK, mask)
82        return self.card.get_i(SPC_TRIG_ORMASK)

Set the OR mask for the trigger input lines (see register 'SPC_TRIG_ORMASK' in chapter Trigger in the manual)

Parameters
  • mask (int): The OR mask for the trigger input lines
Returns
  • int: The OR mask for the trigger input lines
def and_mask(self, mask: int = None) -> int:
 85    def and_mask(self, mask : int = None) -> int:
 86        """
 87        Set the AND mask for the trigger input lines (see register 'SPC_TRIG_ANDMASK' in chapter `Trigger` in the manual)
 88        
 89        Parameters
 90        ----------
 91        mask : int
 92            The AND mask for the trigger input lines
 93        
 94        Returns
 95        -------
 96        int
 97            The AND mask for the trigger input lines
 98        """
 99
100        if mask is not None:
101            self.card.set_i(SPC_TRIG_ANDMASK, mask)
102        return self.card.get_i(SPC_TRIG_ANDMASK)

Set the AND mask for the trigger input lines (see register 'SPC_TRIG_ANDMASK' in chapter Trigger in the manual)

Parameters
  • mask (int): The AND mask for the trigger input lines
Returns
  • int: The AND mask for the trigger input lines
def ch_mode(self, channel, mode: int = None) -> int:
105    def ch_mode(self, channel, mode : int = None) -> int:
106        """
107        Set the mode for the trigger input lines (see register 'SPC_TRIG_CH0_MODE' in chapter `Trigger` in the manual)
108        
109        Parameters
110        ----------
111        channel : int | Channel
112            The channel to set the mode for
113        mode : int
114            The mode for the trigger input lines
115        
116        Returns
117        -------
118        int
119            The mode for the trigger input lines
120        
121        """
122
123        channel_index = int(channel)
124        if mode is not None:
125            self.card.set_i(SPC_TRIG_CH0_MODE + channel_index, mode)
126        return self.card.get_i(SPC_TRIG_CH0_MODE + channel_index)

Set the mode for the trigger input lines (see register 'SPC_TRIG_CH0_MODE' in chapter Trigger in the manual)

Parameters
  • channel (int | Channel): The channel to set the mode for
  • mode (int): The mode for the trigger input lines
Returns
  • int: The mode for the trigger input lines
def ch_level( self, channel: int, level_num: int, level_value=None, return_unit: pint.registry.Unit = None) -> int:
128    def ch_level(self, channel : int, level_num : int, level_value = None, return_unit : pint.Unit = None) -> int:
129        """
130        Set the level for the trigger input lines (see register 'SPC_TRIG_CH0_LEVEL0' in chapter `Trigger` in the manual)
131        
132        Parameters
133        ----------
134        channel : int | Channel
135            The channel to set the level for
136        level_num : int
137            The level 0 or level 1
138        level_value : int | pint.Quantity | None
139            The level for the trigger input lines
140        
141        Returns
142        -------
143        int
144            The level for the trigger input lines
145        """
146
147        channel_index = int(channel)
148        # if a level value is given in the form of a quantity, convert it to the card's unit as a integer value
149        if isinstance(level_value, units.Quantity):
150            if isinstance(channel, Channel):
151                level_value = channel.reconvert_data(level_value)
152            elif self.channels and isinstance(self.channels[channel_index], Channel):
153                level_value = self.channels[channel_index].reconvert_data(level_value)
154            else:
155                raise ValueError("No channel information available to convert the trigger level value. Please provide a channel object or set the channel information in the Trigger object.")
156        
157        if isinstance(level_value, int):
158            self.card.set_i(SPC_TRIG_CH0_LEVEL0 + channel_index + 100 * level_num, level_value)
159
160        return_value = self.card.get_i(SPC_TRIG_CH0_LEVEL0 + channel_index + 100 * level_num)
161        # if a return unit is given, convert the value to the given unit if a channel object is available
162        if isinstance(return_unit, pint.Unit):
163            if isinstance(channel, Channel):
164                return_value = channel.convert_data(return_value, return_unit=return_unit)
165            elif self.channels and isinstance(self.channels[channel_index], Channel):
166                return_value = self.channels[channel_index].convert_data(return_value, return_unit=return_unit)
167            else:
168                raise ValueError("No channel information available to convert the returning trigger level value. Please provide a channel object or set the channel information in the Trigger object.")
169            
170        return return_value

Set the level for the trigger input lines (see register 'SPC_TRIG_CH0_LEVEL0' in chapter Trigger in the manual)

Parameters
  • channel (int | Channel): The channel to set the level for
  • level_num (int): The level 0 or level 1
  • level_value (int | pint.Quantity | None): The level for the trigger input lines
Returns
  • int: The level for the trigger input lines
def ch_level0( self, channel: int, level_value=None, return_unit: pint.registry.Unit = None) -> int:
172    def ch_level0(self, channel : int, level_value = None, return_unit : pint.Unit = None) -> int:
173        """
174        Set the level 0 for the trigger input lines (see register 'SPC_TRIG_CH0_LEVEL0' in chapter `Trigger` in the manual)
175        
176        Parameters
177        ----------
178        channel : int | Channel
179            The channel to set the level for
180        level_value : int | pint.Quantity | None
181            The level for the trigger input lines
182        
183        Returns
184        -------
185        int
186            The level for the trigger input lines
187        """
188
189        return self.ch_level(channel, 0, level_value, return_unit)

Set the level 0 for the trigger input lines (see register 'SPC_TRIG_CH0_LEVEL0' in chapter Trigger in the manual)

Parameters
  • channel (int | Channel): The channel to set the level for
  • level_value (int | pint.Quantity | None): The level for the trigger input lines
Returns
  • int: The level for the trigger input lines
def ch_level1( self, channel: int, level_value=None, return_unit: pint.registry.Unit = None) -> int:
191    def ch_level1(self, channel : int, level_value = None, return_unit : pint.Unit = None) -> int:
192        """
193        Set the level 1 for the trigger input lines (see register 'SPC_TRIG_CH0_LEVEL1' in chapter `Trigger` in the manual)
194        
195        Parameters
196        ----------
197        channel : int | Channel
198            The channel to set the level for
199        level_value : int | pint.Quantity | None
200            The level for the trigger input lines
201        
202        Returns
203        -------
204        int
205            The level for the trigger input lines
206        """
207
208        return self.ch_level(channel, 1, level_value, return_unit)

Set the level 1 for the trigger input lines (see register 'SPC_TRIG_CH0_LEVEL1' in chapter Trigger in the manual)

Parameters
  • channel (int | Channel): The channel to set the level for
  • level_value (int | pint.Quantity | None): The level for the trigger input lines
Returns
  • int: The level for the trigger input lines
def ch_or_mask0(self, mask: int = None) -> int:
211    def ch_or_mask0(self, mask : int = None) -> int:
212        """
213        Set the channel OR mask0 for the trigger input lines (see register 'SPC_TRIG_CH_ORMASK0' in chapter `Trigger` in the manual)
214        
215        Parameters
216        ----------
217        mask : int
218            The OR mask for the trigger input lines
219        
220        Returns
221        -------
222        int
223            The OR mask for the trigger input lines
224        """
225
226        if mask is not None:
227            self.card.set_i(SPC_TRIG_CH_ORMASK0, mask)
228        return self.card.get_i(SPC_TRIG_CH_ORMASK0)

Set the channel OR mask0 for the trigger input lines (see register 'SPC_TRIG_CH_ORMASK0' in chapter Trigger in the manual)

Parameters
  • mask (int): The OR mask for the trigger input lines
Returns
  • int: The OR mask for the trigger input lines
def ch_and_mask0(self, mask: int = None) -> int:
231    def ch_and_mask0(self, mask : int = None) -> int:
232        """
233        Set the AND mask0 for the trigger input lines (see register 'SPC_TRIG_CH_ANDMASK0' in chapter `Trigger` in the manual)
234        
235        Parameters
236        ----------
237        mask : int
238            The AND mask0 for the trigger input lines
239        
240        Returns
241        -------
242        int
243            The AND mask0 for the trigger input lines
244        """
245
246        if mask is not None:
247            self.card.set_i(SPC_TRIG_CH_ANDMASK0, mask)
248        return self.card.get_i(SPC_TRIG_CH_ANDMASK0)

Set the AND mask0 for the trigger input lines (see register 'SPC_TRIG_CH_ANDMASK0' in chapter Trigger in the manual)

Parameters
  • mask (int): The AND mask0 for the trigger input lines
Returns
  • int: The AND mask0 for the trigger input lines
def delay(self, delay: int = None, return_unit: pint.registry.Unit = None) -> int:
251    def delay(self, delay : int = None, return_unit : pint.Unit = None) -> int:
252        """
253        Set the delay for the trigger input lines in number of sample clocks (see register 'SPC_TRIG_DELAY' in chapter `Trigger` in the manual)
254        
255        Parameters
256        ----------
257        delay : int | pint.Quantity
258            The delay for the trigger input lines
259        return_unit : pint.Unit
260            The unit to return the value in
261
262        Returns
263        -------
264        int | pint.Quantity
265            The delay for the trigger input lines
266
267        NOTE
268        ----
269        different cards have different step sizes for the delay. 
270        If a delay with unit is given, this function takes the value, 
271        calculates the integer value and rounds to the nearest allowed delay value
272        """
273
274        sr = self.card.get_i(SPC_SAMPLERATE) * units.Hz
275        if delay is not None:
276            if isinstance(delay, units.Quantity):
277                delay_step = self.card.get_i(SPC_TRIG_AVAILDELAY_STEP)
278                delay = np.rint(int(delay * sr) / delay_step).astype(np.int64) * delay_step
279            self.card.set_i(SPC_TRIG_DELAY, delay)
280        return_value = self.card.get_i(SPC_TRIG_DELAY)
281        if return_unit is not None: 
282            return_value = UnitConversion.to_unit(return_value / sr, return_unit)
283        return return_value

Set the delay for the trigger input lines in number of sample clocks (see register 'SPC_TRIG_DELAY' in chapter Trigger in the manual)

Parameters
  • delay (int | pint.Quantity): The delay for the trigger input lines
  • return_unit (pint.Unit): The unit to return the value in
Returns
  • int | pint.Quantity: The delay for the trigger input lines
NOTE

different cards have different step sizes for the delay. If a delay with unit is given, this function takes the value, calculates the integer value and rounds to the nearest allowed delay value

def trigger_counter(self) -> int:
285    def trigger_counter(self) -> int:
286        """
287        Get the number of trigger events since acquisition start (see register 'SPC_TRIGGERCOUNTER' in chapter `Trigger` in the manual)
288        
289        Returns
290        -------
291        int
292            The trigger counter
293        """
294
295        return self.card.get_i(SPC_TRIGGERCOUNTER)

Get the number of trigger events since acquisition start (see register 'SPC_TRIGGERCOUNTER' in chapter Trigger in the manual)

Returns
  • int: The trigger counter
def ext0_mode(self, mode: int = None) -> int:
298    def ext0_mode(self, mode : int = None) -> int:
299        """
300        Set the mode for the main external window trigger (ext0/Trg0) (see register 'SPC_TRIG_EXT0_MODE' in chapter `Trigger` in the manual)
301        
302        Parameters
303        ----------
304        mode : int
305            The mode for the main external window trigger (ext0/Trg0)
306        
307        Returns
308        -------
309        int
310            The mode for the main external window trigger (ext0/Trg0)
311        """
312
313        if mode is not None:
314            self.card.set_i(SPC_TRIG_EXT0_MODE, mode)
315        return self.card.get_i(SPC_TRIG_EXT0_MODE)

Set the mode for the main external window trigger (ext0/Trg0) (see register 'SPC_TRIG_EXT0_MODE' in chapter Trigger in the manual)

Parameters
  • mode (int): The mode for the main external window trigger (ext0/Trg0)
Returns
  • int: The mode for the main external window trigger (ext0/Trg0)
def termination(self, termination: int = None) -> int:
318    def termination(self, termination : int = None) -> int:
319        """
320        Set the trigger termination (see register 'SPC_TRIG_TERM' in chapter `Trigger` in the manual)
321        
322        Parameters
323        ----------
324        termination : int
325            The trigger termination: a „1“ sets the 50 Ohm termination for external trigger signals. A „0“ sets the high impedance termination
326        
327        Returns
328        -------
329        int
330            The trigger termination: a „1“ sets the 50 Ohm termination for external trigger signals. A „0“ sets the high impedance termination
331        """
332
333        if termination is not None:
334            self.card.set_i(SPC_TRIG_TERM, termination)
335        return self.card.get_i(SPC_TRIG_TERM)

Set the trigger termination (see register 'SPC_TRIG_TERM' in chapter Trigger in the manual)

Parameters
  • termination (int): The trigger termination: a „1“ sets the 50 Ohm termination for external trigger signals. A „0“ sets the high impedance termination
Returns
  • int: The trigger termination: a „1“ sets the 50 Ohm termination for external trigger signals. A „0“ sets the high impedance termination
def ext0_coupling(self, coupling: int = None) -> int:
338    def ext0_coupling(self, coupling : int = None) -> int:
339        """
340        Set the trigger input coupling (see hardware manual register name 'SPC_TRIG_EXT0_ACDC')
341        
342        Parameters
343        ----------
344        coupling : int
345            The trigger input coupling: COUPLING_DC enables DC coupling, COUPLING_AC enables AC coupling for the external trigger 
346            input (AC coupling is the default).
347
348        Returns
349        -------
350        int
351            The trigger input coupling: COUPLING_DC enables DC coupling, COUPLING_AC enables AC coupling for the external trigger 
352            input (AC coupling is the default).
353        """
354
355        if coupling is not None:
356            self.card.set_i(SPC_TRIG_EXT0_ACDC, coupling)
357        return self.card.get_i(SPC_TRIG_EXT0_ACDC)

Set the trigger input coupling (see hardware manual register name 'SPC_TRIG_EXT0_ACDC')

Parameters
  • coupling (int): The trigger input coupling: COUPLING_DC enables DC coupling, COUPLING_AC enables AC coupling for the external trigger input (AC coupling is the default).
Returns
  • int: The trigger input coupling: COUPLING_DC enables DC coupling, COUPLING_AC enables AC coupling for the external trigger input (AC coupling is the default).
def ext1_mode(self, mode: int = None) -> int:
360    def ext1_mode(self, mode : int = None) -> int:
361        """
362        Set the mode for the ext1 trigger (see register 'SPC_TRIG_EXT1_MODE' in chapter `Trigger` in the manual)
363        
364        Parameters
365        ----------
366        mode : int
367            The mode for the ext1 trigger
368        
369        Returns
370        -------
371        int
372            The mode for the ext1 trigger
373        """
374
375        if mode is not None:
376            self.card.set_i(SPC_TRIG_EXT1_MODE, mode)
377        return self.card.get_i(SPC_TRIG_EXT1_MODE)

Set the mode for the ext1 trigger (see register 'SPC_TRIG_EXT1_MODE' in chapter Trigger in the manual)

Parameters
  • mode (int): The mode for the ext1 trigger
Returns
  • int: The mode for the ext1 trigger
def ext0_level0(self, level=None, return_unit=None) -> int:
380    def ext0_level0(self, level = None, return_unit = None) -> int:
381        """
382        Set the trigger level 0 for the ext0 trigger (see register 'SPC_TRIG_EXT0_LEVEL0' in chapter `Trigger` in the manual)
383        
384        Parameters
385        ----------
386        level : int
387            The trigger level 0 for the ext0 trigger in mV
388        return_unit : pint.Unit
389            The unit to return the value in
390        
391        Returns
392        -------
393        int | pint.Quantity
394            The trigger level 0 for the ext0 trigger in mV or in the specified unit
395        """
396
397        if level is not None:
398            level = UnitConversion.convert(level, units.mV, int)
399            self.card.set_i(SPC_TRIG_EXT0_LEVEL0, level)
400        return_value = self.card.get_i(SPC_TRIG_EXT0_LEVEL0)
401        if return_unit is not None: return UnitConversion.to_unit(return_value * units.mV, return_unit)
402        return return_value

Set the trigger level 0 for the ext0 trigger (see register 'SPC_TRIG_EXT0_LEVEL0' in chapter Trigger in the manual)

Parameters
  • level (int): The trigger level 0 for the ext0 trigger in mV
  • return_unit (pint.Unit): The unit to return the value in
Returns
  • int | pint.Quantity: The trigger level 0 for the ext0 trigger in mV or in the specified unit
def ext0_level1(self, level=None, return_unit=None) -> int:
404    def ext0_level1(self, level = None, return_unit = None) -> int:
405        """
406        Set the trigger level 1 for the ext0 trigger (see register 'SPC_TRIG_EXT0_LEVEL1' in chapter `Trigger` in the manual)
407        
408        Parameters
409        ----------
410        level : int
411            The trigger level for the ext0 trigger in mV
412        return_unit : pint.Unit
413            The unit to return the value in
414        
415        Returns
416        -------
417        int | pint.Quantity
418            The trigger level for the ext0 trigger in mV or in the specified unit
419        """
420
421        if level is not None:
422            level = UnitConversion.convert(level, units.mV, int)
423            self.card.set_i(SPC_TRIG_EXT0_LEVEL1, level)
424        return_value = self.card.get_i(SPC_TRIG_EXT0_LEVEL1)
425        if return_unit is not None: return UnitConversion.to_unit(return_value * units.mV, return_unit)
426        return return_value

Set the trigger level 1 for the ext0 trigger (see register 'SPC_TRIG_EXT0_LEVEL1' in chapter Trigger in the manual)

Parameters
  • level (int): The trigger level for the ext0 trigger in mV
  • return_unit (pint.Unit): The unit to return the value in
Returns
  • int | pint.Quantity: The trigger level for the ext0 trigger in mV or in the specified unit
def ext1_level0(self, level=None, return_unit=None) -> int:
428    def ext1_level0(self, level = None, return_unit = None) -> int:
429        """
430        Set the trigger level 0 for the ext1 trigger (see register 'SPC_TRIG_EXT1_LEVEL0' in chapter `Trigger` in the manual)
431        
432        Parameters
433        ----------
434        level : int
435            The trigger level 0 for the ext1 trigger in mV
436        return_unit : pint.Unit
437            The unit to return the value in
438        
439        Returns
440        -------
441        int | pint.Quantity
442            The trigger level 0 for the ext1 trigger in mV or in the specified unit
443        """
444
445        if level is not None:
446            level = UnitConversion.convert(level, units.mV, int)
447            self.card.set_i(SPC_TRIG_EXT1_LEVEL0, level)
448        return_value = self.card.get_i(SPC_TRIG_EXT1_LEVEL0)
449        if return_unit is not None: return UnitConversion.to_unit(return_value * units.mV, return_unit)
450        return return_value

Set the trigger level 0 for the ext1 trigger (see register 'SPC_TRIG_EXT1_LEVEL0' in chapter Trigger in the manual)

Parameters
  • level (int): The trigger level 0 for the ext1 trigger in mV
  • return_unit (pint.Unit): The unit to return the value in
Returns
  • int | pint.Quantity: The trigger level 0 for the ext1 trigger in mV or in the specified unit
class MultiPurposeIOs(spcm.CardFunctionality):
 89class MultiPurposeIOs(CardFunctionality):
 90    """a higher-level abstraction of the CardFunctionality class to implement the Card's Multi purpose I/O functionality"""
 91
 92    xio_lines : list[MultiPurposeIO] = []
 93    num_xio_lines : int = None
 94
 95    def __init__(self, card : Card, *args, **kwargs) -> None:
 96        """
 97        Constructor for the MultiPurposeIO class
 98    
 99        Parameters
100        ----------
101        card : Card
102            The card object to communicate with the card
103        """
104
105        super().__init__(card, *args, **kwargs)
106        
107        self.xio_lines = []
108        self.num_xio_lines = self.get_num_xio_lines()
109        self.load()
110    
111    def __str__(self) -> str:
112        """
113        String representation of the MultiPurposeIO class
114    
115        Returns
116        -------
117        str
118            String representation of the MultiPurposeIO class
119        """
120        
121        return f"MultiPurposeIOs(card={self.card})"
122    
123    __repr__ = __str__
124    def __iter__(self) -> "MultiPurposeIOs":
125        """Define this class as an iterator"""
126        return self
127    
128    def __getitem__(self, index : int) -> MultiPurposeIO:
129        """
130        Get the xio line at the given index
131
132        Parameters
133        ----------
134        index : int
135            The index of the xio line to be returned
136        
137        Returns
138        -------
139        MultiPurposeIO
140            The xio line at the given index
141        """
142
143        return self.xio_lines[index]
144    
145    _xio_iterator_index = -1
146    def __next__(self) -> MultiPurposeIO:
147        """
148        This method is called when the next element is requested from the iterator
149
150        Returns
151        -------
152        MultiPurposeIO
153            The next xio line in the iterator
154        
155        Raises
156        ------
157        StopIteration
158        """
159        self._xio_iterator_index += 1
160        if self._xio_iterator_index >= len(self.xio_lines):
161            self._xio_iterator_index = -1
162            raise StopIteration
163        return self.xio_lines[self._xio_iterator_index]
164    
165    def __len__(self) -> int:
166        """Returns the number of available xio lines of the card"""
167        return len(self.xio_lines)
168    
169
170    def get_num_xio_lines(self) -> int:
171        """
172        Returns the number of digital input/output lines of the card (see register 'SPCM_NUM_XIO_LINES' in chapter `Multi Purpose I/O Lines` in the manual)
173    
174        Returns
175        -------
176        int
177            The number of digital input/output lines of the card
178
179        """
180
181        return self.card.get_i(SPC_NUM_XIO_LINES)
182
183    def load(self) -> None:
184        """
185        Loads the digital input/output lines of the card
186        """
187
188        self.xio_lines = [MultiPurposeIO(self.card, x_index) for x_index in range(self.num_xio_lines)]
189    
190    def asyncio(self, output : int = None) -> int:
191        """
192        Sets the async input/output of the card (see register 'SPCM_XX_ASYNCIO' in chapter `Multi Purpose I/O Lines` in the manual)
193    
194        Parameters
195        ----------
196        output : int
197            The async input/output of the card
198
199        Returns
200        -------
201        int
202            The async input/output of the card
203        """
204
205        if output is not None:
206            self.card.set_i(SPCM_XX_ASYNCIO, output)
207        return self.card.get_i(SPCM_XX_ASYNCIO)

a higher-level abstraction of the CardFunctionality class to implement the Card's Multi purpose I/O functionality

MultiPurposeIOs(card: Card, *args, **kwargs)
 95    def __init__(self, card : Card, *args, **kwargs) -> None:
 96        """
 97        Constructor for the MultiPurposeIO class
 98    
 99        Parameters
100        ----------
101        card : Card
102            The card object to communicate with the card
103        """
104
105        super().__init__(card, *args, **kwargs)
106        
107        self.xio_lines = []
108        self.num_xio_lines = self.get_num_xio_lines()
109        self.load()

Constructor for the MultiPurposeIO class

Parameters
  • card (Card): The card object to communicate with the card
xio_lines: list[MultiPurposeIO] = []
num_xio_lines: int = None
def get_num_xio_lines(self) -> int:
170    def get_num_xio_lines(self) -> int:
171        """
172        Returns the number of digital input/output lines of the card (see register 'SPCM_NUM_XIO_LINES' in chapter `Multi Purpose I/O Lines` in the manual)
173    
174        Returns
175        -------
176        int
177            The number of digital input/output lines of the card
178
179        """
180
181        return self.card.get_i(SPC_NUM_XIO_LINES)

Returns the number of digital input/output lines of the card (see register 'SPCM_NUM_XIO_LINES' in chapter Multi Purpose I/O Lines in the manual)

Returns
  • int: The number of digital input/output lines of the card
def load(self) -> None:
183    def load(self) -> None:
184        """
185        Loads the digital input/output lines of the card
186        """
187
188        self.xio_lines = [MultiPurposeIO(self.card, x_index) for x_index in range(self.num_xio_lines)]

Loads the digital input/output lines of the card

def asyncio(self, output: int = None) -> int:
190    def asyncio(self, output : int = None) -> int:
191        """
192        Sets the async input/output of the card (see register 'SPCM_XX_ASYNCIO' in chapter `Multi Purpose I/O Lines` in the manual)
193    
194        Parameters
195        ----------
196        output : int
197            The async input/output of the card
198
199        Returns
200        -------
201        int
202            The async input/output of the card
203        """
204
205        if output is not None:
206            self.card.set_i(SPCM_XX_ASYNCIO, output)
207        return self.card.get_i(SPCM_XX_ASYNCIO)

Sets the async input/output of the card (see register 'SPCM_XX_ASYNCIO' in chapter Multi Purpose I/O Lines in the manual)

Parameters
  • output (int): The async input/output of the card
Returns
  • int: The async input/output of the card
class MultiPurposeIO:
 9class MultiPurposeIO:
10    """a higher-level abstraction of the CardFunctionality class to implement the Card's Multi purpose I/O functionality"""
11
12    card : Card = None
13    x_index : int = None
14
15    def __init__(self, card : Card, x_index : int = None) -> None:
16        """
17        Constructor for the MultiPurposeIO class
18    
19        Parameters
20        ----------
21        card : Card
22            The card object to communicate with the card
23        x_index : int
24            The index of the digital input/output to be enabled.
25        """
26
27        self.card = card
28        self.x_index = x_index
29    
30    def __str__(self) -> str:
31        """
32        String representation of the MultiPurposeIO class
33    
34        Returns
35        -------
36        str
37            String representation of the MultiPurposeIO class
38        """
39        
40        return f"MultiPurposeIO(card={self.card}, x_index={self.x_index})"
41    
42    __repr__ = __str__
43
44    def avail_modes(self) -> int:
45        """
46        Returns the available modes of the digital input/output of the card (see register 'SPCM_X0_AVAILMODES' in chapter `Multi Purpose I/O Lines` in the manual)
47
48        Returns
49        -------
50        int
51            The available modes of the digital input/output
52        """
53
54        return self.get_i(SPCM_X0_AVAILMODES + self.x_index)
55
56    def x_mode(self, mode : int = None) -> int:
57        """
58        Sets the mode of the digital input/output of the card (see register 'SPCM_X0_MODE' in chapter `Multi Purpose I/O Lines` in the manual)
59    
60        Parameters
61        ----------
62        mode : int
63            The mode of the digital input/output
64        """
65
66        if mode is not None:
67            self.card.set_i(SPCM_X0_MODE + self.x_index, mode)
68        return self.card.get_i(SPCM_X0_MODE + self.x_index)
69    
70    def dig_mode(self, mode : int = None) -> int:
71        """
72        Sets the digital input/output mode of the xio line (see register 'SPCM_DIGMODE0' in chapter `Multi Purpose I/O Lines` in the manual)
73    
74        Parameters
75        ----------
76        mode : int
77            The digital input/output mode of the xio line
78
79        Returns
80        -------
81        int
82            The digital input/output mode of the xio line
83        """
84
85        if mode is not None:
86            self.card.set_i(SPC_DIGMODE0 + self.x_index, mode)
87        return self.card.get_i(SPC_DIGMODE0 + self.x_index)

a higher-level abstraction of the CardFunctionality class to implement the Card's Multi purpose I/O functionality

MultiPurposeIO(card: Card, x_index: int = None)
15    def __init__(self, card : Card, x_index : int = None) -> None:
16        """
17        Constructor for the MultiPurposeIO class
18    
19        Parameters
20        ----------
21        card : Card
22            The card object to communicate with the card
23        x_index : int
24            The index of the digital input/output to be enabled.
25        """
26
27        self.card = card
28        self.x_index = x_index

Constructor for the MultiPurposeIO class

Parameters
  • card (Card): The card object to communicate with the card
  • x_index (int): The index of the digital input/output to be enabled.
card: Card = None
x_index: int = None
def avail_modes(self) -> int:
44    def avail_modes(self) -> int:
45        """
46        Returns the available modes of the digital input/output of the card (see register 'SPCM_X0_AVAILMODES' in chapter `Multi Purpose I/O Lines` in the manual)
47
48        Returns
49        -------
50        int
51            The available modes of the digital input/output
52        """
53
54        return self.get_i(SPCM_X0_AVAILMODES + self.x_index)

Returns the available modes of the digital input/output of the card (see register 'SPCM_X0_AVAILMODES' in chapter Multi Purpose I/O Lines in the manual)

Returns
  • int: The available modes of the digital input/output
def x_mode(self, mode: int = None) -> int:
56    def x_mode(self, mode : int = None) -> int:
57        """
58        Sets the mode of the digital input/output of the card (see register 'SPCM_X0_MODE' in chapter `Multi Purpose I/O Lines` in the manual)
59    
60        Parameters
61        ----------
62        mode : int
63            The mode of the digital input/output
64        """
65
66        if mode is not None:
67            self.card.set_i(SPCM_X0_MODE + self.x_index, mode)
68        return self.card.get_i(SPCM_X0_MODE + self.x_index)

Sets the mode of the digital input/output of the card (see register 'SPCM_X0_MODE' in chapter Multi Purpose I/O Lines in the manual)

Parameters
  • mode (int): The mode of the digital input/output
def dig_mode(self, mode: int = None) -> int:
70    def dig_mode(self, mode : int = None) -> int:
71        """
72        Sets the digital input/output mode of the xio line (see register 'SPCM_DIGMODE0' in chapter `Multi Purpose I/O Lines` in the manual)
73    
74        Parameters
75        ----------
76        mode : int
77            The digital input/output mode of the xio line
78
79        Returns
80        -------
81        int
82            The digital input/output mode of the xio line
83        """
84
85        if mode is not None:
86            self.card.set_i(SPC_DIGMODE0 + self.x_index, mode)
87        return self.card.get_i(SPC_DIGMODE0 + self.x_index)

Sets the digital input/output mode of the xio line (see register 'SPCM_DIGMODE0' in chapter Multi Purpose I/O Lines in the manual)

Parameters
  • mode (int): The digital input/output mode of the xio line
Returns
  • int: The digital input/output mode of the xio line
class DataTransfer(spcm.CardFunctionality):
 21class DataTransfer(CardFunctionality):
 22    """
 23    A high-level class to control Data Transfer to and from Spectrum Instrumentation cards.
 24
 25    This class is an iterator class that implements the functions `__iter__` and `__next__`.
 26    This allows the user to supply the class to a for loop and iterate over the data that 
 27    is transferred from or to the card. Each iteration will return a numpy array with a data
 28    block of size `notify_samples`. In case of a digitizer you can read the data from that 
 29    block and process it. In case of a generator you can write data to the block and it's 
 30    then transferred.
 31
 32    For more information about what setups are available, please have a look at the user manual
 33    for your specific card.
 34
 35    Parameters
 36    ----------
 37    `buffer` : NDArray[np.int_]
 38        numpy object that can be used to write data into the spcm buffer
 39    `buffer_size`: int
 40        defines the size of the current buffer shared between the PC and the card
 41    `buffer_type`: int
 42        defines the type of data in the buffer that is used for the transfer
 43    `num_channels`: int
 44        defines the number of channels that are used for the transfer
 45    `bytes_per_sample`: int
 46        defines the number of bytes per sample
 47    `bits_per_sample`: int
 48        defines the number of bits per sample
 49
 50    """
 51    # public
 52    buffer_size : int = 0
 53    notify_size : int = 0
 54
 55    direction : Direction = Direction.Acquisition
 56
 57    buffer_type : int
 58    num_channels : int = 0
 59    bytes_per_sample : int = 0
 60    bits_per_sample : int = 0
 61
 62    current_user_pos : int = 0
 63
 64    # private
 65    _buffer_samples : int = 0
 66    _notify_samples : int = 0
 67
 68    @property
 69    def buffer(self) -> npt.NDArray[np.int_]:
 70        """
 71        The numpy buffer object that interfaces the Card and can be written and read from
 72        
 73        Returns
 74        -------
 75        numpy array
 76            the numpy buffer object with the following array index definition: 
 77            `[channel, sample]`
 78            or in case of multiple recording / replay:
 79            `[segment, sample, channel]`
 80        """
 81        return self._np_buffer
 82    
 83    @buffer.setter
 84    def buffer(self, value) -> None:
 85        self._np_buffer = value
 86    
 87    @buffer.deleter
 88    def buffer(self) -> None:
 89        del self._np_buffer
 90
 91    @property
 92    def buffer_samples(self) -> int:
 93        """
 94        The number of samples in the buffer
 95        
 96        Returns
 97        -------
 98        int
 99            the number of samples in the buffer
100        """
101        return self._buffer_samples
102    
103    @buffer_samples.setter
104    def buffer_samples(self, value) -> None:
105        if value is not None:
106            self._buffer_samples = value
107
108            self.buffer_size = self.samples_to_bytes(self._buffer_samples)
109
110            # if self.bits_per_sample > 1:
111            #     self.buffer_size = int(self._buffer_samples * self.bytes_per_sample * self.num_channels)
112            # else:
113            #     self.buffer_size = int(self._buffer_samples * self.num_channels // 8)
114    
115    @buffer_samples.deleter
116    def buffer_samples(self) -> None:
117        del self._buffer_samples
118    
119    def bytes_to_samples(self, num_bytes : int) -> int:
120        """
121        Convert bytes to samples
122        
123        Parameters
124        ----------
125        bytes : int
126            the number of bytes
127        
128        Returns
129        -------
130        int
131            the number of samples
132        """
133
134        if self.bits_per_sample > 1:
135            num_samples = num_bytes // self.bytes_per_sample // self.num_channels
136        else:
137            num_samples = num_bytes // self.num_channels * 8
138        return num_samples
139    
140    def samples_to_bytes(self, num_samples : int) -> int:
141        """
142        Convert samples to bytes
143        
144        Parameters
145        ----------
146        num_samples : int
147            the number of samples
148        
149        Returns
150        -------
151        int
152            the number of bytes
153        """
154
155        if self.bits_per_sample > 1:
156            num_bytes = num_samples * self.bytes_per_sample * self.num_channels
157        else:
158            num_bytes = num_samples * self.num_channels // 8
159        return num_bytes
160
161    # @property
162    # def notify_samples(self) -> int:
163    #     """
164    #     The number of samples to notify the user about
165        
166    #     Returns
167    #     -------
168    #     int
169    #         the number of samples to notify the user about
170    #     """
171    #     return self._notify_samples
172    
173    # @notify_samples.setter
174    def notify_samples(self, notify_samples : int = None) -> int:
175        """
176        Set the number of samples to notify the user about
177        
178        Parameters
179        ----------
180        notify_samples : int | pint.Quantity
181            the number of samples to notify the user about
182        """
183
184        if notify_samples is not None:
185            notify_samples = UnitConversion.convert(notify_samples, units.Sa, int)
186            self._notify_samples = notify_samples
187            self.notify_size = self.samples_to_bytes(self._notify_samples)
188            # self.notify_size = int(self._notify_samples * self.bytes_per_sample * self.num_channels)
189        return self._notify_samples
190    
191    # @notify_samples.deleter
192    # def notify_samples(self) -> None:
193    #     del self._notify_samples
194
195    # private
196    _memory_size : int = 0
197    _c_buffer = None # Internal numpy ctypes buffer object
198    _buffer_alignment : int = 4096
199    _np_buffer : npt.NDArray[np.int_] # Internal object on which the getter setter logic is working
200    _8bit_mode : bool = False
201    _12bit_mode : bool = False
202    _pre_trigger : int = 0
203
204    def __init__(self, card, *args, **kwargs) -> None:
205        """
206        Initialize the DataTransfer object with a card object and additional arguments
207
208        Parameters
209        ----------
210        card : Card
211            the card object that is used for the data transfer
212        *args : list
213            list of additional arguments
214        **kwargs : dict
215            dictionary of additional keyword arguments
216        """
217        
218        self.buffer_size = 0
219        self.notify_size = 0
220        self.num_channels = 0
221        self.bytes_per_sample = 0
222        self.bits_per_sample = 0
223
224        self.current_user_pos = 0
225
226        self._buffer_samples = 0
227        self._notify_samples = 0
228        self._memory_size = 0
229        self._c_buffer = None
230        self._buffer_alignment = 4096
231        self._np_buffer = None
232        self._8bit_mode = False
233        self._12bit_mode = False
234        self._pre_trigger = 0
235        
236        super().__init__(card, *args, **kwargs)
237        self.buffer_type = SPCM_BUF_DATA
238        self._bytes_per_sample()
239        self._bits_per_sample()
240        self.num_channels = self.card.active_channels()
241
242        # Find out the direction of transfer
243        if self.function_type == SPCM_TYPE_AI or self.function_type == SPCM_TYPE_DI:
244            self.direction = Direction.Acquisition
245        elif self.function_type == SPCM_TYPE_AO or self.function_type == SPCM_TYPE_DO:
246            self.direction = Direction.Generation
247        else:
248            self.direction = Direction.Undefined
249
250    def _sample_rate(self) -> pint.Quantity:
251        """
252        Get the sample rate of the card
253
254        Returns
255        -------
256        pint.Quantity
257            the sample rate of the card in Hz as a pint quantity
258        """
259        return self.card.get_i(SPC_SAMPLERATE) * units.Hz
260
261    def memory_size(self, memory_size : int = None) -> int:
262        """
263        Sets the memory size in samples per channel. The memory size setting must be set before transferring 
264        data to the card. (see register `SPC_MEMSIZE` in the manual)
265        
266        Parameters
267        ----------
268        memory_size : int | pint.Quantity
269            the size of the memory in Bytes
270        """
271
272        if memory_size is not None:
273            memory_size = UnitConversion.convert(memory_size, units.Sa, int)
274            self.card.set_i(SPC_MEMSIZE, memory_size)
275        self._memory_size = self.card.get_i(SPC_MEMSIZE)
276        return self._memory_size
277    
278    def output_buffer_size(self, buffer_samples : int = None) -> int:
279        """
280        Set the size of the output buffer (see register `SPC_DATA_OUTBUFSIZE` in the manual)
281        
282        Parameters
283        ----------
284        buffer_samples : int | pint.Quantity
285            the size of the output buffer in Bytes
286        """
287
288        if buffer_samples is not None:
289            buffer_samples = UnitConversion.convert(buffer_samples, units.B, int)
290            buffer_size = self.samples_to_bytes(buffer_size)
291            self.card.set_i(SPC_DATA_OUTBUFSIZE, buffer_size)
292        return self.card.get_i(SPC_DATA_OUTBUFSIZE)
293    
294    def loops(self, loops : int = None) -> int:
295        return self.card.loops(loops)
296
297    def _bits_per_sample(self) -> int:
298        """
299        Get the number of bits per sample
300
301        Returns
302        -------
303        int
304            number of bits per sample
305        """
306        if self._8bit_mode:
307            self.bits_per_sample = 8
308        elif self._12bit_mode:
309            self.bits_per_sample = 12        
310        else:
311            self.bits_per_sample = self.card.bits_per_sample()
312    
313    def _bytes_per_sample(self) -> int:
314        """
315        Get the number of bytes per sample
316
317        Returns
318        -------
319        int
320            number of bytes per sample
321        """
322        if self._8bit_mode:
323            self.bytes_per_sample = 1
324        elif self._12bit_mode:
325            self.bytes_per_sample = 1.5
326        else:
327            self.bytes_per_sample = self.card.bytes_per_sample()
328    
329    def pre_trigger(self, num_samples : int = None) -> int:
330        """
331        Set the number of pre trigger samples (see register `SPC_PRETRIGGER` in the manual)
332        
333        Parameters
334        ----------
335        num_samples : int | pint.Quantity
336            the number of pre trigger samples
337        
338        Returns
339        -------
340        int
341            the number of pre trigger samples
342        """
343
344        if num_samples is not None:
345            num_samples = UnitConversion.convert(num_samples, units.Sa, int)
346            self.card.set_i(SPC_PRETRIGGER, num_samples)
347        self._pre_trigger = self.card.get_i(SPC_PRETRIGGER)
348        return self._pre_trigger
349    
350    def post_trigger(self, num_samples : int = None) -> int:
351        """
352        Set the number of post trigger samples (see register `SPC_POSTTRIGGER` in the manual)
353        
354        Parameters
355        ----------
356        num_samples : int | pint.Quantity
357            the number of post trigger samples
358        
359        Returns
360        -------
361        int
362            the number of post trigger samples
363        """
364
365        if self._memory_size < num_samples:
366            raise ValueError("The number of post trigger samples needs to be smaller than the total number of samples")
367        if num_samples is not None:
368            num_samples = UnitConversion.convert(num_samples, units.Sa, int)
369            self.card.set_i(SPC_POSTTRIGGER, num_samples)
370        post_trigger = self.card.get_i(SPC_POSTTRIGGER)
371        self._pre_trigger = self._memory_size - post_trigger
372        return post_trigger
373    
374    def allocate_buffer(self, num_samples : int, no_reshape = False) -> None:
375        """
376        Memory allocation for the buffer that is used for communicating with the card
377
378        Parameters
379        ----------
380        num_samples : int | pint.Quantity = None
381            use the number of samples an get the number of active channels and bytes per samples directly from the card
382        """
383        
384        self.buffer_samples = UnitConversion.convert(num_samples, units.Sa, int)
385
386        sample_type = self.numpy_type()
387
388        dwMask = self._buffer_alignment - 1
389
390        item_size = sample_type(0).itemsize
391        # allocate a buffer (numpy array) for DMA transfer: a little bigger one to have room for address alignment
392        databuffer_unaligned = np.empty(((self._buffer_alignment + self.buffer_size) // item_size, ), dtype = sample_type)   # byte count to sample (// = integer division)
393        # two numpy-arrays may share the same memory: skip the begin up to the alignment boundary (ArrayVariable[SKIP_VALUE:])
394        # Address of data-memory from numpy-array: ArrayVariable.__array_interface__['data'][0]
395        start_pos_samples = ((self._buffer_alignment - (databuffer_unaligned.__array_interface__['data'][0] & dwMask)) // item_size)
396        self.buffer = databuffer_unaligned[start_pos_samples:start_pos_samples + (self.buffer_size // item_size)]   # byte address to sample size
397        if self.bits_per_sample > 1 and not self._12bit_mode and not no_reshape:
398            self.buffer = self.buffer.reshape((self.num_channels, self.buffer_samples), order='F')  # index definition: [channel, sample] !
399    
400    def start_buffer_transfer(self, *args, buffer_type=SPCM_BUF_DATA, direction=None, notify_samples=None, transfer_offset=None, transfer_length=None, exception_num_samples=False) -> None:
401        """
402        Start the transfer of the data to or from the card  (see the API function `spcm_dwDefTransfer_i64` in the manual)
403        
404        Parameters
405        ----------
406        *args : list
407            list of additonal arguments that are added as flags to the start dma command
408        buffer_type : int
409            the type of buffer that is used for the transfer
410        direction : int
411            the direction of the transfer
412        notify_samples : int
413            the number of samples to notify the user about
414        transfer_offset : int
415            the offset of the transfer
416        transfer_length : int
417            the length of the transfer
418        exception_num_samples : bool
419            if True, an exception is raised if the number of samples is not a multiple of the notify samples. The automatic buffer handling only works with the number of samples being a multiple of the notify samples.
420
421        Raises
422        ------
423        SpcmException
424        """
425
426        self.notify_samples(UnitConversion.convert(notify_samples, units.Sa, int))
427        transfer_offset = UnitConversion.convert(transfer_offset, units.Sa, int)
428        transfer_length = UnitConversion.convert(transfer_length, units.Sa, int)
429
430        if self.buffer is None: 
431            raise SpcmException(text="No buffer defined for transfer")
432        if buffer_type: 
433            self.buffer_type = buffer_type
434        if direction is None:
435            if self.direction == Direction.Acquisition:
436                direction = SPCM_DIR_CARDTOPC
437            elif self.direction == Direction.Generation:
438                direction = SPCM_DIR_PCTOCARD
439            else:
440                raise SpcmException(text="Please define a direction for transfer (SPCM_DIR_CARDTOPC or SPCM_DIR_PCTOCARD)")
441        
442        if self._notify_samples != 0 and np.remainder(self.buffer_samples, self._notify_samples) and exception_num_samples:
443            raise SpcmException("The number of samples needs to be a multiple of the notify samples.")
444
445        if transfer_offset:
446            transfer_offset_bytes = self.samples_to_bytes(transfer_offset)
447            # transfer_offset_bytes = transfer_offset * self.bytes_per_sample * self.num_channels
448        else:
449            transfer_offset_bytes = 0
450
451        self.buffer_samples = transfer_length
452        
453        # we define the buffer for transfer and start the DMA transfer
454        self.card._print("Starting the DMA transfer and waiting until data is in board memory")
455        self._c_buffer = self.buffer.ctypes.data_as(c_void_p)
456        self.card._check_error(spcm_dwDefTransfer_i64(self.card._handle, self.buffer_type, direction, self.notify_size, self._c_buffer, transfer_offset_bytes, self.buffer_size))
457        
458        # Execute additional commands if available
459        cmd = 0
460        for arg in args:
461            cmd |= arg
462        self.card.cmd(cmd)
463        self.card._print("... data transfer started")
464
465    def duration(self, duration : pint.Quantity, pre_trigger_duration : pint.Quantity = None, post_trigger_duration : pint.Quantity = None) -> None:
466        """
467        Set the duration of the data transfer
468        
469        Parameters
470        ----------
471        duration : pint.Quantity
472            the duration of the data transfer
473        pre_trigger_duration : pint.Quantity = None
474            the duration before the trigger event
475        post_trigger_duration : pint.Quantity = None
476            the duration after the trigger event
477        
478        Returns
479        -------
480        pint.Quantity
481            the duration of the data transfer
482        """
483
484        if pre_trigger_duration is None and post_trigger_duration is None:
485            raise ValueError("Please define either pre_trigger_duration or post_trigger_duration")
486
487        memsize_min = self.card.get_i(SPC_AVAILMEMSIZE_MIN)
488        memsize_max = self.card.get_i(SPC_AVAILMEMSIZE_MAX)
489        memsize_stp = self.card.get_i(SPC_AVAILMEMSIZE_STEP)
490        num_samples = (duration * self._sample_rate()).to_base_units().magnitude
491        num_samples = np.ceil(num_samples / memsize_stp) * memsize_stp
492        num_samples = np.clip(num_samples, memsize_min, memsize_max)
493        num_samples = int(num_samples)
494        self.memory_size(num_samples)
495        self.allocate_buffer(num_samples)
496        if pre_trigger_duration is not None:
497            pre_min = self.card.get_i(SPC_AVAILPRETRIGGER_MIN)
498            pre_max = self.card.get_i(SPC_AVAILPRETRIGGER_MAX)
499            pre_stp = self.card.get_i(SPC_AVAILPRETRIGGER_STEP)
500            pre_samples = (pre_trigger_duration * self._sample_rate()).to_base_units().magnitude
501            pre_samples = np.ceil(pre_samples / pre_stp) * pre_stp
502            pre_samples = np.clip(pre_samples, pre_min, pre_max)
503            pre_samples = int(post_samples)
504            self.post_trigger(post_samples)
505        if post_trigger_duration is not None:
506            post_min = self.card.get_i(SPC_AVAILPOSTTRIGGER_MIN)
507            post_max = self.card.get_i(SPC_AVAILPOSTTRIGGER_MAX)
508            post_stp = self.card.get_i(SPC_AVAILPOSTTRIGGER_STEP)
509            post_samples = (post_trigger_duration * self._sample_rate()).to_base_units().magnitude
510            post_samples = np.ceil(post_samples / post_stp) * post_stp
511            post_samples = np.clip(post_samples, post_min, post_max)
512            post_samples = int(post_samples)
513            self.post_trigger(post_samples)
514        return num_samples, post_samples
515
516    def time_data(self, total_num_samples : int = None) -> npt.NDArray:
517        """
518        Get the time array for the data buffer
519
520        Parameters
521        ----------
522        total_num_samples : int | pint.Quantity
523            the total number of samples
524        
525        Returns
526        -------
527        numpy array
528            the time array
529        """
530
531        sample_rate = self._sample_rate()
532        if total_num_samples is None:
533            total_num_samples = self._buffer_samples
534        total_num_samples = UnitConversion.convert(total_num_samples, units.Sa, int)
535        pre_trigger = UnitConversion.convert(self._pre_trigger, units.Sa, int)
536        return ((np.arange(total_num_samples) - pre_trigger) / sample_rate).to_base_units()
537    
538    def unpack_12bit_buffer(self, data : npt.NDArray[np.int_] = None) -> npt.NDArray[np.int_]:
539        """
540        Unpack the 12bit buffer to a 16bit buffer
541
542        Returns
543        -------
544        numpy array
545            the unpacked 16bit buffer
546        """
547
548        if not self._12bit_mode:
549            raise SpcmException("The card is not in 12bit packed mode")
550        
551        if data is None:
552            data = self.buffer
553
554        fst_int8, mid_int8, lst_int8 = np.reshape(data, (data.shape[0] // 3, 3)).astype(np.int16).T
555        nibble_h = (mid_int8 >> 0) & 0x0F
556        nibble_m = (fst_int8 >> 4) & 0x0F
557        nibble_l = (fst_int8 >> 0) & 0x0F
558        fst_int12 = ((nibble_h << 12) >> 4) | (nibble_m << 4) | (nibble_l << 0)
559        nibble_h = (lst_int8 >> 4) & 0x0F
560        nibble_m = (lst_int8 >> 0) & 0x0F
561        nibble_l = (mid_int8 >> 4) & 0x0F
562        snd_int12 = ((nibble_h << 12) >> 4) | (nibble_m << 4) | (nibble_l << 0)
563        data_int12 = np.concatenate((fst_int12[:, None], snd_int12[:, None]), axis=1).reshape((-1,))
564        data_int12 = data_int12.reshape((self.num_channels, self._buffer_samples), order='F')
565        return data_int12
566    
567    def unpackbits(self):
568        """
569        Unpack the buffer to bits
570
571        Returns
572        -------
573        numpy array
574            the unpacked buffer
575        """
576        data = self.buffer
577        dshape = list(data.shape)
578        return_data = data.reshape([-1, 1])
579        num_bits = return_data.dtype.itemsize * 8
580        mask = 2**np.arange(num_bits, dtype=return_data.dtype).reshape([1, num_bits])
581        return (return_data & mask).astype(bool).astype(int).reshape(dshape + [num_bits])
582
583    def tofile(self, filename : str, **kwargs) -> None:
584        """
585        Export the buffer to a file. The file format is determined by the file extension
586        Supported file formats are: 
587        * .bin: raw binary file
588        * .csv: comma-separated values file
589        * .npy: numpy binary file
590        * .npz: compressed numpy binary file
591        * .txt: whitespace-delimited text file
592        * .h5: hdf5 file format
593
594        Parameters
595        ----------
596        filename : str
597            the name of the file that the buffer should be exported to
598        
599        Raises
600        ------
601        ImportError
602            if the file format is not supported
603        """
604
605        file_path = Path(filename)
606        if file_path.suffix == '.bin':
607            dtype = kwargs.get('dtype', self.numpy_type())
608            self.buffer.tofile(file_path, dtype=dtype)
609        elif file_path.suffix == '.csv':
610            delimiter = kwargs.get('delimiter', ',')
611            np.savetxt(file_path, self.buffer, delimiter=delimiter)
612        elif file_path.suffix == '.npy':
613            np.save(file_path, self.buffer)
614        elif file_path.suffix == '.npz':
615            np.savez_compressed(file_path, self.buffer)
616        elif file_path.suffix == '.txt':
617            np.savetxt(file_path, self.buffer, fmt='%d')
618        elif file_path.suffix == '.h5' or file_path.suffix == '.hdf5':
619            import h5py
620            with h5py.File(file_path, 'w') as f:
621                f.create_dataset('data', data=self.buffer)
622        else:
623            raise ImportError("File format not supported")
624        
625    def fromfile(self, filename : str, **kwargs) -> None:
626        """
627        Import the buffer from a file. The file format is determined by the file extension
628        Supported file formats are: 
629        * .bin: raw binary file
630        * .csv: comma-separated values file
631        * .npy: numpy binary file
632        * .npz: compressed numpy binary file
633        * .txt: whitespace-delimited text file
634        * .h5: hdf5 file format
635
636        Parameters
637        ----------
638        filename : str
639            the name of the file that the buffer should be imported from
640        
641        Raises
642        ------
643        ImportError
644            if the file format is not supported
645        """
646
647        file_path = Path(filename)
648        if file_path.suffix == '.bin':
649            dtype = kwargs.get('dtype', self.numpy_type())
650            shape = kwargs.get('shape', (self.num_channels, self.buffer_size // self.num_channels))
651            buffer = np.fromfile(file_path, dtype=dtype)
652            self.buffer[:] = buffer.reshape(shape, order='C')
653        elif file_path.suffix == '.csv':
654            delimiter = kwargs.get('delimiter', ',')
655            self.buffer[:] = np.loadtxt(file_path, delimiter=delimiter)
656        elif file_path.suffix == '.npy':
657            self.buffer[:] = np.load(file_path)
658        elif file_path.suffix == '.npz':
659            data = np.load(file_path)
660            self.buffer[:] = data['arr_0']
661        elif file_path.suffix == '.txt':
662            self.buffer[:] = np.loadtxt(file_path)
663        elif file_path.suffix == '.h5' or file_path.suffix == '.hdf5':
664            import h5py
665            with h5py.File(file_path, 'r') as f:
666                self.buffer[:] = f['data'][()]
667        else:
668            raise ImportError("File format not supported")
669
670    def avail_card_len(self, available_samples : int = 0) -> None:
671        """
672        Set the amount of data that has been read out of the data buffer (see register `SPC_DATA_AVAIL_CARD_LEN` in the manual)
673
674        Parameters
675        ----------
676        available_samples : int | pint.Quantity
677            the amount of data that is available for reading
678        """
679
680        available_samples = UnitConversion.convert(available_samples, units.Sa, int)
681        # print(available_samples, self.bytes_per_sample, self.num_channels)
682        available_bytes = self.samples_to_bytes(available_samples)
683        self.card.set_i(SPC_DATA_AVAIL_CARD_LEN, available_bytes)
684    
685    def avail_user_pos(self, in_bytes : bool = False) -> int:
686        """
687        Get the current position of the pointer in the data buffer (see register `SPC_DATA_AVAIL_USER_POS` in the manual)
688
689        Parameters
690        ----------
691        in_bytes : bool
692            if True, the position is returned in bytes
693
694        Returns
695        -------
696        int
697            pointer position
698        """
699
700        self.current_user_pos = self.card.get_i(SPC_DATA_AVAIL_USER_POS)
701        if not in_bytes:
702            self.current_user_pos = self.bytes_to_samples(self.current_user_pos)
703        return self.current_user_pos
704    
705    def avail_user_len(self, in_bytes : bool = False) -> int:
706        """
707        Get the current length of the data in the data buffer (see register `SPC_DATA_AVAIL_USER_LEN` in the manual)
708
709        Parameters
710        ----------
711        in_bytes : bool
712            if True, the length is returned in bytes
713
714        Returns
715        -------
716        int
717            data length available
718        """
719
720        user_len = self.card.get_i(SPC_DATA_AVAIL_USER_LEN)
721        if not in_bytes:
722            user_len = self.bytes_to_samples(user_len)
723        return user_len
724    
725    def fill_size_promille(self, return_unit = None) -> int:
726        """
727        Get the fill size of the data buffer (see register `SPC_FILLSIZEPROMILLE` in the manual)
728
729        Returns
730        -------
731        int
732            fill size
733        """
734
735        return_value = self.card.get_i(SPC_FILLSIZEPROMILLE)
736        if return_unit is not None: return_value = UnitConversion.to_unit(return_value * units.promille, return_unit)
737        return return_value
738    
739    def wait_dma(self) -> None:
740        """
741        Wait for the DMA transfer to finish (see register `M2CMD_DATA_WAITDMA` in the manual)
742        """
743        
744        self.card.cmd(M2CMD_DATA_WAITDMA)
745    wait = wait_dma
746
747    def numpy_type(self) -> npt.NDArray[np.int_]:
748        """
749        Get the type of numpy data from number of bytes
750
751        Returns
752        -------
753        numpy data type
754            the type of data that is used by the card
755        """
756
757        if self._8bit_mode:
758            return np.uint8
759        if self._12bit_mode:
760            return np.int8
761        if self.bits_per_sample == 1:
762            if self.num_channels <= 8:
763                return np.uint8
764            elif self.num_channels <= 16:
765                return np.uint16
766            elif self.num_channels <= 32:
767                return np.uint32
768            return np.uint64
769        if self.bits_per_sample <= 8:
770            return np.int8
771        elif self.bits_per_sample <= 16:
772            return np.int16
773        elif self.bits_per_sample <= 32:
774            return np.int32
775        return np.int64
776
777    # Data conversion mode
778    def data_conversion(self, mode : int = None) -> int:
779        """
780        Set the data conversion mode (see register `SPC_DATACONVERSION` in the manual)
781        
782        Parameters
783        ----------
784        mode : int
785            the data conversion mode
786        """
787
788        if mode is not None:
789            self.card.set_i(SPC_DATACONVERSION, mode)
790        mode = self.card.get_i(SPC_DATACONVERSION)
791        self._8bit_mode = (mode == SPCM_DC_12BIT_TO_8BIT or mode == SPCM_DC_14BIT_TO_8BIT or mode == SPCM_DC_16BIT_TO_8BIT)
792        self._12bit_mode = (mode == SPCM_DC_12BIT_TO_12BITPACKED)
793        self._bits_per_sample()
794        self._bytes_per_sample()
795        return mode
796    
797    def avail_data_conversion(self) -> int:
798        """
799        Get the available data conversion modes (see register `SPC_AVAILDATACONVERSION` in the manual)
800
801        Returns
802        -------
803        int
804            the available data conversion modes
805        """
806        return self.card.get_i(SPC_AVAILDATACONVERSION)
807    
808    # Iterator methods
809
810    iterator_index = 0
811    _max_timeout = 64
812
813    _to_transfer_samples = 0
814    _current_samples = 0
815    
816    _verbose = False
817
818    def verbose(self, verbose : bool = None) -> bool:
819        """
820        Set or get the verbose mode for the data transfer
821
822        Parameters
823        ----------
824        verbose : bool = None
825            the verbose mode
826        """
827
828        if verbose is not None:
829            self._verbose = verbose
830        return self._verbose
831
832    def to_transfer_samples(self, samples) -> None:
833        """
834        This method sets the number of samples to transfer
835
836        Parameters
837        ----------
838        samples : int | pint.Quantity
839            the number of samples to transfer
840        """
841
842        samples = UnitConversion.convert(samples, units.Sa, int)
843        self._to_transfer_samples = samples
844    
845    def __iter__(self):
846        """
847        This method is called when the iterator is initialized
848
849        Returns
850        -------
851        DataIterator
852            the iterator itself
853        """
854
855        self.iterator_index = 0
856        return self
857    
858    def __next__(self) -> npt.ArrayLike:
859        """
860        This method is called when the next element is requested from the iterator
861
862        Returns
863        -------
864        npt.ArrayLike
865            the next data block
866        
867        Raises
868        ------
869        StopIteration
870        """
871        timeout_counter = 0
872
873        if self.iterator_index != 0:
874            self.avail_card_len(self._notify_samples)
875
876        while True:
877            try:
878                # print(self.card.status())
879                self.wait_dma()
880            except SpcmTimeout:
881                self.card._print("... Timeout ({})".format(timeout_counter), end='\r')
882                timeout_counter += 1
883                if timeout_counter > self._max_timeout:
884                    raise StopIteration
885            else:
886                break
887        
888        self.iterator_index += 1
889
890        fill_size = self.fill_size_promille()
891
892        self._current_samples += self._notify_samples
893        if self._to_transfer_samples != 0 and self._to_transfer_samples < self._current_samples:
894            raise StopIteration
895
896        user_pos = self.avail_user_pos()
897        
898        # self.card._print("Fill size: {}%  Pos:{:08x} Len:{:08x} Total:{:.2f} MiS / {:.2f} MiS".format(fill_size/10, user_pos, user_len, self._current_samples / MEBI(1), self._to_transfer_samples / MEBI(1)), end='\r', verbose=self._verbose)
899        self.card._print("Fill size: {}%  Pos:{:08x} Total:{:.2f} MiS / {:.2f} MiS".format(fill_size/10, user_pos, self._current_samples / MEBI(1), self._to_transfer_samples / MEBI(1)), end='\r', verbose=self._verbose)
900
901        # self.avail_card_len(self._notify_samples) # TODO this probably always a problem! Because the data is not read out yets
902        
903        return self.buffer[:, user_pos:user_pos+self._notify_samples]

A high-level class to control Data Transfer to and from Spectrum Instrumentation cards.

This class is an iterator class that implements the functions __iter__ and __next__. This allows the user to supply the class to a for loop and iterate over the data that is transferred from or to the card. Each iteration will return a numpy array with a data block of size notify_samples. In case of a digitizer you can read the data from that block and process it. In case of a generator you can write data to the block and it's then transferred.

For more information about what setups are available, please have a look at the user manual for your specific card.

Parameters
  • buffer (NDArray[np.int_]): numpy object that can be used to write data into the spcm buffer
  • buffer_size (int): defines the size of the current buffer shared between the PC and the card
  • buffer_type (int): defines the type of data in the buffer that is used for the transfer
  • num_channels (int): defines the number of channels that are used for the transfer
  • bytes_per_sample (int): defines the number of bytes per sample
  • bits_per_sample (int): defines the number of bits per sample
DataTransfer(card, *args, **kwargs)
204    def __init__(self, card, *args, **kwargs) -> None:
205        """
206        Initialize the DataTransfer object with a card object and additional arguments
207
208        Parameters
209        ----------
210        card : Card
211            the card object that is used for the data transfer
212        *args : list
213            list of additional arguments
214        **kwargs : dict
215            dictionary of additional keyword arguments
216        """
217        
218        self.buffer_size = 0
219        self.notify_size = 0
220        self.num_channels = 0
221        self.bytes_per_sample = 0
222        self.bits_per_sample = 0
223
224        self.current_user_pos = 0
225
226        self._buffer_samples = 0
227        self._notify_samples = 0
228        self._memory_size = 0
229        self._c_buffer = None
230        self._buffer_alignment = 4096
231        self._np_buffer = None
232        self._8bit_mode = False
233        self._12bit_mode = False
234        self._pre_trigger = 0
235        
236        super().__init__(card, *args, **kwargs)
237        self.buffer_type = SPCM_BUF_DATA
238        self._bytes_per_sample()
239        self._bits_per_sample()
240        self.num_channels = self.card.active_channels()
241
242        # Find out the direction of transfer
243        if self.function_type == SPCM_TYPE_AI or self.function_type == SPCM_TYPE_DI:
244            self.direction = Direction.Acquisition
245        elif self.function_type == SPCM_TYPE_AO or self.function_type == SPCM_TYPE_DO:
246            self.direction = Direction.Generation
247        else:
248            self.direction = Direction.Undefined

Initialize the DataTransfer object with a card object and additional arguments

Parameters
  • card (Card): the card object that is used for the data transfer
  • *args (list): list of additional arguments
  • **kwargs (dict): dictionary of additional keyword arguments
buffer_size: int = 0
notify_size: int = 0
direction: spcm_core.pyspcm.Direction = <Direction.Acquisition: 1>
buffer_type: int
num_channels: int = 0
bytes_per_sample: int = 0
bits_per_sample: int = 0
current_user_pos: int = 0
buffer: numpy.ndarray[typing.Any, numpy.dtype[numpy.int64]]
68    @property
69    def buffer(self) -> npt.NDArray[np.int_]:
70        """
71        The numpy buffer object that interfaces the Card and can be written and read from
72        
73        Returns
74        -------
75        numpy array
76            the numpy buffer object with the following array index definition: 
77            `[channel, sample]`
78            or in case of multiple recording / replay:
79            `[segment, sample, channel]`
80        """
81        return self._np_buffer

The numpy buffer object that interfaces the Card and can be written and read from

Returns
  • numpy array: the numpy buffer object with the following array index definition: [channel, sample] or in case of multiple recording / replay: [segment, sample, channel]
buffer_samples: int
 91    @property
 92    def buffer_samples(self) -> int:
 93        """
 94        The number of samples in the buffer
 95        
 96        Returns
 97        -------
 98        int
 99            the number of samples in the buffer
100        """
101        return self._buffer_samples

The number of samples in the buffer

Returns
  • int: the number of samples in the buffer
def bytes_to_samples(self, num_bytes: int) -> int:
119    def bytes_to_samples(self, num_bytes : int) -> int:
120        """
121        Convert bytes to samples
122        
123        Parameters
124        ----------
125        bytes : int
126            the number of bytes
127        
128        Returns
129        -------
130        int
131            the number of samples
132        """
133
134        if self.bits_per_sample > 1:
135            num_samples = num_bytes // self.bytes_per_sample // self.num_channels
136        else:
137            num_samples = num_bytes // self.num_channels * 8
138        return num_samples

Convert bytes to samples

Parameters
  • bytes (int): the number of bytes
Returns
  • int: the number of samples
def samples_to_bytes(self, num_samples: int) -> int:
140    def samples_to_bytes(self, num_samples : int) -> int:
141        """
142        Convert samples to bytes
143        
144        Parameters
145        ----------
146        num_samples : int
147            the number of samples
148        
149        Returns
150        -------
151        int
152            the number of bytes
153        """
154
155        if self.bits_per_sample > 1:
156            num_bytes = num_samples * self.bytes_per_sample * self.num_channels
157        else:
158            num_bytes = num_samples * self.num_channels // 8
159        return num_bytes

Convert samples to bytes

Parameters
  • num_samples (int): the number of samples
Returns
  • int: the number of bytes
def notify_samples(self, notify_samples: int = None) -> int:
174    def notify_samples(self, notify_samples : int = None) -> int:
175        """
176        Set the number of samples to notify the user about
177        
178        Parameters
179        ----------
180        notify_samples : int | pint.Quantity
181            the number of samples to notify the user about
182        """
183
184        if notify_samples is not None:
185            notify_samples = UnitConversion.convert(notify_samples, units.Sa, int)
186            self._notify_samples = notify_samples
187            self.notify_size = self.samples_to_bytes(self._notify_samples)
188            # self.notify_size = int(self._notify_samples * self.bytes_per_sample * self.num_channels)
189        return self._notify_samples

Set the number of samples to notify the user about

Parameters
  • notify_samples (int | pint.Quantity): the number of samples to notify the user about
def memory_size(self, memory_size: int = None) -> int:
261    def memory_size(self, memory_size : int = None) -> int:
262        """
263        Sets the memory size in samples per channel. The memory size setting must be set before transferring 
264        data to the card. (see register `SPC_MEMSIZE` in the manual)
265        
266        Parameters
267        ----------
268        memory_size : int | pint.Quantity
269            the size of the memory in Bytes
270        """
271
272        if memory_size is not None:
273            memory_size = UnitConversion.convert(memory_size, units.Sa, int)
274            self.card.set_i(SPC_MEMSIZE, memory_size)
275        self._memory_size = self.card.get_i(SPC_MEMSIZE)
276        return self._memory_size

Sets the memory size in samples per channel. The memory size setting must be set before transferring data to the card. (see register SPC_MEMSIZE in the manual)

Parameters
  • memory_size (int | pint.Quantity): the size of the memory in Bytes
def output_buffer_size(self, buffer_samples: int = None) -> int:
278    def output_buffer_size(self, buffer_samples : int = None) -> int:
279        """
280        Set the size of the output buffer (see register `SPC_DATA_OUTBUFSIZE` in the manual)
281        
282        Parameters
283        ----------
284        buffer_samples : int | pint.Quantity
285            the size of the output buffer in Bytes
286        """
287
288        if buffer_samples is not None:
289            buffer_samples = UnitConversion.convert(buffer_samples, units.B, int)
290            buffer_size = self.samples_to_bytes(buffer_size)
291            self.card.set_i(SPC_DATA_OUTBUFSIZE, buffer_size)
292        return self.card.get_i(SPC_DATA_OUTBUFSIZE)

Set the size of the output buffer (see register SPC_DATA_OUTBUFSIZE in the manual)

Parameters
  • buffer_samples (int | pint.Quantity): the size of the output buffer in Bytes
def loops(self, loops: int = None) -> int:
294    def loops(self, loops : int = None) -> int:
295        return self.card.loops(loops)
def pre_trigger(self, num_samples: int = None) -> int:
329    def pre_trigger(self, num_samples : int = None) -> int:
330        """
331        Set the number of pre trigger samples (see register `SPC_PRETRIGGER` in the manual)
332        
333        Parameters
334        ----------
335        num_samples : int | pint.Quantity
336            the number of pre trigger samples
337        
338        Returns
339        -------
340        int
341            the number of pre trigger samples
342        """
343
344        if num_samples is not None:
345            num_samples = UnitConversion.convert(num_samples, units.Sa, int)
346            self.card.set_i(SPC_PRETRIGGER, num_samples)
347        self._pre_trigger = self.card.get_i(SPC_PRETRIGGER)
348        return self._pre_trigger

Set the number of pre trigger samples (see register SPC_PRETRIGGER in the manual)

Parameters
  • num_samples (int | pint.Quantity): the number of pre trigger samples
Returns
  • int: the number of pre trigger samples
def post_trigger(self, num_samples: int = None) -> int:
350    def post_trigger(self, num_samples : int = None) -> int:
351        """
352        Set the number of post trigger samples (see register `SPC_POSTTRIGGER` in the manual)
353        
354        Parameters
355        ----------
356        num_samples : int | pint.Quantity
357            the number of post trigger samples
358        
359        Returns
360        -------
361        int
362            the number of post trigger samples
363        """
364
365        if self._memory_size < num_samples:
366            raise ValueError("The number of post trigger samples needs to be smaller than the total number of samples")
367        if num_samples is not None:
368            num_samples = UnitConversion.convert(num_samples, units.Sa, int)
369            self.card.set_i(SPC_POSTTRIGGER, num_samples)
370        post_trigger = self.card.get_i(SPC_POSTTRIGGER)
371        self._pre_trigger = self._memory_size - post_trigger
372        return post_trigger

Set the number of post trigger samples (see register SPC_POSTTRIGGER in the manual)

Parameters
  • num_samples (int | pint.Quantity): the number of post trigger samples
Returns
  • int: the number of post trigger samples
def allocate_buffer(self, num_samples: int, no_reshape=False) -> None:
374    def allocate_buffer(self, num_samples : int, no_reshape = False) -> None:
375        """
376        Memory allocation for the buffer that is used for communicating with the card
377
378        Parameters
379        ----------
380        num_samples : int | pint.Quantity = None
381            use the number of samples an get the number of active channels and bytes per samples directly from the card
382        """
383        
384        self.buffer_samples = UnitConversion.convert(num_samples, units.Sa, int)
385
386        sample_type = self.numpy_type()
387
388        dwMask = self._buffer_alignment - 1
389
390        item_size = sample_type(0).itemsize
391        # allocate a buffer (numpy array) for DMA transfer: a little bigger one to have room for address alignment
392        databuffer_unaligned = np.empty(((self._buffer_alignment + self.buffer_size) // item_size, ), dtype = sample_type)   # byte count to sample (// = integer division)
393        # two numpy-arrays may share the same memory: skip the begin up to the alignment boundary (ArrayVariable[SKIP_VALUE:])
394        # Address of data-memory from numpy-array: ArrayVariable.__array_interface__['data'][0]
395        start_pos_samples = ((self._buffer_alignment - (databuffer_unaligned.__array_interface__['data'][0] & dwMask)) // item_size)
396        self.buffer = databuffer_unaligned[start_pos_samples:start_pos_samples + (self.buffer_size // item_size)]   # byte address to sample size
397        if self.bits_per_sample > 1 and not self._12bit_mode and not no_reshape:
398            self.buffer = self.buffer.reshape((self.num_channels, self.buffer_samples), order='F')  # index definition: [channel, sample] !

Memory allocation for the buffer that is used for communicating with the card

Parameters
  • num_samples (int | pint.Quantity = None): use the number of samples an get the number of active channels and bytes per samples directly from the card
def start_buffer_transfer( self, *args, buffer_type=1000, direction=None, notify_samples=None, transfer_offset=None, transfer_length=None, exception_num_samples=False) -> None:
400    def start_buffer_transfer(self, *args, buffer_type=SPCM_BUF_DATA, direction=None, notify_samples=None, transfer_offset=None, transfer_length=None, exception_num_samples=False) -> None:
401        """
402        Start the transfer of the data to or from the card  (see the API function `spcm_dwDefTransfer_i64` in the manual)
403        
404        Parameters
405        ----------
406        *args : list
407            list of additonal arguments that are added as flags to the start dma command
408        buffer_type : int
409            the type of buffer that is used for the transfer
410        direction : int
411            the direction of the transfer
412        notify_samples : int
413            the number of samples to notify the user about
414        transfer_offset : int
415            the offset of the transfer
416        transfer_length : int
417            the length of the transfer
418        exception_num_samples : bool
419            if True, an exception is raised if the number of samples is not a multiple of the notify samples. The automatic buffer handling only works with the number of samples being a multiple of the notify samples.
420
421        Raises
422        ------
423        SpcmException
424        """
425
426        self.notify_samples(UnitConversion.convert(notify_samples, units.Sa, int))
427        transfer_offset = UnitConversion.convert(transfer_offset, units.Sa, int)
428        transfer_length = UnitConversion.convert(transfer_length, units.Sa, int)
429
430        if self.buffer is None: 
431            raise SpcmException(text="No buffer defined for transfer")
432        if buffer_type: 
433            self.buffer_type = buffer_type
434        if direction is None:
435            if self.direction == Direction.Acquisition:
436                direction = SPCM_DIR_CARDTOPC
437            elif self.direction == Direction.Generation:
438                direction = SPCM_DIR_PCTOCARD
439            else:
440                raise SpcmException(text="Please define a direction for transfer (SPCM_DIR_CARDTOPC or SPCM_DIR_PCTOCARD)")
441        
442        if self._notify_samples != 0 and np.remainder(self.buffer_samples, self._notify_samples) and exception_num_samples:
443            raise SpcmException("The number of samples needs to be a multiple of the notify samples.")
444
445        if transfer_offset:
446            transfer_offset_bytes = self.samples_to_bytes(transfer_offset)
447            # transfer_offset_bytes = transfer_offset * self.bytes_per_sample * self.num_channels
448        else:
449            transfer_offset_bytes = 0
450
451        self.buffer_samples = transfer_length
452        
453        # we define the buffer for transfer and start the DMA transfer
454        self.card._print("Starting the DMA transfer and waiting until data is in board memory")
455        self._c_buffer = self.buffer.ctypes.data_as(c_void_p)
456        self.card._check_error(spcm_dwDefTransfer_i64(self.card._handle, self.buffer_type, direction, self.notify_size, self._c_buffer, transfer_offset_bytes, self.buffer_size))
457        
458        # Execute additional commands if available
459        cmd = 0
460        for arg in args:
461            cmd |= arg
462        self.card.cmd(cmd)
463        self.card._print("... data transfer started")

Start the transfer of the data to or from the card (see the API function spcm_dwDefTransfer_i64 in the manual)

Parameters
  • *args (list): list of additonal arguments that are added as flags to the start dma command
  • buffer_type (int): the type of buffer that is used for the transfer
  • direction (int): the direction of the transfer
  • notify_samples (int): the number of samples to notify the user about
  • transfer_offset (int): the offset of the transfer
  • transfer_length (int): the length of the transfer
  • exception_num_samples (bool): if True, an exception is raised if the number of samples is not a multiple of the notify samples. The automatic buffer handling only works with the number of samples being a multiple of the notify samples.
Raises
  • SpcmException
def duration( self, duration: pint.registry.Quantity, pre_trigger_duration: pint.registry.Quantity = None, post_trigger_duration: pint.registry.Quantity = None) -> None:
465    def duration(self, duration : pint.Quantity, pre_trigger_duration : pint.Quantity = None, post_trigger_duration : pint.Quantity = None) -> None:
466        """
467        Set the duration of the data transfer
468        
469        Parameters
470        ----------
471        duration : pint.Quantity
472            the duration of the data transfer
473        pre_trigger_duration : pint.Quantity = None
474            the duration before the trigger event
475        post_trigger_duration : pint.Quantity = None
476            the duration after the trigger event
477        
478        Returns
479        -------
480        pint.Quantity
481            the duration of the data transfer
482        """
483
484        if pre_trigger_duration is None and post_trigger_duration is None:
485            raise ValueError("Please define either pre_trigger_duration or post_trigger_duration")
486
487        memsize_min = self.card.get_i(SPC_AVAILMEMSIZE_MIN)
488        memsize_max = self.card.get_i(SPC_AVAILMEMSIZE_MAX)
489        memsize_stp = self.card.get_i(SPC_AVAILMEMSIZE_STEP)
490        num_samples = (duration * self._sample_rate()).to_base_units().magnitude
491        num_samples = np.ceil(num_samples / memsize_stp) * memsize_stp
492        num_samples = np.clip(num_samples, memsize_min, memsize_max)
493        num_samples = int(num_samples)
494        self.memory_size(num_samples)
495        self.allocate_buffer(num_samples)
496        if pre_trigger_duration is not None:
497            pre_min = self.card.get_i(SPC_AVAILPRETRIGGER_MIN)
498            pre_max = self.card.get_i(SPC_AVAILPRETRIGGER_MAX)
499            pre_stp = self.card.get_i(SPC_AVAILPRETRIGGER_STEP)
500            pre_samples = (pre_trigger_duration * self._sample_rate()).to_base_units().magnitude
501            pre_samples = np.ceil(pre_samples / pre_stp) * pre_stp
502            pre_samples = np.clip(pre_samples, pre_min, pre_max)
503            pre_samples = int(post_samples)
504            self.post_trigger(post_samples)
505        if post_trigger_duration is not None:
506            post_min = self.card.get_i(SPC_AVAILPOSTTRIGGER_MIN)
507            post_max = self.card.get_i(SPC_AVAILPOSTTRIGGER_MAX)
508            post_stp = self.card.get_i(SPC_AVAILPOSTTRIGGER_STEP)
509            post_samples = (post_trigger_duration * self._sample_rate()).to_base_units().magnitude
510            post_samples = np.ceil(post_samples / post_stp) * post_stp
511            post_samples = np.clip(post_samples, post_min, post_max)
512            post_samples = int(post_samples)
513            self.post_trigger(post_samples)
514        return num_samples, post_samples

Set the duration of the data transfer

Parameters
  • duration (pint.Quantity): the duration of the data transfer
  • pre_trigger_duration (pint.Quantity = None): the duration before the trigger event
  • post_trigger_duration (pint.Quantity = None): the duration after the trigger event
Returns
  • pint.Quantity: the duration of the data transfer
def time_data( self, total_num_samples: int = None) -> numpy.ndarray[typing.Any, numpy.dtype[+_ScalarType_co]]:
516    def time_data(self, total_num_samples : int = None) -> npt.NDArray:
517        """
518        Get the time array for the data buffer
519
520        Parameters
521        ----------
522        total_num_samples : int | pint.Quantity
523            the total number of samples
524        
525        Returns
526        -------
527        numpy array
528            the time array
529        """
530
531        sample_rate = self._sample_rate()
532        if total_num_samples is None:
533            total_num_samples = self._buffer_samples
534        total_num_samples = UnitConversion.convert(total_num_samples, units.Sa, int)
535        pre_trigger = UnitConversion.convert(self._pre_trigger, units.Sa, int)
536        return ((np.arange(total_num_samples) - pre_trigger) / sample_rate).to_base_units()

Get the time array for the data buffer

Parameters
  • total_num_samples (int | pint.Quantity): the total number of samples
Returns
  • numpy array: the time array
def unpack_12bit_buffer( self, data: numpy.ndarray[typing.Any, numpy.dtype[numpy.int64]] = None) -> numpy.ndarray[typing.Any, numpy.dtype[numpy.int64]]:
538    def unpack_12bit_buffer(self, data : npt.NDArray[np.int_] = None) -> npt.NDArray[np.int_]:
539        """
540        Unpack the 12bit buffer to a 16bit buffer
541
542        Returns
543        -------
544        numpy array
545            the unpacked 16bit buffer
546        """
547
548        if not self._12bit_mode:
549            raise SpcmException("The card is not in 12bit packed mode")
550        
551        if data is None:
552            data = self.buffer
553
554        fst_int8, mid_int8, lst_int8 = np.reshape(data, (data.shape[0] // 3, 3)).astype(np.int16).T
555        nibble_h = (mid_int8 >> 0) & 0x0F
556        nibble_m = (fst_int8 >> 4) & 0x0F
557        nibble_l = (fst_int8 >> 0) & 0x0F
558        fst_int12 = ((nibble_h << 12) >> 4) | (nibble_m << 4) | (nibble_l << 0)
559        nibble_h = (lst_int8 >> 4) & 0x0F
560        nibble_m = (lst_int8 >> 0) & 0x0F
561        nibble_l = (mid_int8 >> 4) & 0x0F
562        snd_int12 = ((nibble_h << 12) >> 4) | (nibble_m << 4) | (nibble_l << 0)
563        data_int12 = np.concatenate((fst_int12[:, None], snd_int12[:, None]), axis=1).reshape((-1,))
564        data_int12 = data_int12.reshape((self.num_channels, self._buffer_samples), order='F')
565        return data_int12

Unpack the 12bit buffer to a 16bit buffer

Returns
  • numpy array: the unpacked 16bit buffer
def unpackbits(self):
567    def unpackbits(self):
568        """
569        Unpack the buffer to bits
570
571        Returns
572        -------
573        numpy array
574            the unpacked buffer
575        """
576        data = self.buffer
577        dshape = list(data.shape)
578        return_data = data.reshape([-1, 1])
579        num_bits = return_data.dtype.itemsize * 8
580        mask = 2**np.arange(num_bits, dtype=return_data.dtype).reshape([1, num_bits])
581        return (return_data & mask).astype(bool).astype(int).reshape(dshape + [num_bits])

Unpack the buffer to bits

Returns
  • numpy array: the unpacked buffer
def tofile(self, filename: str, **kwargs) -> None:
583    def tofile(self, filename : str, **kwargs) -> None:
584        """
585        Export the buffer to a file. The file format is determined by the file extension
586        Supported file formats are: 
587        * .bin: raw binary file
588        * .csv: comma-separated values file
589        * .npy: numpy binary file
590        * .npz: compressed numpy binary file
591        * .txt: whitespace-delimited text file
592        * .h5: hdf5 file format
593
594        Parameters
595        ----------
596        filename : str
597            the name of the file that the buffer should be exported to
598        
599        Raises
600        ------
601        ImportError
602            if the file format is not supported
603        """
604
605        file_path = Path(filename)
606        if file_path.suffix == '.bin':
607            dtype = kwargs.get('dtype', self.numpy_type())
608            self.buffer.tofile(file_path, dtype=dtype)
609        elif file_path.suffix == '.csv':
610            delimiter = kwargs.get('delimiter', ',')
611            np.savetxt(file_path, self.buffer, delimiter=delimiter)
612        elif file_path.suffix == '.npy':
613            np.save(file_path, self.buffer)
614        elif file_path.suffix == '.npz':
615            np.savez_compressed(file_path, self.buffer)
616        elif file_path.suffix == '.txt':
617            np.savetxt(file_path, self.buffer, fmt='%d')
618        elif file_path.suffix == '.h5' or file_path.suffix == '.hdf5':
619            import h5py
620            with h5py.File(file_path, 'w') as f:
621                f.create_dataset('data', data=self.buffer)
622        else:
623            raise ImportError("File format not supported")

Export the buffer to a file. The file format is determined by the file extension Supported file formats are:

  • .bin: raw binary file
  • .csv: comma-separated values file
  • .npy: numpy binary file
  • .npz: compressed numpy binary file
  • .txt: whitespace-delimited text file
  • .h5: hdf5 file format
Parameters
  • filename (str): the name of the file that the buffer should be exported to
Raises
  • ImportError: if the file format is not supported
def fromfile(self, filename: str, **kwargs) -> None:
625    def fromfile(self, filename : str, **kwargs) -> None:
626        """
627        Import the buffer from a file. The file format is determined by the file extension
628        Supported file formats are: 
629        * .bin: raw binary file
630        * .csv: comma-separated values file
631        * .npy: numpy binary file
632        * .npz: compressed numpy binary file
633        * .txt: whitespace-delimited text file
634        * .h5: hdf5 file format
635
636        Parameters
637        ----------
638        filename : str
639            the name of the file that the buffer should be imported from
640        
641        Raises
642        ------
643        ImportError
644            if the file format is not supported
645        """
646
647        file_path = Path(filename)
648        if file_path.suffix == '.bin':
649            dtype = kwargs.get('dtype', self.numpy_type())
650            shape = kwargs.get('shape', (self.num_channels, self.buffer_size // self.num_channels))
651            buffer = np.fromfile(file_path, dtype=dtype)
652            self.buffer[:] = buffer.reshape(shape, order='C')
653        elif file_path.suffix == '.csv':
654            delimiter = kwargs.get('delimiter', ',')
655            self.buffer[:] = np.loadtxt(file_path, delimiter=delimiter)
656        elif file_path.suffix == '.npy':
657            self.buffer[:] = np.load(file_path)
658        elif file_path.suffix == '.npz':
659            data = np.load(file_path)
660            self.buffer[:] = data['arr_0']
661        elif file_path.suffix == '.txt':
662            self.buffer[:] = np.loadtxt(file_path)
663        elif file_path.suffix == '.h5' or file_path.suffix == '.hdf5':
664            import h5py
665            with h5py.File(file_path, 'r') as f:
666                self.buffer[:] = f['data'][()]
667        else:
668            raise ImportError("File format not supported")

Import the buffer from a file. The file format is determined by the file extension Supported file formats are:

  • .bin: raw binary file
  • .csv: comma-separated values file
  • .npy: numpy binary file
  • .npz: compressed numpy binary file
  • .txt: whitespace-delimited text file
  • .h5: hdf5 file format
Parameters
  • filename (str): the name of the file that the buffer should be imported from
Raises
  • ImportError: if the file format is not supported
def avail_card_len(self, available_samples: int = 0) -> None:
670    def avail_card_len(self, available_samples : int = 0) -> None:
671        """
672        Set the amount of data that has been read out of the data buffer (see register `SPC_DATA_AVAIL_CARD_LEN` in the manual)
673
674        Parameters
675        ----------
676        available_samples : int | pint.Quantity
677            the amount of data that is available for reading
678        """
679
680        available_samples = UnitConversion.convert(available_samples, units.Sa, int)
681        # print(available_samples, self.bytes_per_sample, self.num_channels)
682        available_bytes = self.samples_to_bytes(available_samples)
683        self.card.set_i(SPC_DATA_AVAIL_CARD_LEN, available_bytes)

Set the amount of data that has been read out of the data buffer (see register SPC_DATA_AVAIL_CARD_LEN in the manual)

Parameters
  • available_samples (int | pint.Quantity): the amount of data that is available for reading
def avail_user_pos(self, in_bytes: bool = False) -> int:
685    def avail_user_pos(self, in_bytes : bool = False) -> int:
686        """
687        Get the current position of the pointer in the data buffer (see register `SPC_DATA_AVAIL_USER_POS` in the manual)
688
689        Parameters
690        ----------
691        in_bytes : bool
692            if True, the position is returned in bytes
693
694        Returns
695        -------
696        int
697            pointer position
698        """
699
700        self.current_user_pos = self.card.get_i(SPC_DATA_AVAIL_USER_POS)
701        if not in_bytes:
702            self.current_user_pos = self.bytes_to_samples(self.current_user_pos)
703        return self.current_user_pos

Get the current position of the pointer in the data buffer (see register SPC_DATA_AVAIL_USER_POS in the manual)

Parameters
  • in_bytes (bool): if True, the position is returned in bytes
Returns
  • int: pointer position
def avail_user_len(self, in_bytes: bool = False) -> int:
705    def avail_user_len(self, in_bytes : bool = False) -> int:
706        """
707        Get the current length of the data in the data buffer (see register `SPC_DATA_AVAIL_USER_LEN` in the manual)
708
709        Parameters
710        ----------
711        in_bytes : bool
712            if True, the length is returned in bytes
713
714        Returns
715        -------
716        int
717            data length available
718        """
719
720        user_len = self.card.get_i(SPC_DATA_AVAIL_USER_LEN)
721        if not in_bytes:
722            user_len = self.bytes_to_samples(user_len)
723        return user_len

Get the current length of the data in the data buffer (see register SPC_DATA_AVAIL_USER_LEN in the manual)

Parameters
  • in_bytes (bool): if True, the length is returned in bytes
Returns
  • int: data length available
def fill_size_promille(self, return_unit=None) -> int:
725    def fill_size_promille(self, return_unit = None) -> int:
726        """
727        Get the fill size of the data buffer (see register `SPC_FILLSIZEPROMILLE` in the manual)
728
729        Returns
730        -------
731        int
732            fill size
733        """
734
735        return_value = self.card.get_i(SPC_FILLSIZEPROMILLE)
736        if return_unit is not None: return_value = UnitConversion.to_unit(return_value * units.promille, return_unit)
737        return return_value

Get the fill size of the data buffer (see register SPC_FILLSIZEPROMILLE in the manual)

Returns
  • int: fill size
def wait_dma(self) -> None:
739    def wait_dma(self) -> None:
740        """
741        Wait for the DMA transfer to finish (see register `M2CMD_DATA_WAITDMA` in the manual)
742        """
743        
744        self.card.cmd(M2CMD_DATA_WAITDMA)

Wait for the DMA transfer to finish (see register M2CMD_DATA_WAITDMA in the manual)

def wait(self) -> None:
739    def wait_dma(self) -> None:
740        """
741        Wait for the DMA transfer to finish (see register `M2CMD_DATA_WAITDMA` in the manual)
742        """
743        
744        self.card.cmd(M2CMD_DATA_WAITDMA)

Wait for the DMA transfer to finish (see register M2CMD_DATA_WAITDMA in the manual)

def numpy_type(self) -> numpy.ndarray[typing.Any, numpy.dtype[numpy.int64]]:
747    def numpy_type(self) -> npt.NDArray[np.int_]:
748        """
749        Get the type of numpy data from number of bytes
750
751        Returns
752        -------
753        numpy data type
754            the type of data that is used by the card
755        """
756
757        if self._8bit_mode:
758            return np.uint8
759        if self._12bit_mode:
760            return np.int8
761        if self.bits_per_sample == 1:
762            if self.num_channels <= 8:
763                return np.uint8
764            elif self.num_channels <= 16:
765                return np.uint16
766            elif self.num_channels <= 32:
767                return np.uint32
768            return np.uint64
769        if self.bits_per_sample <= 8:
770            return np.int8
771        elif self.bits_per_sample <= 16:
772            return np.int16
773        elif self.bits_per_sample <= 32:
774            return np.int32
775        return np.int64

Get the type of numpy data from number of bytes

Returns
  • numpy data type: the type of data that is used by the card
def data_conversion(self, mode: int = None) -> int:
778    def data_conversion(self, mode : int = None) -> int:
779        """
780        Set the data conversion mode (see register `SPC_DATACONVERSION` in the manual)
781        
782        Parameters
783        ----------
784        mode : int
785            the data conversion mode
786        """
787
788        if mode is not None:
789            self.card.set_i(SPC_DATACONVERSION, mode)
790        mode = self.card.get_i(SPC_DATACONVERSION)
791        self._8bit_mode = (mode == SPCM_DC_12BIT_TO_8BIT or mode == SPCM_DC_14BIT_TO_8BIT or mode == SPCM_DC_16BIT_TO_8BIT)
792        self._12bit_mode = (mode == SPCM_DC_12BIT_TO_12BITPACKED)
793        self._bits_per_sample()
794        self._bytes_per_sample()
795        return mode

Set the data conversion mode (see register SPC_DATACONVERSION in the manual)

Parameters
  • mode (int): the data conversion mode
def avail_data_conversion(self) -> int:
797    def avail_data_conversion(self) -> int:
798        """
799        Get the available data conversion modes (see register `SPC_AVAILDATACONVERSION` in the manual)
800
801        Returns
802        -------
803        int
804            the available data conversion modes
805        """
806        return self.card.get_i(SPC_AVAILDATACONVERSION)

Get the available data conversion modes (see register SPC_AVAILDATACONVERSION in the manual)

Returns
  • int: the available data conversion modes
iterator_index = 0
def verbose(self, verbose: bool = None) -> bool:
818    def verbose(self, verbose : bool = None) -> bool:
819        """
820        Set or get the verbose mode for the data transfer
821
822        Parameters
823        ----------
824        verbose : bool = None
825            the verbose mode
826        """
827
828        if verbose is not None:
829            self._verbose = verbose
830        return self._verbose

Set or get the verbose mode for the data transfer

Parameters
  • verbose (bool = None): the verbose mode
def to_transfer_samples(self, samples) -> None:
832    def to_transfer_samples(self, samples) -> None:
833        """
834        This method sets the number of samples to transfer
835
836        Parameters
837        ----------
838        samples : int | pint.Quantity
839            the number of samples to transfer
840        """
841
842        samples = UnitConversion.convert(samples, units.Sa, int)
843        self._to_transfer_samples = samples

This method sets the number of samples to transfer

Parameters
  • samples (int | pint.Quantity): the number of samples to transfer
class DDS(spcm.CardFunctionality):
 248class DDS(CardFunctionality):
 249    """a higher-level abstraction of the SpcmCardFunctionality class to implement DDS functionality
 250
 251    The DDS firmware allows the user a certain maximum number of dds cores, that 
 252    each on it's own generates a sine wave with the following parameters:
 253    * static parameters:
 254        + frequency
 255        + amplitude
 256        + phase
 257    * dynamic parameters:
 258        + frequency_slope
 259            changes the active frequency of the dds core with a linear slope
 260        + amplitude_slope
 261            changes the active amplitude of the dds core with a linear slope
 262    Each of these cores can either be added together and outputted, or specific groups
 263    of cores can be added together and outputted on a specific hardware output channel.
 264    Furthermore, specific dds cores can be connected to input parameters of another dds core.
 265
 266    For more information about what setups are available, please have a look at the user manual
 267    for your specific card.
 268
 269    Commands
 270    ---------
 271    The DDS functionality is controlled through commands that are listed and then written to the card.
 272    These written lists of commands are collected in a shadow register and are transferred to 
 273    the active register when a trigger is received.
 274    
 275    There are three different trigger sources, that can be set with the method 'trg_source()':
 276    * SPCM_DDS_TRG_SRC_NONE  = 0
 277        no triggers are generated and the commands are only transfered to the active register
 278        when a exec_now command is send
 279    * SPCM_DDS_TRG_SRC_TIMER = 1
 280        the triggers are generated on a timed grid with a period that can be set by the 
 281        method 'trg_timer()'
 282    * SPCM_DDS_TRG_SRC_CARD  = 2
 283        the triggers come from the card internal trigger logic (for more information, 
 284        see our product's user manual on how to setup the different triggers). In the DDS-mode
 285        multiple triggers can be processed, as with the mode SPC_STD_REP_SINGLERESTART.
 286    
 287    Note
 288    ----
 289    also the trigger source setting happens when a trigger comes. Hence a change of
 290    the trigger mode only happens after an 'arm()' command was send and an internal trigger was
 291    received.
 292 
 293    """
 294
 295    cores : list[DDSCore] = []
 296    channels : Channels = None
 297
 298    _current_core : int = -1
 299    _channel_from_core : dict[int, int] = {}
 300
 301    def __init__(self, *args, **kwargs) -> None:
 302        super().__init__(*args, **kwargs)
 303        self.channels = kwargs.get("channels", None)
 304        self.cores = []
 305        self.load_cores()
 306    
 307    def load_cores(self):
 308        """
 309        load the cores of the DDS functionality
 310        """
 311
 312        self.cores = []
 313        num_cores = self.num_cores()
 314        
 315        if self.channels is not None:
 316            for channel in self.channels:
 317                cores_on_channel = self.get_cores_on_channel(channel.index)
 318                for core in range(num_cores):
 319                    if cores_on_channel & (1 << core):
 320                        self._channel_from_core[core] = channel
 321        
 322        for core in range(num_cores):
 323            if core in self._channel_from_core:
 324                self.cores.append(DDSCore(core, self, channel=self._channel_from_core[core]))
 325            else:
 326                self.cores.append(DDSCore(core, self))
 327        
 328    def __len__(self) -> int:
 329        """
 330        get the number of cores
 331
 332        Returns
 333        -------
 334        int
 335            the number of cores
 336        """
 337        return len(self.cores)
 338    
 339    def __iter__(self):
 340        """
 341        make the class iterable
 342
 343        Returns
 344        -------
 345        self
 346        """
 347        return self
 348    
 349    def __next__(self):
 350        """
 351        get the next core
 352
 353        Returns
 354        -------
 355        DDSCore
 356            the next core
 357        """
 358
 359        self._current_core += 1
 360        if self._current_core < len(self.cores):
 361            return self.cores[self._current_core]
 362        else:
 363            self._current_core = -1
 364            raise StopIteration
 365    
 366    def __getitem__(self, index : int) -> DDSCore:
 367        """
 368        get a specific core
 369
 370        Parameters
 371        ----------
 372        index : int
 373            the index of the core
 374
 375        Returns
 376        -------
 377        DDSCore
 378            the specific core
 379        """
 380
 381        return self.cores[index]
 382
 383    def set_i(self, reg : int, value : int) -> None:
 384        """
 385        set an integer value to a register
 386
 387        Parameters
 388        ----------
 389        reg : int
 390            the register to be changed
 391        value : int
 392            the value to be set
 393        
 394        Raises
 395        ------
 396        SpcmException
 397            if the command list is full
 398        """
 399
 400        self.card.set_i(reg, value)
 401    
 402    def set_d(self, reg : int, value : float) -> None:
 403        """
 404        set a double value to a register
 405
 406        Parameters
 407        ----------
 408        reg : int
 409            the register to be changed
 410        value : float
 411            the value to be set
 412        """
 413    
 414        self.card.set_d(reg, value)
 415
 416    def reset(self) -> None:
 417        """
 418        Resets the DDS specific part of the firmware (see register `SPC_DDS_CMD` in the manual)
 419        """
 420
 421        self.cmd(SPCM_DDS_CMD_RESET)
 422    
 423    # DDS information
 424    def num_cores(self) -> int:
 425        """
 426        get the total num of available cores on the card. (see register `SPC_DDS_NUM_CORES` in the manual)
 427
 428        Returns
 429        -------
 430        int
 431            the available number of dds cores
 432        """
 433        return self.card.get_i(SPC_DDS_NUM_CORES)
 434    
 435    def queue_cmd_max(self):
 436        """
 437        get the total number of commands that can be hold by the queue. (see register `SPC_DDS_QUEUE_CMD_MAX` in the manual)
 438
 439        Returns
 440        -------
 441        int
 442            the total number of commands
 443        """
 444        return self.card.get_i(SPC_DDS_QUEUE_CMD_MAX)
 445    
 446    def queue_cmd_count(self):
 447        """
 448        get the current number of commands that are in the queue. (see register `SPC_DDS_QUEUE_CMD_COUNT` in the manual)
 449
 450        Returns
 451        -------
 452        int
 453            the current number of commands
 454        """
 455        return self.card.get_i(SPC_DDS_QUEUE_CMD_COUNT)
 456    
 457    def status(self):
 458        return self.card.get_i(SPC_DDS_STATUS)
 459
 460    # DDS setup settings
 461    def data_transfer_mode(self, mode : int) -> None:
 462        """
 463        set the data transfer mode for the DDS functionality (see register `SPC_DDS_DATA_TRANSFER_MODE` in the manual)
 464
 465        Parameters
 466        ----------
 467        mode : int
 468            the data transfer mode:
 469            * SPCM_DDS_DTM_SINGLE = 0
 470                the data is transferred using single commands (with lower latency)
 471            * SPCM_DDS_DTM_DMA = 1
 472                the data is transferred using DMA (with higher bandwidth)
 473        """
 474
 475        self._dtm = mode
 476        self.set_i(SPC_DDS_DATA_TRANSFER_MODE, mode)
 477    
 478    def get_data_transfer_mode(self) -> int:
 479        """
 480        get the data transfer mode for the DDS functionality (see register `SPC_DDS_DATA_TRANSFER_MODE` in the manual)
 481
 482        Returns
 483        -------
 484        int
 485            the data transfer mode:
 486            * SPCM_DDS_DTM_SINGLE = 0
 487                the data is transferred using single commands (with lower latency)
 488            * SPCM_DDS_DTM_DMA = 1
 489                the data is transferred using DMA (with higher bandwidth)
 490        """
 491
 492        self._dtm = self.card.get_i(SPC_DDS_DATA_TRANSFER_MODE)
 493        return self._dtm
 494    
 495    def phase_behaviour(self, behaviour : int) -> None:
 496        """
 497        set the phase behaviour of the DDS cores (see register `SPC_DDS_PHASE_BEHAVIOUR` in the manual)
 498
 499        Parameters
 500        ----------
 501        behaviour : int
 502            the phase behaviour
 503        """
 504
 505        self.set_i(SPC_DDS_PHASE_BEHAVIOUR, behaviour)
 506    
 507    def get_phase_behaviour(self) -> int:
 508        """
 509        get the phase behaviour of the DDS cores (see register `SPC_DDS_PHASE_BEHAVIOUR` in the manual)
 510
 511        Returns
 512        -------
 513        int
 514            the phase behaviour
 515        """
 516
 517        return self.card.get_i(SPC_DDS_PHASE_BEHAVIOUR)
 518
 519    def cores_on_channel(self, channel : int, *args) -> None:
 520        """
 521        setup the cores that are connected to a specific channel (see register `SPC_DDS_CORES_ON_CH0` in the manual)
 522
 523        Parameters
 524        ----------
 525        channel : int
 526            the channel number
 527        *args : int
 528            the cores that are connected to the channel
 529        
 530        TODO: change the channel associated with each core
 531        """
 532
 533        mask = 0
 534        for core in args:
 535            mask |= core
 536        self.set_i(SPC_DDS_CORES_ON_CH0 + channel, mask)
 537    
 538    def get_cores_on_channel(self, channel : int) -> int:
 539        """
 540        get the cores that are connected to a specific channel (see register `SPC_DDS_CORES_ON_CH0` in the manual)
 541
 542        Parameters
 543        ----------
 544        channel : int
 545            the channel number
 546
 547        Returns
 548        -------
 549        int
 550            the cores that are connected to the channel
 551        """
 552
 553        return self.card.get_i(SPC_DDS_CORES_ON_CH0 + channel)
 554
 555    def trg_src(self, src : int) -> None:
 556        """
 557        setup the source of where the trigger is coming from (see register `SPC_DDS_TRG_SRC` in the manual)
 558
 559        NOTE
 560        ---
 561        the trigger source is also set using the shadow register, hence only after an exec_at_trig or exec_now --
 562        
 563        Parameters
 564        ----------
 565        src : int
 566            set the trigger source:
 567            * SPCM_DDS_TRG_SRC_NONE  = 0
 568                no trigger source set, only exec_now changes what is output by the cores
 569            * SPCM_DDS_TRG_SRC_TIMER = 1
 570                an internal timer sends out triggers with a period defined by `trg_timer(period)`
 571            * SPCM_DDS_TRG_SRC_CARD  = 2
 572                use the trigger engine of the card (see the user manual for more information about setting up the trigger engine)
 573        """
 574
 575        self.set_i(SPC_DDS_TRG_SRC, src)
 576
 577    def get_trg_src(self) -> int:
 578        """
 579        get the source of where the trigger is coming from (see register `SPC_DDS_TRG_SRC` in the manual)
 580
 581        NOTE
 582        ----
 583        the trigger source is also set using the shadow register, hence only after an exec_at_trig or exec_now --
 584        
 585        Returns
 586        ----------
 587        int
 588            get one of the trigger source:
 589            * SPCM_DDS_TRG_SRC_NONE  = 0
 590                no trigger source set, only exec_now changes what is output by the cores
 591            * SPCM_DDS_TRG_SRC_TIMER = 1
 592                an internal timer sends out triggers with a period defined by `trg_timer(period)`
 593            * SPCM_DDS_TRG_SRC_CARD  = 2
 594                use the trigger engine of the card (see the user manual for more information about setting up the trigger engine)
 595        """
 596
 597        return self.card.get_i(SPC_DDS_TRG_SRC)
 598    
 599    def trg_timer(self, period : float) -> None:
 600        """
 601        set the period at which the timer should raise DDS trigger events. (see register `SPC_DDS_TRG_TIMER` in the manual)
 602
 603        NOTE
 604        ----
 605        only used in conjecture with the trigger source set to SPCM_DDS_TRG_SRC_TIMER ---
 606        
 607        Parameters
 608        ----------
 609        period : float | pint.Quantity
 610            the time between DDS trigger events in seconds
 611        """
 612
 613        period = UnitConversion.convert(period, units.s, float, rounding=None)
 614        self.set_d(SPC_DDS_TRG_TIMER, float(period))
 615    
 616    def get_trg_timer(self, return_unit = None) -> float:
 617        """
 618        get the period at which the timer should raise DDS trigger events. (see register `SPC_DDS_TRG_TIMER` in the manual)
 619
 620        NOTE
 621        ----
 622        only used in conjecture with the trigger source set to SPCM_DDS_TRG_SRC_TIMER ---
 623
 624        Parameters
 625        ----------
 626        return_unit : pint.Unit = None
 627            the unit of the returned time between DDS trigger events, by default None
 628        
 629        Returns
 630        ----------
 631        float
 632            the time between DDS trigger events in seconds
 633        """
 634
 635        return_value = self.card.get_d(SPC_DDS_TRG_TIMER)
 636        if return_unit is not None: return_value = UnitConversion.to_unit(return_value * units.s, return_unit)
 637        return return_value
 638
 639    def x_mode(self, xio : int, mode : int) -> None:
 640        """
 641        setup the kind of output that the XIO outputs will give (see register `SPC_DDS_X0_MODE` in the manual)
 642
 643        Parameters
 644        ----------
 645        xio : int
 646            the XIO channel number
 647        mode : int
 648            the mode that the channel needs to run in
 649        """
 650
 651        self.set_i(SPC_DDS_X0_MODE + xio, mode)
 652
 653    def get_x_mode(self, xio : int) -> int:
 654        """
 655        get the kind of output that the XIO outputs will give (see register `SPC_DDS_X0_MODE` in the manual)
 656
 657        Parameters
 658        ----------
 659        xio : int
 660            the XIO channel number
 661
 662        Returns
 663        -------
 664        int
 665            the mode that the channel needs to run in
 666            SPC_DDS_XIO_SEQUENCE = 0
 667                turn on and off the XIO channels using commands in the DDS cmd queue
 668            SPC_DDS_XIO_ARM = 1
 669                when the DDS firmware is waiting for a trigger to come this signal is high
 670            SPC_DDS_XIO_LATCH = 2
 671                when the DDS firmware starts executing a change this signal is high
 672        """
 673
 674        return self.card.get_i(SPC_DDS_X0_MODE + xio)
 675
 676    def freq_ramp_stepsize(self, divider : int) -> None:
 677        """
 678        number of timesteps before the frequency is changed during a frequency ramp. (see register `SPC_DDS_FREQ_RAMP_STEPSIZE` in the manual)
 679
 680        NOTES
 681        -----
 682        - this is a global setting for all cores
 683        - internally the time divider is used to calculate the amount of change per event using a given frequency slope, please set the time divider before setting the frequency slope
 684        
 685        Parameters
 686        ----------
 687        divider : int
 688            the number of DDS timesteps that a value is kept constant during a frequency ramp
 689        """
 690
 691        self.set_i(SPC_DDS_FREQ_RAMP_STEPSIZE, int(divider))
 692
 693    def get_freq_ramp_stepsize(self) -> int:
 694        """
 695        get the number of timesteps before the frequency is changed during a frequency ramp. (see register `SPC_DDS_FREQ_RAMP_STEPSIZE` in the manual)
 696        
 697        NOTES
 698        -----
 699        - this is a global setting for all cores
 700        - internally the time divider is used to calculate the amount of change per event using a given frequency slope, please set the time divider before setting the frequency slope
 701        
 702        Returns
 703        ----------
 704        divider : int
 705            the number of DDS timesteps that a value is kept constant during a frequency ramp
 706        """
 707
 708        return self.card.get_i(SPC_DDS_FREQ_RAMP_STEPSIZE)
 709
 710    def amp_ramp_stepsize(self, divider : int) -> None:
 711        """
 712        number of timesteps before the amplitude is changed during a frequency ramp. (see register `SPC_DDS_AMP_RAMP_STEPSIZE` in the manual)
 713
 714        NOTES
 715        -----
 716        - this is a global setting for all cores
 717        - internally the time divider is used to calculate the amount of change per event using a given amplitude slope, 
 718            please set the time divider before setting the amplitude slope
 719        
 720        Parameters
 721        ----------
 722        divider : int
 723            the number of DDS timesteps that a value is kept constant during an amplitude ramp
 724        """
 725
 726        self.set_i(SPC_DDS_AMP_RAMP_STEPSIZE, int(divider))
 727
 728    def get_amp_ramp_stepsize(self) -> int:
 729        """
 730        get the number of timesteps before the amplitude is changed during a frequency ramp. (see register `SPC_DDS_AMP_RAMP_STEPSIZE` in the manual)
 731
 732        NOTES
 733        -----
 734        - this is a global setting for all cores
 735        - internally the time divider is used to calculate the amount of change per event using a given amplitude slope, 
 736            please set the time divider before setting the amplitude slope
 737        
 738        Returns
 739        ----------
 740        divider : int
 741            the number of DDS timesteps that a value is kept constant during an amplitude ramp
 742        """
 743
 744        return self.card.get_i(SPC_DDS_AMP_RAMP_STEPSIZE)
 745
 746    # DDS "static" parameters
 747    # def amp(self, core_index : int, amplitude : float) -> None:
 748    def amp(self, *args) -> None:
 749        """
 750        set the amplitude of the sine wave of a specific core (see register `SPC_DDS_CORE0_AMP` in the manual)
 751        
 752        Parameters
 753        ----------
 754        core_index : int (optional)
 755            the index of the core to be changed
 756        amplitude : float
 757            the value between 0 and 1 corresponding to the amplitude
 758        """
 759
 760        if len(args) == 1:
 761            amplitude = args[0]
 762            for core in self.cores:
 763                core.amp(amplitude)
 764        elif len(args) == 2:
 765            core_index, amplitude = args
 766            self.cores[core_index].amp(amplitude)
 767        else:
 768            raise TypeError("amp() takes 1 or 2 positional arguments ({} given)".format(len(args) + 1))
 769        # self.set_d(SPC_DDS_CORE0_AMP + core_index, float(amplitude))
 770    # aliases
 771    amplitude = amp
 772
 773    def get_amp(self, core_index : int, return_unit = None) -> float:
 774        """
 775        gets the amplitude of the sine wave of a specific core (see register `SPC_DDS_CORE0_AMP` in the manual)
 776        
 777        Parameters
 778        ----------
 779        core_index : int
 780            the index of the core to be changed
 781        return_unit : pint.Unit = None
 782            the unit of the returned amplitude, by default None
 783
 784        Returns
 785        -------
 786        float | pint.Quantity
 787            the value between 0 and 1 corresponding to the amplitude of the specific core or in the specified unit
 788        """
 789
 790        return self.cores[core_index].get_amp(return_unit)
 791        # return self.card.get_d(SPC_DDS_CORE0_AMP + core_index)
 792    # aliases
 793    get_amplitude = get_amp
 794
 795    def avail_amp_min(self) -> float:
 796        """
 797        get the minimum available amplitude (see register `SPC_DDS_AVAIL_AMP_MIN` in the manual)
 798
 799        Returns
 800        -------
 801        float
 802            the minimum available amplitude
 803        
 804        TODO: unitize!
 805        """
 806
 807        return self.card.get_d(SPC_DDS_AVAIL_AMP_MIN)
 808    
 809    def avail_amp_max(self) -> float:
 810        """
 811        get the maximum available amplitude (see register `SPC_DDS_AVAIL_AMP_MAX` in the manual)
 812
 813        Returns
 814        -------
 815        float
 816            the maximum available amplitude
 817        
 818        TODO: unitize!
 819        """
 820
 821        return self.card.get_d(SPC_DDS_AVAIL_AMP_MAX)
 822    
 823    def avail_amp_step(self) -> float:
 824        """
 825        get the step size of the available amplitudes (see register `SPC_DDS_AVAIL_AMP_STEP` in the manual)
 826
 827        Returns
 828        -------
 829        float
 830            the step size of the available amplitudes
 831        
 832        TODO: unitize!
 833        """
 834
 835        return self.card.get_d(SPC_DDS_AVAIL_AMP_STEP)
 836
 837    # def freq(self, core_index : int, frequency : float) -> None:
 838    def freq(self, *args) -> None:
 839        """
 840        set the frequency of the sine wave of a specific core (see register `SPC_DDS_CORE0_FREQ` in the manual)
 841        
 842        Parameters
 843        ----------
 844        core_index : int (optional)
 845            the index of the core to be changed
 846        frequency : float
 847            the value of the frequency in Hz
 848        """
 849
 850        if len(args) == 1:
 851            frequency = args[0]
 852            for core in self.cores:
 853                core.freq(frequency)
 854        elif len(args) == 2:
 855            core_index, frequency = args
 856            self.cores[core_index].freq(frequency)
 857        else:
 858            raise TypeError("freq() takes 1 or 2 positional arguments ({} given)".format(len(args) + 1))
 859        # self.set_d(SPC_DDS_CORE0_FREQ + core_index, float(frequency))
 860    # aliases
 861    frequency = freq
 862
 863    def get_freq(self, core_index : int, return_unit = None) -> float:
 864        """
 865        gets the frequency of the sine wave of a specific core (see register `SPC_DDS_CORE0_FREQ` in the manual)
 866        
 867        Parameters
 868        ----------
 869        core_index : int
 870            the index of the core to be changed
 871        return_unit : pint.Unit = None
 872            the unit of the returned frequency, by default None
 873        
 874        Returns
 875        -------
 876        float | pint.Quantity
 877            the value of the frequency in Hz the specific core or in the specified unit
 878        """
 879
 880        return self.cores[core_index].get_freq(return_unit)
 881    # aliases
 882    get_frequency = get_freq
 883
 884    def avail_freq_min(self) -> float:
 885        """
 886        get the minimum available frequency (see register `SPC_DDS_AVAIL_FREQ_MIN` in the manual)
 887
 888        Returns
 889        -------
 890        float
 891            the minimum available frequency
 892        
 893        TODO: unitize!
 894        """
 895
 896        return self.card.get_d(SPC_DDS_AVAIL_FREQ_MIN)
 897    
 898    def avail_freq_max(self) -> float:
 899        """
 900        get the maximum available frequency (see register `SPC_DDS_AVAIL_FREQ_MAX` in the manual)
 901
 902        Returns
 903        -------
 904        float
 905            the maximum available frequency
 906        
 907        TODO: unitize!
 908        """
 909
 910        return self.card.get_d(SPC_DDS_AVAIL_FREQ_MAX)
 911    
 912    def avail_freq_step(self) -> float:
 913        """
 914        get the step size of the available frequencies (see register `SPC_DDS_AVAIL_FREQ_STEP` in the manual)
 915
 916        Returns
 917        -------
 918        float
 919            the step size of the available frequencies
 920        
 921        TODO: unitize!
 922        """
 923
 924        return self.card.get_d(SPC_DDS_AVAIL_FREQ_STEP)
 925
 926    # def phase(self, core_index : int, phase : float) -> None:
 927    def phase(self, *args) -> None:
 928        """
 929        set the phase of the sine wave of a specific core (see register `SPC_DDS_CORE0_PHASE` in the manual)
 930        
 931        Parameters
 932        ----------
 933        core_index : int (optional)
 934            the index of the core to be changed
 935        phase : float
 936            the value between 0 and 360 degrees of the phase
 937        """
 938
 939        if len(args) == 1:
 940            phase = args[0]
 941            for core in self.cores:
 942                core.phase(phase)
 943        elif len(args) == 2:
 944            core_index, phase = args
 945            self.cores[core_index].phase(phase)
 946        else:
 947            raise TypeError("phase() takes 1 or 2 positional arguments ({} given)".format(len(args) + 1))
 948        # self.set_d(SPC_DDS_CORE0_PHASE + core_index, float(phase))
 949
 950    def get_phase(self, core_index : int, return_unit = None) -> float:
 951        """
 952        gets the phase of the sine wave of a specific core (see register `SPC_DDS_CORE0_PHASE` in the manual)
 953        
 954        Parameters
 955        ----------
 956        core_index : int
 957            the index of the core to be changed
 958        return_unit : pint.Unit = None
 959            the unit of the returned phase, by default None
 960        
 961        Returns
 962        -------
 963        float
 964            the value between 0 and 360 degrees of the phase
 965        """
 966
 967        return self.cores[core_index].get_phase(return_unit)
 968      
 969    def avail_phase_min(self) -> float:
 970        """
 971        get the minimum available phase (see register `SPC_DDS_AVAIL_PHASE_MIN` in the manual)
 972
 973        Returns
 974        -------
 975        float
 976            the minimum available phase
 977        
 978        TODO: unitize!
 979        """
 980
 981        return self.card.get_d(SPC_DDS_AVAIL_PHASE_MIN)
 982    
 983    def avail_phase_max(self) -> float:
 984        """
 985        get the maximum available phase (see register `SPC_DDS_AVAIL_PHASE_MAX` in the manual)
 986
 987        Returns
 988        -------
 989        float
 990            the maximum available phase
 991        
 992        TODO: unitize!
 993        """
 994
 995        return self.card.get_d(SPC_DDS_AVAIL_PHASE_MAX)
 996    
 997    def avail_phase_step(self) -> float:
 998        """
 999        get the step size of the available phases (see register `SPC_DDS_AVAIL_PHASE_STEP` in the manual)
1000
1001        Returns
1002        -------
1003        float
1004            the step size of the available phases
1005        
1006        TODO: unitize!
1007        """
1008
1009        return self.card.get_d(SPC_DDS_AVAIL_PHASE_STEP)
1010
1011    def x_manual_output(self, state_mask : int) -> None:
1012        """
1013        set the output of the xio channels using a bit mask (see register `SPC_DDS_X_MANUAL_OUTPUT` in the manual)
1014        
1015        Parameters
1016        ----------
1017        state_mask : int
1018            bit mask where the bits correspond to specific channels and 1 to on and 0 to off.
1019        """
1020
1021        self.set_i(SPC_DDS_X_MANUAL_OUTPUT, state_mask)
1022
1023    def get_x_manual_output(self) -> int:
1024        """
1025        get the output of the xio channels using a bit mask (see register `SPC_DDS_X_MANUAL_OUTPUT` in the manual)
1026        
1027        Returns
1028        ----------
1029        int
1030            bit mask where the bits correspond to specific channels and 1 to on and 0 to off.
1031        """
1032
1033        return self.card.get_i(SPC_DDS_X_MANUAL_OUTPUT)
1034
1035    # DDS dynamic parameters
1036    # def freq_slope(self, core_index : int, slope : float) -> None:
1037    def freq_slope(self, *args) -> None:
1038        """
1039        set the frequency slope of the linearly changing frequency of the sine wave of a specific core (see register `SPC_DDS_CORE0_FREQ_SLOPE` in the manual)
1040        
1041        Parameters
1042        ----------
1043        core_index : int (optional)
1044            the index of the core to be changed
1045        slope : float
1046            the rate of frequency change in Hz/s
1047        """
1048
1049        if len(args) == 1:
1050            slope = args[0]
1051            for core in self.cores:
1052                core.freq_slope(slope)
1053        elif len(args) == 2:
1054            core_index, slope = args
1055            self.cores[core_index].freq_slope(slope)
1056        else:
1057            raise TypeError("freq_slope() takes 1 or 2 positional arguments ({} given)".format(len(args) + 1))
1058        # self.set_d(SPC_DDS_CORE0_FREQ_SLOPE + core_index, float(slope))
1059    # aliases
1060    frequency_slope = freq_slope
1061
1062    def get_freq_slope(self, core_index : int, return_unit=None) -> float:
1063        """
1064        get the frequency slope of the linearly changing frequency of the sine wave of a specific core (see register `SPC_DDS_CORE0_FREQ_SLOPE` in the manual)
1065        
1066        Parameters
1067        ----------
1068        core_index : int
1069            the index of the core to be changed
1070        return_unit : pint.Unit = None
1071            the unit of the returned frequency slope, by default None
1072        
1073        Returns
1074        -------
1075        float
1076            the rate of frequency change in Hz/s
1077        """
1078
1079        return self.cores[core_index].get_freq_slope(return_unit)
1080    # aliases
1081    get_frequency_slope = get_freq_slope
1082
1083    def avail_freq_slope_min(self) -> float:
1084        """
1085        get the minimum available frequency slope (see register `SPC_DDS_AVAIL_FREQ_SLOPE_MIN` in the manual)
1086
1087        Returns
1088        -------
1089        float
1090            the minimum available frequency slope
1091        
1092        TODO: unitize!
1093        """
1094
1095        return self.card.get_d(SPC_DDS_AVAIL_FREQ_SLOPE_MIN)
1096    
1097    def avail_freq_slope_max(self) -> float:
1098        """
1099        get the maximum available frequency slope (see register `SPC_DDS_AVAIL_FREQ_SLOPE_MAX` in the manual)
1100
1101        Returns
1102        -------
1103        float
1104            the maximum available frequency slope
1105        
1106        TODO: unitize!
1107        """
1108
1109        return self.card.get_d(SPC_DDS_AVAIL_FREQ_SLOPE_MAX)
1110    
1111    def avail_freq_slope_step(self) -> float:
1112        """
1113        get the step size of the available frequency slopes (see register `SPC_DDS_AVAIL_FREQ_SLOPE_STEP` in the manual)
1114
1115        Returns
1116        -------
1117        float
1118            the step size of the available frequency slopes
1119        
1120        TODO: unitize!
1121        """
1122
1123        return self.card.get_d(SPC_DDS_AVAIL_FREQ_SLOPE_STEP)
1124
1125    # def amp_slope(self, core_index : int, slope : float) -> None:
1126    def amp_slope(self, *args) -> None:
1127        """
1128        set the amplitude slope of the linearly changing amplitude of the sine wave of a specific core (see register `SPC_DDS_CORE0_AMP_SLOPE` in the manual)
1129        
1130        Parameters
1131        ----------
1132        core_index : int (optional)
1133            the index of the core to be changed
1134        slope : float
1135            the rate of amplitude change in 1/s
1136        """
1137
1138        if len(args) == 1:
1139            slope = args[0]
1140            for core in self.cores:
1141                core.amp_slope(slope)
1142        elif len(args) == 2:
1143            core_index, slope = args
1144            self.cores[core_index].amp_slope(slope)
1145        else:
1146            raise TypeError("amp_slope() takes 1 or 2 positional arguments ({} given)".format(len(args) + 1))
1147        # self.set_d(SPC_DDS_CORE0_AMP_SLOPE + core_index, float(slope))
1148    # aliases
1149    amplitude_slope = amp_slope
1150
1151    def get_amp_slope(self, core_index : int, return_unit = None) -> float:
1152        """
1153        set the amplitude slope of the linearly changing amplitude of the sine wave of a specific core (see register `SPC_DDS_CORE0_AMP_SLOPE` in the manual)
1154        
1155        Parameters
1156        ----------
1157        core_index : int
1158            the index of the core to be changed
1159        return_unit : pint.Unit = None
1160            the unit of the returned amplitude slope, by default None
1161        
1162        Returns
1163        -------
1164        float
1165            the rate of amplitude change in 1/s
1166        """
1167
1168        return self.cores[core_index].get_amp_slope(return_unit)
1169    # aliases
1170    amplitude_slope = amp_slope
1171
1172    def avail_amp_slope_min(self) -> float:
1173        """
1174        get the minimum available amplitude slope (see register `SPC_DDS_AVAIL_AMP_SLOPE_MIN` in the manual)
1175
1176        Returns
1177        -------
1178        float
1179            the minimum available amplitude slope
1180        
1181        TODO: unitize!
1182        """
1183
1184        return self.card.get_d(SPC_DDS_AVAIL_AMP_SLOPE_MIN)
1185    
1186    def avail_amp_slope_max(self) -> float:
1187        """
1188        get the maximum available amplitude slope (see register `SPC_DDS_AVAIL_AMP_SLOPE_MAX` in the manual)
1189
1190        Returns
1191        -------
1192        float
1193            the maximum available amplitude slope
1194        
1195        TODO: unitize!
1196        """
1197
1198        return self.card.get_d(SPC_DDS_AVAIL_AMP_SLOPE_MAX)
1199    
1200    def avail_amp_slope_step(self) -> float:
1201        """
1202        get the step size of the available amplitude slopes (see register `SPC_DDS_AVAIL_AMP_SLOPE_STEP` in the manual)
1203
1204        Returns
1205        -------
1206        float
1207            the step size of the available amplitude slopes
1208        
1209        TODO: unitize!
1210        """
1211
1212        return self.card.get_d(SPC_DDS_AVAIL_AMP_SLOPE_STEP)
1213
1214    # DDS control
1215    def cmd(self, command : int) -> None:
1216        """
1217        execute a DDS specific control flow command (see register `SPC_DDS_CMD` in the manual)
1218        
1219        Parameters
1220        ----------
1221        command : int
1222            DDS specific command
1223        """
1224
1225        self.set_i(SPC_DDS_CMD, command)
1226
1227    def exec_at_trg(self) -> None:
1228        """
1229        execute the commands in the shadow register at the next trigger event (see register `SPC_DDS_CMD` in the manual)
1230        """
1231        self.cmd(SPCM_DDS_CMD_EXEC_AT_TRG)
1232    # aliases
1233    arm = exec_at_trg
1234    wait_for_trg = exec_at_trg
1235    
1236    def exec_now(self) -> None:
1237        """
1238        execute the commands in the shadow register as soon as possible (see register `SPC_DDS_CMD` in the manual)
1239        """
1240
1241        self.cmd(SPCM_DDS_CMD_EXEC_NOW)
1242    # aliases
1243    direct_latch = exec_now
1244
1245    def trg_count(self) -> int:
1246        """
1247        get the number of trigger exec_at_trg and exec_now command that have been executed (see register `SPC_DDS_TRG_COUNT` in the manual)
1248
1249        Returns
1250        -------
1251        int
1252            the number of trigger exec_at_trg and exec_now command that have been executed
1253        """
1254
1255        return self.card.get_i(SPC_DDS_TRG_COUNT)
1256    
1257    def write_to_card(self, flags=0) -> None:
1258        """
1259        send a list of all the commands that came after the last write_list and send them to the card (see register `SPC_DDS_CMD` in the manual)
1260        """
1261        
1262        self.cmd(SPCM_DDS_CMD_WRITE_TO_CARD | flags)
1263    
1264    # DDS helper functions
1265    def kwargs2mask(self, kwargs : dict[str, bool], prefix : str = "") -> int:
1266        """
1267        DDS helper: transform a dictionary with keys with a specific prefix to a bitmask
1268
1269        Parameters
1270        ----------
1271        kwargs : dict
1272            dictonary with keys with a specific prefix and values given by bools
1273        prefix : str
1274            a prefix for the key names
1275        
1276        Returns
1277        -------
1278        int
1279            bit mask
1280        
1281        Example
1282        -------
1283        ['core_0' = True, 'core_2' = False, 'core_3' = True] => 0b1001 = 9
1284        """
1285        
1286        mask = 0
1287        for keyword, value in kwargs.items():
1288            bit = int(keyword[len(prefix)+1:])
1289            if value:
1290                mask |= 1 << bit
1291            else:
1292                mask &= ~(1 << bit)
1293        return mask
1294    # aliases
1295    k2m = kwargs2mask

a higher-level abstraction of the SpcmCardFunctionality class to implement DDS functionality

The DDS firmware allows the user a certain maximum number of dds cores, that each on it's own generates a sine wave with the following parameters:

  • static parameters:
    • frequency
    • amplitude
    • phase
  • dynamic parameters:
    • frequency_slope changes the active frequency of the dds core with a linear slope
    • amplitude_slope changes the active amplitude of the dds core with a linear slope Each of these cores can either be added together and outputted, or specific groups of cores can be added together and outputted on a specific hardware output channel. Furthermore, specific dds cores can be connected to input parameters of another dds core.

For more information about what setups are available, please have a look at the user manual for your specific card.

Commands

The DDS functionality is controlled through commands that are listed and then written to the card. These written lists of commands are collected in a shadow register and are transferred to the active register when a trigger is received.

There are three different trigger sources, that can be set with the method 'trg_source()':

  • SPCM_DDS_TRG_SRC_NONE = 0 no triggers are generated and the commands are only transfered to the active register when a exec_now command is send
  • SPCM_DDS_TRG_SRC_TIMER = 1 the triggers are generated on a timed grid with a period that can be set by the method 'trg_timer()'
  • SPCM_DDS_TRG_SRC_CARD = 2 the triggers come from the card internal trigger logic (for more information, see our product's user manual on how to setup the different triggers). In the DDS-mode multiple triggers can be processed, as with the mode SPC_STD_REP_SINGLERESTART.
Note

also the trigger source setting happens when a trigger comes. Hence a change of the trigger mode only happens after an 'arm()' command was send and an internal trigger was received.

DDS(*args, **kwargs)
301    def __init__(self, *args, **kwargs) -> None:
302        super().__init__(*args, **kwargs)
303        self.channels = kwargs.get("channels", None)
304        self.cores = []
305        self.load_cores()

Takes a Card object that is used by the functionality

Parameters
  • card (Card): a Card object on which the functionality works
cores: list[DDSCore] = []
channels: Channels = None
def load_cores(self):
307    def load_cores(self):
308        """
309        load the cores of the DDS functionality
310        """
311
312        self.cores = []
313        num_cores = self.num_cores()
314        
315        if self.channels is not None:
316            for channel in self.channels:
317                cores_on_channel = self.get_cores_on_channel(channel.index)
318                for core in range(num_cores):
319                    if cores_on_channel & (1 << core):
320                        self._channel_from_core[core] = channel
321        
322        for core in range(num_cores):
323            if core in self._channel_from_core:
324                self.cores.append(DDSCore(core, self, channel=self._channel_from_core[core]))
325            else:
326                self.cores.append(DDSCore(core, self))

load the cores of the DDS functionality

def set_i(self, reg: int, value: int) -> None:
383    def set_i(self, reg : int, value : int) -> None:
384        """
385        set an integer value to a register
386
387        Parameters
388        ----------
389        reg : int
390            the register to be changed
391        value : int
392            the value to be set
393        
394        Raises
395        ------
396        SpcmException
397            if the command list is full
398        """
399
400        self.card.set_i(reg, value)

set an integer value to a register

Parameters
  • reg (int): the register to be changed
  • value (int): the value to be set
Raises
  • SpcmException: if the command list is full
def set_d(self, reg: int, value: float) -> None:
402    def set_d(self, reg : int, value : float) -> None:
403        """
404        set a double value to a register
405
406        Parameters
407        ----------
408        reg : int
409            the register to be changed
410        value : float
411            the value to be set
412        """
413    
414        self.card.set_d(reg, value)

set a double value to a register

Parameters
  • reg (int): the register to be changed
  • value (float): the value to be set
def reset(self) -> None:
416    def reset(self) -> None:
417        """
418        Resets the DDS specific part of the firmware (see register `SPC_DDS_CMD` in the manual)
419        """
420
421        self.cmd(SPCM_DDS_CMD_RESET)

Resets the DDS specific part of the firmware (see register SPC_DDS_CMD in the manual)

def num_cores(self) -> int:
424    def num_cores(self) -> int:
425        """
426        get the total num of available cores on the card. (see register `SPC_DDS_NUM_CORES` in the manual)
427
428        Returns
429        -------
430        int
431            the available number of dds cores
432        """
433        return self.card.get_i(SPC_DDS_NUM_CORES)

get the total num of available cores on the card. (see register SPC_DDS_NUM_CORES in the manual)

Returns
  • int: the available number of dds cores
def queue_cmd_max(self):
435    def queue_cmd_max(self):
436        """
437        get the total number of commands that can be hold by the queue. (see register `SPC_DDS_QUEUE_CMD_MAX` in the manual)
438
439        Returns
440        -------
441        int
442            the total number of commands
443        """
444        return self.card.get_i(SPC_DDS_QUEUE_CMD_MAX)

get the total number of commands that can be hold by the queue. (see register SPC_DDS_QUEUE_CMD_MAX in the manual)

Returns
  • int: the total number of commands
def queue_cmd_count(self):
446    def queue_cmd_count(self):
447        """
448        get the current number of commands that are in the queue. (see register `SPC_DDS_QUEUE_CMD_COUNT` in the manual)
449
450        Returns
451        -------
452        int
453            the current number of commands
454        """
455        return self.card.get_i(SPC_DDS_QUEUE_CMD_COUNT)

get the current number of commands that are in the queue. (see register SPC_DDS_QUEUE_CMD_COUNT in the manual)

Returns
  • int: the current number of commands
def status(self):
457    def status(self):
458        return self.card.get_i(SPC_DDS_STATUS)
def data_transfer_mode(self, mode: int) -> None:
461    def data_transfer_mode(self, mode : int) -> None:
462        """
463        set the data transfer mode for the DDS functionality (see register `SPC_DDS_DATA_TRANSFER_MODE` in the manual)
464
465        Parameters
466        ----------
467        mode : int
468            the data transfer mode:
469            * SPCM_DDS_DTM_SINGLE = 0
470                the data is transferred using single commands (with lower latency)
471            * SPCM_DDS_DTM_DMA = 1
472                the data is transferred using DMA (with higher bandwidth)
473        """
474
475        self._dtm = mode
476        self.set_i(SPC_DDS_DATA_TRANSFER_MODE, mode)

set the data transfer mode for the DDS functionality (see register SPC_DDS_DATA_TRANSFER_MODE in the manual)

Parameters
  • mode (int): the data transfer mode:
    • SPCM_DDS_DTM_SINGLE = 0 the data is transferred using single commands (with lower latency)
    • SPCM_DDS_DTM_DMA = 1 the data is transferred using DMA (with higher bandwidth)
def get_data_transfer_mode(self) -> int:
478    def get_data_transfer_mode(self) -> int:
479        """
480        get the data transfer mode for the DDS functionality (see register `SPC_DDS_DATA_TRANSFER_MODE` in the manual)
481
482        Returns
483        -------
484        int
485            the data transfer mode:
486            * SPCM_DDS_DTM_SINGLE = 0
487                the data is transferred using single commands (with lower latency)
488            * SPCM_DDS_DTM_DMA = 1
489                the data is transferred using DMA (with higher bandwidth)
490        """
491
492        self._dtm = self.card.get_i(SPC_DDS_DATA_TRANSFER_MODE)
493        return self._dtm

get the data transfer mode for the DDS functionality (see register SPC_DDS_DATA_TRANSFER_MODE in the manual)

Returns
  • int: the data transfer mode:
    • SPCM_DDS_DTM_SINGLE = 0 the data is transferred using single commands (with lower latency)
    • SPCM_DDS_DTM_DMA = 1 the data is transferred using DMA (with higher bandwidth)
def phase_behaviour(self, behaviour: int) -> None:
495    def phase_behaviour(self, behaviour : int) -> None:
496        """
497        set the phase behaviour of the DDS cores (see register `SPC_DDS_PHASE_BEHAVIOUR` in the manual)
498
499        Parameters
500        ----------
501        behaviour : int
502            the phase behaviour
503        """
504
505        self.set_i(SPC_DDS_PHASE_BEHAVIOUR, behaviour)

set the phase behaviour of the DDS cores (see register SPC_DDS_PHASE_BEHAVIOUR in the manual)

Parameters
  • behaviour (int): the phase behaviour
def get_phase_behaviour(self) -> int:
507    def get_phase_behaviour(self) -> int:
508        """
509        get the phase behaviour of the DDS cores (see register `SPC_DDS_PHASE_BEHAVIOUR` in the manual)
510
511        Returns
512        -------
513        int
514            the phase behaviour
515        """
516
517        return self.card.get_i(SPC_DDS_PHASE_BEHAVIOUR)

get the phase behaviour of the DDS cores (see register SPC_DDS_PHASE_BEHAVIOUR in the manual)

Returns
  • int: the phase behaviour
def cores_on_channel(self, channel: int, *args) -> None:
519    def cores_on_channel(self, channel : int, *args) -> None:
520        """
521        setup the cores that are connected to a specific channel (see register `SPC_DDS_CORES_ON_CH0` in the manual)
522
523        Parameters
524        ----------
525        channel : int
526            the channel number
527        *args : int
528            the cores that are connected to the channel
529        
530        TODO: change the channel associated with each core
531        """
532
533        mask = 0
534        for core in args:
535            mask |= core
536        self.set_i(SPC_DDS_CORES_ON_CH0 + channel, mask)

setup the cores that are connected to a specific channel (see register SPC_DDS_CORES_ON_CH0 in the manual)

Parameters
  • channel (int): the channel number
  • *args (int): the cores that are connected to the channel
  • TODO (change the channel associated with each core):
def get_cores_on_channel(self, channel: int) -> int:
538    def get_cores_on_channel(self, channel : int) -> int:
539        """
540        get the cores that are connected to a specific channel (see register `SPC_DDS_CORES_ON_CH0` in the manual)
541
542        Parameters
543        ----------
544        channel : int
545            the channel number
546
547        Returns
548        -------
549        int
550            the cores that are connected to the channel
551        """
552
553        return self.card.get_i(SPC_DDS_CORES_ON_CH0 + channel)

get the cores that are connected to a specific channel (see register SPC_DDS_CORES_ON_CH0 in the manual)

Parameters
  • channel (int): the channel number
Returns
  • int: the cores that are connected to the channel
def trg_src(self, src: int) -> None:
555    def trg_src(self, src : int) -> None:
556        """
557        setup the source of where the trigger is coming from (see register `SPC_DDS_TRG_SRC` in the manual)
558
559        NOTE
560        ---
561        the trigger source is also set using the shadow register, hence only after an exec_at_trig or exec_now --
562        
563        Parameters
564        ----------
565        src : int
566            set the trigger source:
567            * SPCM_DDS_TRG_SRC_NONE  = 0
568                no trigger source set, only exec_now changes what is output by the cores
569            * SPCM_DDS_TRG_SRC_TIMER = 1
570                an internal timer sends out triggers with a period defined by `trg_timer(period)`
571            * SPCM_DDS_TRG_SRC_CARD  = 2
572                use the trigger engine of the card (see the user manual for more information about setting up the trigger engine)
573        """
574
575        self.set_i(SPC_DDS_TRG_SRC, src)

setup the source of where the trigger is coming from (see register SPC_DDS_TRG_SRC in the manual)

NOTE

the trigger source is also set using the shadow register, hence only after an exec_at_trig or exec_now --

Parameters
  • src (int): set the trigger source:
    • SPCM_DDS_TRG_SRC_NONE = 0 no trigger source set, only exec_now changes what is output by the cores
    • SPCM_DDS_TRG_SRC_TIMER = 1 an internal timer sends out triggers with a period defined by trg_timer(period)
    • SPCM_DDS_TRG_SRC_CARD = 2 use the trigger engine of the card (see the user manual for more information about setting up the trigger engine)
def get_trg_src(self) -> int:
577    def get_trg_src(self) -> int:
578        """
579        get the source of where the trigger is coming from (see register `SPC_DDS_TRG_SRC` in the manual)
580
581        NOTE
582        ----
583        the trigger source is also set using the shadow register, hence only after an exec_at_trig or exec_now --
584        
585        Returns
586        ----------
587        int
588            get one of the trigger source:
589            * SPCM_DDS_TRG_SRC_NONE  = 0
590                no trigger source set, only exec_now changes what is output by the cores
591            * SPCM_DDS_TRG_SRC_TIMER = 1
592                an internal timer sends out triggers with a period defined by `trg_timer(period)`
593            * SPCM_DDS_TRG_SRC_CARD  = 2
594                use the trigger engine of the card (see the user manual for more information about setting up the trigger engine)
595        """
596
597        return self.card.get_i(SPC_DDS_TRG_SRC)

get the source of where the trigger is coming from (see register SPC_DDS_TRG_SRC in the manual)

NOTE

the trigger source is also set using the shadow register, hence only after an exec_at_trig or exec_now --

Returns
  • int: get one of the trigger source:
    • SPCM_DDS_TRG_SRC_NONE = 0 no trigger source set, only exec_now changes what is output by the cores
    • SPCM_DDS_TRG_SRC_TIMER = 1 an internal timer sends out triggers with a period defined by trg_timer(period)
    • SPCM_DDS_TRG_SRC_CARD = 2 use the trigger engine of the card (see the user manual for more information about setting up the trigger engine)
def trg_timer(self, period: float) -> None:
599    def trg_timer(self, period : float) -> None:
600        """
601        set the period at which the timer should raise DDS trigger events. (see register `SPC_DDS_TRG_TIMER` in the manual)
602
603        NOTE
604        ----
605        only used in conjecture with the trigger source set to SPCM_DDS_TRG_SRC_TIMER ---
606        
607        Parameters
608        ----------
609        period : float | pint.Quantity
610            the time between DDS trigger events in seconds
611        """
612
613        period = UnitConversion.convert(period, units.s, float, rounding=None)
614        self.set_d(SPC_DDS_TRG_TIMER, float(period))

set the period at which the timer should raise DDS trigger events. (see register SPC_DDS_TRG_TIMER in the manual)

NOTE

only used in conjecture with the trigger source set to SPCM_DDS_TRG_SRC_TIMER ---

Parameters
  • period (float | pint.Quantity): the time between DDS trigger events in seconds
def get_trg_timer(self, return_unit=None) -> float:
616    def get_trg_timer(self, return_unit = None) -> float:
617        """
618        get the period at which the timer should raise DDS trigger events. (see register `SPC_DDS_TRG_TIMER` in the manual)
619
620        NOTE
621        ----
622        only used in conjecture with the trigger source set to SPCM_DDS_TRG_SRC_TIMER ---
623
624        Parameters
625        ----------
626        return_unit : pint.Unit = None
627            the unit of the returned time between DDS trigger events, by default None
628        
629        Returns
630        ----------
631        float
632            the time between DDS trigger events in seconds
633        """
634
635        return_value = self.card.get_d(SPC_DDS_TRG_TIMER)
636        if return_unit is not None: return_value = UnitConversion.to_unit(return_value * units.s, return_unit)
637        return return_value

get the period at which the timer should raise DDS trigger events. (see register SPC_DDS_TRG_TIMER in the manual)

NOTE

only used in conjecture with the trigger source set to SPCM_DDS_TRG_SRC_TIMER ---

Parameters
  • return_unit (pint.Unit = None): the unit of the returned time between DDS trigger events, by default None
Returns
  • float: the time between DDS trigger events in seconds
def x_mode(self, xio: int, mode: int) -> None:
639    def x_mode(self, xio : int, mode : int) -> None:
640        """
641        setup the kind of output that the XIO outputs will give (see register `SPC_DDS_X0_MODE` in the manual)
642
643        Parameters
644        ----------
645        xio : int
646            the XIO channel number
647        mode : int
648            the mode that the channel needs to run in
649        """
650
651        self.set_i(SPC_DDS_X0_MODE + xio, mode)

setup the kind of output that the XIO outputs will give (see register SPC_DDS_X0_MODE in the manual)

Parameters
  • xio (int): the XIO channel number
  • mode (int): the mode that the channel needs to run in
def get_x_mode(self, xio: int) -> int:
653    def get_x_mode(self, xio : int) -> int:
654        """
655        get the kind of output that the XIO outputs will give (see register `SPC_DDS_X0_MODE` in the manual)
656
657        Parameters
658        ----------
659        xio : int
660            the XIO channel number
661
662        Returns
663        -------
664        int
665            the mode that the channel needs to run in
666            SPC_DDS_XIO_SEQUENCE = 0
667                turn on and off the XIO channels using commands in the DDS cmd queue
668            SPC_DDS_XIO_ARM = 1
669                when the DDS firmware is waiting for a trigger to come this signal is high
670            SPC_DDS_XIO_LATCH = 2
671                when the DDS firmware starts executing a change this signal is high
672        """
673
674        return self.card.get_i(SPC_DDS_X0_MODE + xio)

get the kind of output that the XIO outputs will give (see register SPC_DDS_X0_MODE in the manual)

Parameters
  • xio (int): the XIO channel number
Returns
  • int: the mode that the channel needs to run in SPC_DDS_XIO_SEQUENCE = 0 turn on and off the XIO channels using commands in the DDS cmd queue SPC_DDS_XIO_ARM = 1 when the DDS firmware is waiting for a trigger to come this signal is high SPC_DDS_XIO_LATCH = 2 when the DDS firmware starts executing a change this signal is high
def freq_ramp_stepsize(self, divider: int) -> None:
676    def freq_ramp_stepsize(self, divider : int) -> None:
677        """
678        number of timesteps before the frequency is changed during a frequency ramp. (see register `SPC_DDS_FREQ_RAMP_STEPSIZE` in the manual)
679
680        NOTES
681        -----
682        - this is a global setting for all cores
683        - internally the time divider is used to calculate the amount of change per event using a given frequency slope, please set the time divider before setting the frequency slope
684        
685        Parameters
686        ----------
687        divider : int
688            the number of DDS timesteps that a value is kept constant during a frequency ramp
689        """
690
691        self.set_i(SPC_DDS_FREQ_RAMP_STEPSIZE, int(divider))

number of timesteps before the frequency is changed during a frequency ramp. (see register SPC_DDS_FREQ_RAMP_STEPSIZE in the manual)

NOTES
  • this is a global setting for all cores
  • internally the time divider is used to calculate the amount of change per event using a given frequency slope, please set the time divider before setting the frequency slope
Parameters
  • divider (int): the number of DDS timesteps that a value is kept constant during a frequency ramp
def get_freq_ramp_stepsize(self) -> int:
693    def get_freq_ramp_stepsize(self) -> int:
694        """
695        get the number of timesteps before the frequency is changed during a frequency ramp. (see register `SPC_DDS_FREQ_RAMP_STEPSIZE` in the manual)
696        
697        NOTES
698        -----
699        - this is a global setting for all cores
700        - internally the time divider is used to calculate the amount of change per event using a given frequency slope, please set the time divider before setting the frequency slope
701        
702        Returns
703        ----------
704        divider : int
705            the number of DDS timesteps that a value is kept constant during a frequency ramp
706        """
707
708        return self.card.get_i(SPC_DDS_FREQ_RAMP_STEPSIZE)

get the number of timesteps before the frequency is changed during a frequency ramp. (see register SPC_DDS_FREQ_RAMP_STEPSIZE in the manual)

NOTES
  • this is a global setting for all cores
  • internally the time divider is used to calculate the amount of change per event using a given frequency slope, please set the time divider before setting the frequency slope
Returns
  • divider (int): the number of DDS timesteps that a value is kept constant during a frequency ramp
def amp_ramp_stepsize(self, divider: int) -> None:
710    def amp_ramp_stepsize(self, divider : int) -> None:
711        """
712        number of timesteps before the amplitude is changed during a frequency ramp. (see register `SPC_DDS_AMP_RAMP_STEPSIZE` in the manual)
713
714        NOTES
715        -----
716        - this is a global setting for all cores
717        - internally the time divider is used to calculate the amount of change per event using a given amplitude slope, 
718            please set the time divider before setting the amplitude slope
719        
720        Parameters
721        ----------
722        divider : int
723            the number of DDS timesteps that a value is kept constant during an amplitude ramp
724        """
725
726        self.set_i(SPC_DDS_AMP_RAMP_STEPSIZE, int(divider))

number of timesteps before the amplitude is changed during a frequency ramp. (see register SPC_DDS_AMP_RAMP_STEPSIZE in the manual)

NOTES
  • this is a global setting for all cores
  • internally the time divider is used to calculate the amount of change per event using a given amplitude slope, please set the time divider before setting the amplitude slope
Parameters
  • divider (int): the number of DDS timesteps that a value is kept constant during an amplitude ramp
def get_amp_ramp_stepsize(self) -> int:
728    def get_amp_ramp_stepsize(self) -> int:
729        """
730        get the number of timesteps before the amplitude is changed during a frequency ramp. (see register `SPC_DDS_AMP_RAMP_STEPSIZE` in the manual)
731
732        NOTES
733        -----
734        - this is a global setting for all cores
735        - internally the time divider is used to calculate the amount of change per event using a given amplitude slope, 
736            please set the time divider before setting the amplitude slope
737        
738        Returns
739        ----------
740        divider : int
741            the number of DDS timesteps that a value is kept constant during an amplitude ramp
742        """
743
744        return self.card.get_i(SPC_DDS_AMP_RAMP_STEPSIZE)

get the number of timesteps before the amplitude is changed during a frequency ramp. (see register SPC_DDS_AMP_RAMP_STEPSIZE in the manual)

NOTES
  • this is a global setting for all cores
  • internally the time divider is used to calculate the amount of change per event using a given amplitude slope, please set the time divider before setting the amplitude slope
Returns
  • divider (int): the number of DDS timesteps that a value is kept constant during an amplitude ramp
def amp(self, *args) -> None:
748    def amp(self, *args) -> None:
749        """
750        set the amplitude of the sine wave of a specific core (see register `SPC_DDS_CORE0_AMP` in the manual)
751        
752        Parameters
753        ----------
754        core_index : int (optional)
755            the index of the core to be changed
756        amplitude : float
757            the value between 0 and 1 corresponding to the amplitude
758        """
759
760        if len(args) == 1:
761            amplitude = args[0]
762            for core in self.cores:
763                core.amp(amplitude)
764        elif len(args) == 2:
765            core_index, amplitude = args
766            self.cores[core_index].amp(amplitude)
767        else:
768            raise TypeError("amp() takes 1 or 2 positional arguments ({} given)".format(len(args) + 1))
769        # self.set_d(SPC_DDS_CORE0_AMP + core_index, float(amplitude))

set the amplitude of the sine wave of a specific core (see register SPC_DDS_CORE0_AMP in the manual)

Parameters
  • core_index (int (optional)): the index of the core to be changed
  • amplitude (float): the value between 0 and 1 corresponding to the amplitude
def amplitude(self, *args) -> None:
748    def amp(self, *args) -> None:
749        """
750        set the amplitude of the sine wave of a specific core (see register `SPC_DDS_CORE0_AMP` in the manual)
751        
752        Parameters
753        ----------
754        core_index : int (optional)
755            the index of the core to be changed
756        amplitude : float
757            the value between 0 and 1 corresponding to the amplitude
758        """
759
760        if len(args) == 1:
761            amplitude = args[0]
762            for core in self.cores:
763                core.amp(amplitude)
764        elif len(args) == 2:
765            core_index, amplitude = args
766            self.cores[core_index].amp(amplitude)
767        else:
768            raise TypeError("amp() takes 1 or 2 positional arguments ({} given)".format(len(args) + 1))
769        # self.set_d(SPC_DDS_CORE0_AMP + core_index, float(amplitude))

set the amplitude of the sine wave of a specific core (see register SPC_DDS_CORE0_AMP in the manual)

Parameters
  • core_index (int (optional)): the index of the core to be changed
  • amplitude (float): the value between 0 and 1 corresponding to the amplitude
def get_amp(self, core_index: int, return_unit=None) -> float:
773    def get_amp(self, core_index : int, return_unit = None) -> float:
774        """
775        gets the amplitude of the sine wave of a specific core (see register `SPC_DDS_CORE0_AMP` in the manual)
776        
777        Parameters
778        ----------
779        core_index : int
780            the index of the core to be changed
781        return_unit : pint.Unit = None
782            the unit of the returned amplitude, by default None
783
784        Returns
785        -------
786        float | pint.Quantity
787            the value between 0 and 1 corresponding to the amplitude of the specific core or in the specified unit
788        """
789
790        return self.cores[core_index].get_amp(return_unit)
791        # return self.card.get_d(SPC_DDS_CORE0_AMP + core_index)

gets the amplitude of the sine wave of a specific core (see register SPC_DDS_CORE0_AMP in the manual)

Parameters
  • core_index (int): the index of the core to be changed
  • return_unit (pint.Unit = None): the unit of the returned amplitude, by default None
Returns
  • float | pint.Quantity: the value between 0 and 1 corresponding to the amplitude of the specific core or in the specified unit
def get_amplitude(self, core_index: int, return_unit=None) -> float:
773    def get_amp(self, core_index : int, return_unit = None) -> float:
774        """
775        gets the amplitude of the sine wave of a specific core (see register `SPC_DDS_CORE0_AMP` in the manual)
776        
777        Parameters
778        ----------
779        core_index : int
780            the index of the core to be changed
781        return_unit : pint.Unit = None
782            the unit of the returned amplitude, by default None
783
784        Returns
785        -------
786        float | pint.Quantity
787            the value between 0 and 1 corresponding to the amplitude of the specific core or in the specified unit
788        """
789
790        return self.cores[core_index].get_amp(return_unit)
791        # return self.card.get_d(SPC_DDS_CORE0_AMP + core_index)

gets the amplitude of the sine wave of a specific core (see register SPC_DDS_CORE0_AMP in the manual)

Parameters
  • core_index (int): the index of the core to be changed
  • return_unit (pint.Unit = None): the unit of the returned amplitude, by default None
Returns
  • float | pint.Quantity: the value between 0 and 1 corresponding to the amplitude of the specific core or in the specified unit
def avail_amp_min(self) -> float:
795    def avail_amp_min(self) -> float:
796        """
797        get the minimum available amplitude (see register `SPC_DDS_AVAIL_AMP_MIN` in the manual)
798
799        Returns
800        -------
801        float
802            the minimum available amplitude
803        
804        TODO: unitize!
805        """
806
807        return self.card.get_d(SPC_DDS_AVAIL_AMP_MIN)

get the minimum available amplitude (see register SPC_DDS_AVAIL_AMP_MIN in the manual)

Returns
  • float: the minimum available amplitude
  • TODO (unitize!):
def avail_amp_max(self) -> float:
809    def avail_amp_max(self) -> float:
810        """
811        get the maximum available amplitude (see register `SPC_DDS_AVAIL_AMP_MAX` in the manual)
812
813        Returns
814        -------
815        float
816            the maximum available amplitude
817        
818        TODO: unitize!
819        """
820
821        return self.card.get_d(SPC_DDS_AVAIL_AMP_MAX)

get the maximum available amplitude (see register SPC_DDS_AVAIL_AMP_MAX in the manual)

Returns
  • float: the maximum available amplitude
  • TODO (unitize!):
def avail_amp_step(self) -> float:
823    def avail_amp_step(self) -> float:
824        """
825        get the step size of the available amplitudes (see register `SPC_DDS_AVAIL_AMP_STEP` in the manual)
826
827        Returns
828        -------
829        float
830            the step size of the available amplitudes
831        
832        TODO: unitize!
833        """
834
835        return self.card.get_d(SPC_DDS_AVAIL_AMP_STEP)

get the step size of the available amplitudes (see register SPC_DDS_AVAIL_AMP_STEP in the manual)

Returns
  • float: the step size of the available amplitudes
  • TODO (unitize!):
def freq(self, *args) -> None:
838    def freq(self, *args) -> None:
839        """
840        set the frequency of the sine wave of a specific core (see register `SPC_DDS_CORE0_FREQ` in the manual)
841        
842        Parameters
843        ----------
844        core_index : int (optional)
845            the index of the core to be changed
846        frequency : float
847            the value of the frequency in Hz
848        """
849
850        if len(args) == 1:
851            frequency = args[0]
852            for core in self.cores:
853                core.freq(frequency)
854        elif len(args) == 2:
855            core_index, frequency = args
856            self.cores[core_index].freq(frequency)
857        else:
858            raise TypeError("freq() takes 1 or 2 positional arguments ({} given)".format(len(args) + 1))
859        # self.set_d(SPC_DDS_CORE0_FREQ + core_index, float(frequency))

set the frequency of the sine wave of a specific core (see register SPC_DDS_CORE0_FREQ in the manual)

Parameters
  • core_index (int (optional)): the index of the core to be changed
  • frequency (float): the value of the frequency in Hz
def frequency(self, *args) -> None:
838    def freq(self, *args) -> None:
839        """
840        set the frequency of the sine wave of a specific core (see register `SPC_DDS_CORE0_FREQ` in the manual)
841        
842        Parameters
843        ----------
844        core_index : int (optional)
845            the index of the core to be changed
846        frequency : float
847            the value of the frequency in Hz
848        """
849
850        if len(args) == 1:
851            frequency = args[0]
852            for core in self.cores:
853                core.freq(frequency)
854        elif len(args) == 2:
855            core_index, frequency = args
856            self.cores[core_index].freq(frequency)
857        else:
858            raise TypeError("freq() takes 1 or 2 positional arguments ({} given)".format(len(args) + 1))
859        # self.set_d(SPC_DDS_CORE0_FREQ + core_index, float(frequency))

set the frequency of the sine wave of a specific core (see register SPC_DDS_CORE0_FREQ in the manual)

Parameters
  • core_index (int (optional)): the index of the core to be changed
  • frequency (float): the value of the frequency in Hz
def get_freq(self, core_index: int, return_unit=None) -> float:
863    def get_freq(self, core_index : int, return_unit = None) -> float:
864        """
865        gets the frequency of the sine wave of a specific core (see register `SPC_DDS_CORE0_FREQ` in the manual)
866        
867        Parameters
868        ----------
869        core_index : int
870            the index of the core to be changed
871        return_unit : pint.Unit = None
872            the unit of the returned frequency, by default None
873        
874        Returns
875        -------
876        float | pint.Quantity
877            the value of the frequency in Hz the specific core or in the specified unit
878        """
879
880        return self.cores[core_index].get_freq(return_unit)

gets the frequency of the sine wave of a specific core (see register SPC_DDS_CORE0_FREQ in the manual)

Parameters
  • core_index (int): the index of the core to be changed
  • return_unit (pint.Unit = None): the unit of the returned frequency, by default None
Returns
  • float | pint.Quantity: the value of the frequency in Hz the specific core or in the specified unit
def get_frequency(self, core_index: int, return_unit=None) -> float:
863    def get_freq(self, core_index : int, return_unit = None) -> float:
864        """
865        gets the frequency of the sine wave of a specific core (see register `SPC_DDS_CORE0_FREQ` in the manual)
866        
867        Parameters
868        ----------
869        core_index : int
870            the index of the core to be changed
871        return_unit : pint.Unit = None
872            the unit of the returned frequency, by default None
873        
874        Returns
875        -------
876        float | pint.Quantity
877            the value of the frequency in Hz the specific core or in the specified unit
878        """
879
880        return self.cores[core_index].get_freq(return_unit)

gets the frequency of the sine wave of a specific core (see register SPC_DDS_CORE0_FREQ in the manual)

Parameters
  • core_index (int): the index of the core to be changed
  • return_unit (pint.Unit = None): the unit of the returned frequency, by default None
Returns
  • float | pint.Quantity: the value of the frequency in Hz the specific core or in the specified unit
def avail_freq_min(self) -> float:
884    def avail_freq_min(self) -> float:
885        """
886        get the minimum available frequency (see register `SPC_DDS_AVAIL_FREQ_MIN` in the manual)
887
888        Returns
889        -------
890        float
891            the minimum available frequency
892        
893        TODO: unitize!
894        """
895
896        return self.card.get_d(SPC_DDS_AVAIL_FREQ_MIN)

get the minimum available frequency (see register SPC_DDS_AVAIL_FREQ_MIN in the manual)

Returns
  • float: the minimum available frequency
  • TODO (unitize!):
def avail_freq_max(self) -> float:
898    def avail_freq_max(self) -> float:
899        """
900        get the maximum available frequency (see register `SPC_DDS_AVAIL_FREQ_MAX` in the manual)
901
902        Returns
903        -------
904        float
905            the maximum available frequency
906        
907        TODO: unitize!
908        """
909
910        return self.card.get_d(SPC_DDS_AVAIL_FREQ_MAX)

get the maximum available frequency (see register SPC_DDS_AVAIL_FREQ_MAX in the manual)

Returns
  • float: the maximum available frequency
  • TODO (unitize!):
def avail_freq_step(self) -> float:
912    def avail_freq_step(self) -> float:
913        """
914        get the step size of the available frequencies (see register `SPC_DDS_AVAIL_FREQ_STEP` in the manual)
915
916        Returns
917        -------
918        float
919            the step size of the available frequencies
920        
921        TODO: unitize!
922        """
923
924        return self.card.get_d(SPC_DDS_AVAIL_FREQ_STEP)

get the step size of the available frequencies (see register SPC_DDS_AVAIL_FREQ_STEP in the manual)

Returns
  • float: the step size of the available frequencies
  • TODO (unitize!):
def phase(self, *args) -> None:
927    def phase(self, *args) -> None:
928        """
929        set the phase of the sine wave of a specific core (see register `SPC_DDS_CORE0_PHASE` in the manual)
930        
931        Parameters
932        ----------
933        core_index : int (optional)
934            the index of the core to be changed
935        phase : float
936            the value between 0 and 360 degrees of the phase
937        """
938
939        if len(args) == 1:
940            phase = args[0]
941            for core in self.cores:
942                core.phase(phase)
943        elif len(args) == 2:
944            core_index, phase = args
945            self.cores[core_index].phase(phase)
946        else:
947            raise TypeError("phase() takes 1 or 2 positional arguments ({} given)".format(len(args) + 1))
948        # self.set_d(SPC_DDS_CORE0_PHASE + core_index, float(phase))

set the phase of the sine wave of a specific core (see register SPC_DDS_CORE0_PHASE in the manual)

Parameters
  • core_index (int (optional)): the index of the core to be changed
  • phase (float): the value between 0 and 360 degrees of the phase
def get_phase(self, core_index: int, return_unit=None) -> float:
950    def get_phase(self, core_index : int, return_unit = None) -> float:
951        """
952        gets the phase of the sine wave of a specific core (see register `SPC_DDS_CORE0_PHASE` in the manual)
953        
954        Parameters
955        ----------
956        core_index : int
957            the index of the core to be changed
958        return_unit : pint.Unit = None
959            the unit of the returned phase, by default None
960        
961        Returns
962        -------
963        float
964            the value between 0 and 360 degrees of the phase
965        """
966
967        return self.cores[core_index].get_phase(return_unit)

gets the phase of the sine wave of a specific core (see register SPC_DDS_CORE0_PHASE in the manual)

Parameters
  • core_index (int): the index of the core to be changed
  • return_unit (pint.Unit = None): the unit of the returned phase, by default None
Returns
  • float: the value between 0 and 360 degrees of the phase
def avail_phase_min(self) -> float:
969    def avail_phase_min(self) -> float:
970        """
971        get the minimum available phase (see register `SPC_DDS_AVAIL_PHASE_MIN` in the manual)
972
973        Returns
974        -------
975        float
976            the minimum available phase
977        
978        TODO: unitize!
979        """
980
981        return self.card.get_d(SPC_DDS_AVAIL_PHASE_MIN)

get the minimum available phase (see register SPC_DDS_AVAIL_PHASE_MIN in the manual)

Returns
  • float: the minimum available phase
  • TODO (unitize!):
def avail_phase_max(self) -> float:
983    def avail_phase_max(self) -> float:
984        """
985        get the maximum available phase (see register `SPC_DDS_AVAIL_PHASE_MAX` in the manual)
986
987        Returns
988        -------
989        float
990            the maximum available phase
991        
992        TODO: unitize!
993        """
994
995        return self.card.get_d(SPC_DDS_AVAIL_PHASE_MAX)

get the maximum available phase (see register SPC_DDS_AVAIL_PHASE_MAX in the manual)

Returns
  • float: the maximum available phase
  • TODO (unitize!):
def avail_phase_step(self) -> float:
 997    def avail_phase_step(self) -> float:
 998        """
 999        get the step size of the available phases (see register `SPC_DDS_AVAIL_PHASE_STEP` in the manual)
1000
1001        Returns
1002        -------
1003        float
1004            the step size of the available phases
1005        
1006        TODO: unitize!
1007        """
1008
1009        return self.card.get_d(SPC_DDS_AVAIL_PHASE_STEP)

get the step size of the available phases (see register SPC_DDS_AVAIL_PHASE_STEP in the manual)

Returns
  • float: the step size of the available phases
  • TODO (unitize!):
def x_manual_output(self, state_mask: int) -> None:
1011    def x_manual_output(self, state_mask : int) -> None:
1012        """
1013        set the output of the xio channels using a bit mask (see register `SPC_DDS_X_MANUAL_OUTPUT` in the manual)
1014        
1015        Parameters
1016        ----------
1017        state_mask : int
1018            bit mask where the bits correspond to specific channels and 1 to on and 0 to off.
1019        """
1020
1021        self.set_i(SPC_DDS_X_MANUAL_OUTPUT, state_mask)

set the output of the xio channels using a bit mask (see register SPC_DDS_X_MANUAL_OUTPUT in the manual)

Parameters
  • state_mask (int): bit mask where the bits correspond to specific channels and 1 to on and 0 to off.
def get_x_manual_output(self) -> int:
1023    def get_x_manual_output(self) -> int:
1024        """
1025        get the output of the xio channels using a bit mask (see register `SPC_DDS_X_MANUAL_OUTPUT` in the manual)
1026        
1027        Returns
1028        ----------
1029        int
1030            bit mask where the bits correspond to specific channels and 1 to on and 0 to off.
1031        """
1032
1033        return self.card.get_i(SPC_DDS_X_MANUAL_OUTPUT)

get the output of the xio channels using a bit mask (see register SPC_DDS_X_MANUAL_OUTPUT in the manual)

Returns
  • int: bit mask where the bits correspond to specific channels and 1 to on and 0 to off.
def freq_slope(self, *args) -> None:
1037    def freq_slope(self, *args) -> None:
1038        """
1039        set the frequency slope of the linearly changing frequency of the sine wave of a specific core (see register `SPC_DDS_CORE0_FREQ_SLOPE` in the manual)
1040        
1041        Parameters
1042        ----------
1043        core_index : int (optional)
1044            the index of the core to be changed
1045        slope : float
1046            the rate of frequency change in Hz/s
1047        """
1048
1049        if len(args) == 1:
1050            slope = args[0]
1051            for core in self.cores:
1052                core.freq_slope(slope)
1053        elif len(args) == 2:
1054            core_index, slope = args
1055            self.cores[core_index].freq_slope(slope)
1056        else:
1057            raise TypeError("freq_slope() takes 1 or 2 positional arguments ({} given)".format(len(args) + 1))
1058        # self.set_d(SPC_DDS_CORE0_FREQ_SLOPE + core_index, float(slope))

set the frequency slope of the linearly changing frequency of the sine wave of a specific core (see register SPC_DDS_CORE0_FREQ_SLOPE in the manual)

Parameters
  • core_index (int (optional)): the index of the core to be changed
  • slope (float): the rate of frequency change in Hz/s
def frequency_slope(self, *args) -> None:
1037    def freq_slope(self, *args) -> None:
1038        """
1039        set the frequency slope of the linearly changing frequency of the sine wave of a specific core (see register `SPC_DDS_CORE0_FREQ_SLOPE` in the manual)
1040        
1041        Parameters
1042        ----------
1043        core_index : int (optional)
1044            the index of the core to be changed
1045        slope : float
1046            the rate of frequency change in Hz/s
1047        """
1048
1049        if len(args) == 1:
1050            slope = args[0]
1051            for core in self.cores:
1052                core.freq_slope(slope)
1053        elif len(args) == 2:
1054            core_index, slope = args
1055            self.cores[core_index].freq_slope(slope)
1056        else:
1057            raise TypeError("freq_slope() takes 1 or 2 positional arguments ({} given)".format(len(args) + 1))
1058        # self.set_d(SPC_DDS_CORE0_FREQ_SLOPE + core_index, float(slope))

set the frequency slope of the linearly changing frequency of the sine wave of a specific core (see register SPC_DDS_CORE0_FREQ_SLOPE in the manual)

Parameters
  • core_index (int (optional)): the index of the core to be changed
  • slope (float): the rate of frequency change in Hz/s
def get_freq_slope(self, core_index: int, return_unit=None) -> float:
1062    def get_freq_slope(self, core_index : int, return_unit=None) -> float:
1063        """
1064        get the frequency slope of the linearly changing frequency of the sine wave of a specific core (see register `SPC_DDS_CORE0_FREQ_SLOPE` in the manual)
1065        
1066        Parameters
1067        ----------
1068        core_index : int
1069            the index of the core to be changed
1070        return_unit : pint.Unit = None
1071            the unit of the returned frequency slope, by default None
1072        
1073        Returns
1074        -------
1075        float
1076            the rate of frequency change in Hz/s
1077        """
1078
1079        return self.cores[core_index].get_freq_slope(return_unit)

get the frequency slope of the linearly changing frequency of the sine wave of a specific core (see register SPC_DDS_CORE0_FREQ_SLOPE in the manual)

Parameters
  • core_index (int): the index of the core to be changed
  • return_unit (pint.Unit = None): the unit of the returned frequency slope, by default None
Returns
  • float: the rate of frequency change in Hz/s
def get_frequency_slope(self, core_index: int, return_unit=None) -> float:
1062    def get_freq_slope(self, core_index : int, return_unit=None) -> float:
1063        """
1064        get the frequency slope of the linearly changing frequency of the sine wave of a specific core (see register `SPC_DDS_CORE0_FREQ_SLOPE` in the manual)
1065        
1066        Parameters
1067        ----------
1068        core_index : int
1069            the index of the core to be changed
1070        return_unit : pint.Unit = None
1071            the unit of the returned frequency slope, by default None
1072        
1073        Returns
1074        -------
1075        float
1076            the rate of frequency change in Hz/s
1077        """
1078
1079        return self.cores[core_index].get_freq_slope(return_unit)

get the frequency slope of the linearly changing frequency of the sine wave of a specific core (see register SPC_DDS_CORE0_FREQ_SLOPE in the manual)

Parameters
  • core_index (int): the index of the core to be changed
  • return_unit (pint.Unit = None): the unit of the returned frequency slope, by default None
Returns
  • float: the rate of frequency change in Hz/s
def avail_freq_slope_min(self) -> float:
1083    def avail_freq_slope_min(self) -> float:
1084        """
1085        get the minimum available frequency slope (see register `SPC_DDS_AVAIL_FREQ_SLOPE_MIN` in the manual)
1086
1087        Returns
1088        -------
1089        float
1090            the minimum available frequency slope
1091        
1092        TODO: unitize!
1093        """
1094
1095        return self.card.get_d(SPC_DDS_AVAIL_FREQ_SLOPE_MIN)

get the minimum available frequency slope (see register SPC_DDS_AVAIL_FREQ_SLOPE_MIN in the manual)

Returns
  • float: the minimum available frequency slope
  • TODO (unitize!):
def avail_freq_slope_max(self) -> float:
1097    def avail_freq_slope_max(self) -> float:
1098        """
1099        get the maximum available frequency slope (see register `SPC_DDS_AVAIL_FREQ_SLOPE_MAX` in the manual)
1100
1101        Returns
1102        -------
1103        float
1104            the maximum available frequency slope
1105        
1106        TODO: unitize!
1107        """
1108
1109        return self.card.get_d(SPC_DDS_AVAIL_FREQ_SLOPE_MAX)

get the maximum available frequency slope (see register SPC_DDS_AVAIL_FREQ_SLOPE_MAX in the manual)

Returns
  • float: the maximum available frequency slope
  • TODO (unitize!):
def avail_freq_slope_step(self) -> float:
1111    def avail_freq_slope_step(self) -> float:
1112        """
1113        get the step size of the available frequency slopes (see register `SPC_DDS_AVAIL_FREQ_SLOPE_STEP` in the manual)
1114
1115        Returns
1116        -------
1117        float
1118            the step size of the available frequency slopes
1119        
1120        TODO: unitize!
1121        """
1122
1123        return self.card.get_d(SPC_DDS_AVAIL_FREQ_SLOPE_STEP)

get the step size of the available frequency slopes (see register SPC_DDS_AVAIL_FREQ_SLOPE_STEP in the manual)

Returns
  • float: the step size of the available frequency slopes
  • TODO (unitize!):
def amp_slope(self, *args) -> None:
1126    def amp_slope(self, *args) -> None:
1127        """
1128        set the amplitude slope of the linearly changing amplitude of the sine wave of a specific core (see register `SPC_DDS_CORE0_AMP_SLOPE` in the manual)
1129        
1130        Parameters
1131        ----------
1132        core_index : int (optional)
1133            the index of the core to be changed
1134        slope : float
1135            the rate of amplitude change in 1/s
1136        """
1137
1138        if len(args) == 1:
1139            slope = args[0]
1140            for core in self.cores:
1141                core.amp_slope(slope)
1142        elif len(args) == 2:
1143            core_index, slope = args
1144            self.cores[core_index].amp_slope(slope)
1145        else:
1146            raise TypeError("amp_slope() takes 1 or 2 positional arguments ({} given)".format(len(args) + 1))
1147        # self.set_d(SPC_DDS_CORE0_AMP_SLOPE + core_index, float(slope))

set the amplitude slope of the linearly changing amplitude of the sine wave of a specific core (see register SPC_DDS_CORE0_AMP_SLOPE in the manual)

Parameters
  • core_index (int (optional)): the index of the core to be changed
  • slope (float): the rate of amplitude change in 1/s
def amplitude_slope(self, *args) -> None:
1126    def amp_slope(self, *args) -> None:
1127        """
1128        set the amplitude slope of the linearly changing amplitude of the sine wave of a specific core (see register `SPC_DDS_CORE0_AMP_SLOPE` in the manual)
1129        
1130        Parameters
1131        ----------
1132        core_index : int (optional)
1133            the index of the core to be changed
1134        slope : float
1135            the rate of amplitude change in 1/s
1136        """
1137
1138        if len(args) == 1:
1139            slope = args[0]
1140            for core in self.cores:
1141                core.amp_slope(slope)
1142        elif len(args) == 2:
1143            core_index, slope = args
1144            self.cores[core_index].amp_slope(slope)
1145        else:
1146            raise TypeError("amp_slope() takes 1 or 2 positional arguments ({} given)".format(len(args) + 1))
1147        # self.set_d(SPC_DDS_CORE0_AMP_SLOPE + core_index, float(slope))

set the amplitude slope of the linearly changing amplitude of the sine wave of a specific core (see register SPC_DDS_CORE0_AMP_SLOPE in the manual)

Parameters
  • core_index (int (optional)): the index of the core to be changed
  • slope (float): the rate of amplitude change in 1/s
def get_amp_slope(self, core_index: int, return_unit=None) -> float:
1151    def get_amp_slope(self, core_index : int, return_unit = None) -> float:
1152        """
1153        set the amplitude slope of the linearly changing amplitude of the sine wave of a specific core (see register `SPC_DDS_CORE0_AMP_SLOPE` in the manual)
1154        
1155        Parameters
1156        ----------
1157        core_index : int
1158            the index of the core to be changed
1159        return_unit : pint.Unit = None
1160            the unit of the returned amplitude slope, by default None
1161        
1162        Returns
1163        -------
1164        float
1165            the rate of amplitude change in 1/s
1166        """
1167
1168        return self.cores[core_index].get_amp_slope(return_unit)

set the amplitude slope of the linearly changing amplitude of the sine wave of a specific core (see register SPC_DDS_CORE0_AMP_SLOPE in the manual)

Parameters
  • core_index (int): the index of the core to be changed
  • return_unit (pint.Unit = None): the unit of the returned amplitude slope, by default None
Returns
  • float: the rate of amplitude change in 1/s
def avail_amp_slope_min(self) -> float:
1172    def avail_amp_slope_min(self) -> float:
1173        """
1174        get the minimum available amplitude slope (see register `SPC_DDS_AVAIL_AMP_SLOPE_MIN` in the manual)
1175
1176        Returns
1177        -------
1178        float
1179            the minimum available amplitude slope
1180        
1181        TODO: unitize!
1182        """
1183
1184        return self.card.get_d(SPC_DDS_AVAIL_AMP_SLOPE_MIN)

get the minimum available amplitude slope (see register SPC_DDS_AVAIL_AMP_SLOPE_MIN in the manual)

Returns
  • float: the minimum available amplitude slope
  • TODO (unitize!):
def avail_amp_slope_max(self) -> float:
1186    def avail_amp_slope_max(self) -> float:
1187        """
1188        get the maximum available amplitude slope (see register `SPC_DDS_AVAIL_AMP_SLOPE_MAX` in the manual)
1189
1190        Returns
1191        -------
1192        float
1193            the maximum available amplitude slope
1194        
1195        TODO: unitize!
1196        """
1197
1198        return self.card.get_d(SPC_DDS_AVAIL_AMP_SLOPE_MAX)

get the maximum available amplitude slope (see register SPC_DDS_AVAIL_AMP_SLOPE_MAX in the manual)

Returns
  • float: the maximum available amplitude slope
  • TODO (unitize!):
def avail_amp_slope_step(self) -> float:
1200    def avail_amp_slope_step(self) -> float:
1201        """
1202        get the step size of the available amplitude slopes (see register `SPC_DDS_AVAIL_AMP_SLOPE_STEP` in the manual)
1203
1204        Returns
1205        -------
1206        float
1207            the step size of the available amplitude slopes
1208        
1209        TODO: unitize!
1210        """
1211
1212        return self.card.get_d(SPC_DDS_AVAIL_AMP_SLOPE_STEP)

get the step size of the available amplitude slopes (see register SPC_DDS_AVAIL_AMP_SLOPE_STEP in the manual)

Returns
  • float: the step size of the available amplitude slopes
  • TODO (unitize!):
def cmd(self, command: int) -> None:
1215    def cmd(self, command : int) -> None:
1216        """
1217        execute a DDS specific control flow command (see register `SPC_DDS_CMD` in the manual)
1218        
1219        Parameters
1220        ----------
1221        command : int
1222            DDS specific command
1223        """
1224
1225        self.set_i(SPC_DDS_CMD, command)

execute a DDS specific control flow command (see register SPC_DDS_CMD in the manual)

Parameters
  • command (int): DDS specific command
def exec_at_trg(self) -> None:
1227    def exec_at_trg(self) -> None:
1228        """
1229        execute the commands in the shadow register at the next trigger event (see register `SPC_DDS_CMD` in the manual)
1230        """
1231        self.cmd(SPCM_DDS_CMD_EXEC_AT_TRG)

execute the commands in the shadow register at the next trigger event (see register SPC_DDS_CMD in the manual)

def arm(self) -> None:
1227    def exec_at_trg(self) -> None:
1228        """
1229        execute the commands in the shadow register at the next trigger event (see register `SPC_DDS_CMD` in the manual)
1230        """
1231        self.cmd(SPCM_DDS_CMD_EXEC_AT_TRG)

execute the commands in the shadow register at the next trigger event (see register SPC_DDS_CMD in the manual)

def wait_for_trg(self) -> None:
1227    def exec_at_trg(self) -> None:
1228        """
1229        execute the commands in the shadow register at the next trigger event (see register `SPC_DDS_CMD` in the manual)
1230        """
1231        self.cmd(SPCM_DDS_CMD_EXEC_AT_TRG)

execute the commands in the shadow register at the next trigger event (see register SPC_DDS_CMD in the manual)

def exec_now(self) -> None:
1236    def exec_now(self) -> None:
1237        """
1238        execute the commands in the shadow register as soon as possible (see register `SPC_DDS_CMD` in the manual)
1239        """
1240
1241        self.cmd(SPCM_DDS_CMD_EXEC_NOW)

execute the commands in the shadow register as soon as possible (see register SPC_DDS_CMD in the manual)

def direct_latch(self) -> None:
1236    def exec_now(self) -> None:
1237        """
1238        execute the commands in the shadow register as soon as possible (see register `SPC_DDS_CMD` in the manual)
1239        """
1240
1241        self.cmd(SPCM_DDS_CMD_EXEC_NOW)

execute the commands in the shadow register as soon as possible (see register SPC_DDS_CMD in the manual)

def trg_count(self) -> int:
1245    def trg_count(self) -> int:
1246        """
1247        get the number of trigger exec_at_trg and exec_now command that have been executed (see register `SPC_DDS_TRG_COUNT` in the manual)
1248
1249        Returns
1250        -------
1251        int
1252            the number of trigger exec_at_trg and exec_now command that have been executed
1253        """
1254
1255        return self.card.get_i(SPC_DDS_TRG_COUNT)

get the number of trigger exec_at_trg and exec_now command that have been executed (see register SPC_DDS_TRG_COUNT in the manual)

Returns
  • int: the number of trigger exec_at_trg and exec_now command that have been executed
def write_to_card(self, flags=0) -> None:
1257    def write_to_card(self, flags=0) -> None:
1258        """
1259        send a list of all the commands that came after the last write_list and send them to the card (see register `SPC_DDS_CMD` in the manual)
1260        """
1261        
1262        self.cmd(SPCM_DDS_CMD_WRITE_TO_CARD | flags)

send a list of all the commands that came after the last write_list and send them to the card (see register SPC_DDS_CMD in the manual)

def kwargs2mask(self, kwargs: dict[str, bool], prefix: str = '') -> int:
1265    def kwargs2mask(self, kwargs : dict[str, bool], prefix : str = "") -> int:
1266        """
1267        DDS helper: transform a dictionary with keys with a specific prefix to a bitmask
1268
1269        Parameters
1270        ----------
1271        kwargs : dict
1272            dictonary with keys with a specific prefix and values given by bools
1273        prefix : str
1274            a prefix for the key names
1275        
1276        Returns
1277        -------
1278        int
1279            bit mask
1280        
1281        Example
1282        -------
1283        ['core_0' = True, 'core_2' = False, 'core_3' = True] => 0b1001 = 9
1284        """
1285        
1286        mask = 0
1287        for keyword, value in kwargs.items():
1288            bit = int(keyword[len(prefix)+1:])
1289            if value:
1290                mask |= 1 << bit
1291            else:
1292                mask &= ~(1 << bit)
1293        return mask

DDS helper: transform a dictionary with keys with a specific prefix to a bitmask

Parameters
  • kwargs (dict): dictonary with keys with a specific prefix and values given by bools
  • prefix (str): a prefix for the key names
Returns
  • int: bit mask
Example

['core_0' = True, 'core_2' = False, 'core_3' = True] => 0b1001 = 9

def k2m(self, kwargs: dict[str, bool], prefix: str = '') -> int:
1265    def kwargs2mask(self, kwargs : dict[str, bool], prefix : str = "") -> int:
1266        """
1267        DDS helper: transform a dictionary with keys with a specific prefix to a bitmask
1268
1269        Parameters
1270        ----------
1271        kwargs : dict
1272            dictonary with keys with a specific prefix and values given by bools
1273        prefix : str
1274            a prefix for the key names
1275        
1276        Returns
1277        -------
1278        int
1279            bit mask
1280        
1281        Example
1282        -------
1283        ['core_0' = True, 'core_2' = False, 'core_3' = True] => 0b1001 = 9
1284        """
1285        
1286        mask = 0
1287        for keyword, value in kwargs.items():
1288            bit = int(keyword[len(prefix)+1:])
1289            if value:
1290                mask |= 1 << bit
1291            else:
1292                mask &= ~(1 << bit)
1293        return mask

DDS helper: transform a dictionary with keys with a specific prefix to a bitmask

Parameters
  • kwargs (dict): dictonary with keys with a specific prefix and values given by bools
  • prefix (str): a prefix for the key names
Returns
  • int: bit mask
Example

['core_0' = True, 'core_2' = False, 'core_3' = True] => 0b1001 = 9

class DDSCore:
 13class DDSCore:
 14    """
 15    a class for controlling a single DDS core
 16    """
 17
 18    dds : "DDS"
 19    index : int
 20    channel : Channel
 21
 22    def __init__(self, core_index, dds, *args, **kwargs) -> None:
 23        self.dds = dds
 24        self.index = core_index
 25        self.channel = kwargs.get("channel", None)
 26    
 27    def __int__(self) -> int:
 28        """
 29        get the index of the core
 30
 31        Returns
 32        -------
 33        int
 34            the index of the core
 35        """
 36        return self.index
 37    __index__ = __int__
 38
 39    def __str__(self) -> str:
 40        """
 41        get the string representation of the core
 42
 43        Returns
 44        -------
 45        str
 46            the string representation of the core
 47        """
 48        return f"Core {self.index}"
 49    __repr__ = __str__
 50
 51    def __add__(self, other) -> int:
 52        """
 53        add the index of the core to another index
 54
 55        Parameters
 56        ----------
 57        other : int
 58            the other index
 59
 60        Returns
 61        -------
 62        int
 63            the sum of the two indices
 64        """
 65        return self.index + other
 66
 67    # DDS "static" parameters
 68    def amp(self, amplitude : float) -> None:
 69        """
 70        set the amplitude of the sine wave of a specific core (see register `SPC_DDS_CORE0_AMP` in the manual)
 71        
 72        Parameters
 73        ----------
 74        amplitude : float | pint.Quantity
 75            the value between 0 and 1 corresponding to the amplitude
 76        """
 77
 78        if self.channel is not None:
 79            amplitude = self.channel.to_amplitude_fraction(amplitude)
 80        elif isinstance(amplitude, units.Quantity) and amplitude.check("[]"):
 81            amplitude = UnitConversion.convert(amplitude, units.fraction, float, rounding=None)
 82        self.dds.set_d(SPC_DDS_CORE0_AMP + self.index, float(amplitude))
 83    # aliases
 84    amplitude = amp
 85
 86    def get_amp(self, return_unit = None) -> float:
 87        """
 88        gets the amplitude of the sine wave of a specific core (see register `SPC_DDS_CORE0_AMP` in the manual)
 89
 90        Parameters
 91        ----------
 92        return_unit : pint.Unit = None
 93            the unit of the returned amplitude, by default None
 94
 95        Returns
 96        -------
 97        float
 98            the value between 0 and 1 corresponding to the amplitude
 99        """
100
101        return_value = self.dds.card.get_d(SPC_DDS_CORE0_AMP + self.index)
102        if self.channel is not None:
103            return_value = self.channel.from_amplitude_fraction(return_value, return_unit)
104        else:
105            return_value = UnitConversion.to_unit(return_value, return_unit)
106        return return_value
107    # aliases
108    get_amplitude = get_amp
109
110    def freq(self, frequency : float) -> None:
111        """
112        set the frequency of the sine wave of a specific core (see register `SPC_DDS_CORE0_FREQ` in the manual)
113        
114        Parameters
115        ----------
116        frequency : float | pint.Quantity
117            the value of the frequency in Hz
118        """
119
120        frequency = UnitConversion.convert(frequency, units.Hz, float, rounding=None)
121        self.dds.set_d(SPC_DDS_CORE0_FREQ + self.index, float(frequency))
122    # aliases
123    frequency = freq
124
125    def get_freq(self, return_unit = None) -> float:
126        """
127        gets the frequency of the sine wave of a specific core (see register `SPC_DDS_CORE0_FREQ` in the manual)
128        
129        Parameters
130        ----------
131        return_unit : pint.Unit = None
132            the unit of the returned frequency, by default None
133
134        Returns
135        -------
136        float | pint.Quantity
137            the value of the frequency in Hz the specific core
138        """
139
140        return_value = self.dds.card.get_d(SPC_DDS_CORE0_FREQ + self.index)
141        if return_unit is not None: return_value = UnitConversion.to_unit(return_value * units.Hz, return_unit)
142        return return_value
143    # aliases
144    get_frequency = get_freq
145
146    def phase(self, phase : float) -> None:
147        """
148        set the phase of the sine wave of a specific core (see register `SPC_DDS_CORE0_PHASE` in the manual)
149        
150        Parameters
151        ----------
152        phase : float | pint.Quantity
153            the value between 0 and 360 degrees of the phase
154        """
155
156        phase = UnitConversion.convert(phase, units.deg, float, rounding=None)
157        self.dds.set_d(SPC_DDS_CORE0_PHASE + self.index, float(phase))
158
159    def get_phase(self, return_unit = None) -> float:
160        """
161        gets the phase of the sine wave of a specific core (see register `SPC_DDS_CORE0_PHASE` in the manual)
162        
163        Returns
164        -------
165        float
166            the value between 0 and 360 degrees of the phase
167        """
168
169        return_value = self.dds.card.get_d(SPC_DDS_CORE0_PHASE + self.index)
170        if return_unit is not None: return_value = UnitConversion.to_unit(return_value * units.deg, return_unit)
171        return return_value
172
173    # DDS dynamic parameters
174    def freq_slope(self, slope : float) -> None:
175        """
176        set the frequency slope of the linearly changing frequency of the sine wave of a specific core (see register `SPC_DDS_CORE0_FREQ_SLOPE` in the manual)
177        
178        Parameters
179        ----------
180        slope : float | pint.Quantity
181            the rate of frequency change in Hz/s (positive or negative) or specified unit
182        """
183
184        slope = UnitConversion.convert(slope, units.Hz/units.s, float, rounding=None)
185        self.dds.set_d(SPC_DDS_CORE0_FREQ_SLOPE + self.index, float(slope))
186    # aliases
187    frequency_slope = freq_slope
188
189    def get_freq_slope(self, return_unit = None) -> float:
190        """
191        get the frequency slope of the linearly changing frequency of the sine wave of a specific core (see register `SPC_DDS_CORE0_FREQ_SLOPE` in the manual)
192        
193        Parameters
194        ----------
195        return_unit : pint.Unit = None
196            the unit of the returned frequency slope, by default None
197
198        Returns
199        -------
200        float
201            the rate of frequency change in Hz/s
202        """
203
204        return_value = self.dds.card.get_d(SPC_DDS_CORE0_FREQ_SLOPE + self.index)
205        if return_unit is not None: return_value = UnitConversion.to_unit(return_value * units.Hz/units.s, return_unit)
206        return return_value
207    # aliases
208    get_frequency_slope = get_freq_slope
209
210    def amp_slope(self, slope : float) -> None:
211        """
212        set the amplitude slope of the linearly changing amplitude of the sine wave of a specific core (see register `SPC_DDS_CORE0_AMP_SLOPE` in the manual)
213        
214        Parameters
215        ----------
216        slope : float | pint.Quantity
217            the rate of amplitude change in 1/s (positive or negative) or specified unit
218        """
219
220        slope = UnitConversion.convert(slope, 1/units.s, float, rounding=None)
221        self.dds.set_d(SPC_DDS_CORE0_AMP_SLOPE + self.index, float(slope))
222    # aliases
223    amplitude_slope = amp_slope
224
225    def get_amp_slope(self, return_unit = None) -> float:
226        """
227        set the amplitude slope of the linearly changing amplitude of the sine wave of a specific core (see register `SPC_DDS_CORE0_AMP_SLOPE` in the manual)
228        
229        Parameters
230        ----------
231        return_unit : pint.Unit = None
232            the unit of the returned amplitude slope, by default None
233
234        Returns
235        -------
236        float
237            the rate of amplitude change in 1/s
238        """
239
240
241        return_value = self.dds.card.get_d(SPC_DDS_CORE0_AMP_SLOPE + self.index)
242        if return_unit is not None: return_value = UnitConversion.to_unit(return_value / units.s, return_unit)
243        return return_value
244    # aliases
245    amplitude_slope = amp_slope

a class for controlling a single DDS core

DDSCore(core_index, dds, *args, **kwargs)
22    def __init__(self, core_index, dds, *args, **kwargs) -> None:
23        self.dds = dds
24        self.index = core_index
25        self.channel = kwargs.get("channel", None)
dds: DDS
index: int
channel: Channel
def amp(self, amplitude: float) -> None:
68    def amp(self, amplitude : float) -> None:
69        """
70        set the amplitude of the sine wave of a specific core (see register `SPC_DDS_CORE0_AMP` in the manual)
71        
72        Parameters
73        ----------
74        amplitude : float | pint.Quantity
75            the value between 0 and 1 corresponding to the amplitude
76        """
77
78        if self.channel is not None:
79            amplitude = self.channel.to_amplitude_fraction(amplitude)
80        elif isinstance(amplitude, units.Quantity) and amplitude.check("[]"):
81            amplitude = UnitConversion.convert(amplitude, units.fraction, float, rounding=None)
82        self.dds.set_d(SPC_DDS_CORE0_AMP + self.index, float(amplitude))

set the amplitude of the sine wave of a specific core (see register SPC_DDS_CORE0_AMP in the manual)

Parameters
  • amplitude (float | pint.Quantity): the value between 0 and 1 corresponding to the amplitude
def amplitude(self, amplitude: float) -> None:
68    def amp(self, amplitude : float) -> None:
69        """
70        set the amplitude of the sine wave of a specific core (see register `SPC_DDS_CORE0_AMP` in the manual)
71        
72        Parameters
73        ----------
74        amplitude : float | pint.Quantity
75            the value between 0 and 1 corresponding to the amplitude
76        """
77
78        if self.channel is not None:
79            amplitude = self.channel.to_amplitude_fraction(amplitude)
80        elif isinstance(amplitude, units.Quantity) and amplitude.check("[]"):
81            amplitude = UnitConversion.convert(amplitude, units.fraction, float, rounding=None)
82        self.dds.set_d(SPC_DDS_CORE0_AMP + self.index, float(amplitude))

set the amplitude of the sine wave of a specific core (see register SPC_DDS_CORE0_AMP in the manual)

Parameters
  • amplitude (float | pint.Quantity): the value between 0 and 1 corresponding to the amplitude
def get_amp(self, return_unit=None) -> float:
 86    def get_amp(self, return_unit = None) -> float:
 87        """
 88        gets the amplitude of the sine wave of a specific core (see register `SPC_DDS_CORE0_AMP` in the manual)
 89
 90        Parameters
 91        ----------
 92        return_unit : pint.Unit = None
 93            the unit of the returned amplitude, by default None
 94
 95        Returns
 96        -------
 97        float
 98            the value between 0 and 1 corresponding to the amplitude
 99        """
100
101        return_value = self.dds.card.get_d(SPC_DDS_CORE0_AMP + self.index)
102        if self.channel is not None:
103            return_value = self.channel.from_amplitude_fraction(return_value, return_unit)
104        else:
105            return_value = UnitConversion.to_unit(return_value, return_unit)
106        return return_value

gets the amplitude of the sine wave of a specific core (see register SPC_DDS_CORE0_AMP in the manual)

Parameters
  • return_unit (pint.Unit = None): the unit of the returned amplitude, by default None
Returns
  • float: the value between 0 and 1 corresponding to the amplitude
def get_amplitude(self, return_unit=None) -> float:
 86    def get_amp(self, return_unit = None) -> float:
 87        """
 88        gets the amplitude of the sine wave of a specific core (see register `SPC_DDS_CORE0_AMP` in the manual)
 89
 90        Parameters
 91        ----------
 92        return_unit : pint.Unit = None
 93            the unit of the returned amplitude, by default None
 94
 95        Returns
 96        -------
 97        float
 98            the value between 0 and 1 corresponding to the amplitude
 99        """
100
101        return_value = self.dds.card.get_d(SPC_DDS_CORE0_AMP + self.index)
102        if self.channel is not None:
103            return_value = self.channel.from_amplitude_fraction(return_value, return_unit)
104        else:
105            return_value = UnitConversion.to_unit(return_value, return_unit)
106        return return_value

gets the amplitude of the sine wave of a specific core (see register SPC_DDS_CORE0_AMP in the manual)

Parameters
  • return_unit (pint.Unit = None): the unit of the returned amplitude, by default None
Returns
  • float: the value between 0 and 1 corresponding to the amplitude
def freq(self, frequency: float) -> None:
110    def freq(self, frequency : float) -> None:
111        """
112        set the frequency of the sine wave of a specific core (see register `SPC_DDS_CORE0_FREQ` in the manual)
113        
114        Parameters
115        ----------
116        frequency : float | pint.Quantity
117            the value of the frequency in Hz
118        """
119
120        frequency = UnitConversion.convert(frequency, units.Hz, float, rounding=None)
121        self.dds.set_d(SPC_DDS_CORE0_FREQ + self.index, float(frequency))

set the frequency of the sine wave of a specific core (see register SPC_DDS_CORE0_FREQ in the manual)

Parameters
  • frequency (float | pint.Quantity): the value of the frequency in Hz
def frequency(self, frequency: float) -> None:
110    def freq(self, frequency : float) -> None:
111        """
112        set the frequency of the sine wave of a specific core (see register `SPC_DDS_CORE0_FREQ` in the manual)
113        
114        Parameters
115        ----------
116        frequency : float | pint.Quantity
117            the value of the frequency in Hz
118        """
119
120        frequency = UnitConversion.convert(frequency, units.Hz, float, rounding=None)
121        self.dds.set_d(SPC_DDS_CORE0_FREQ + self.index, float(frequency))

set the frequency of the sine wave of a specific core (see register SPC_DDS_CORE0_FREQ in the manual)

Parameters
  • frequency (float | pint.Quantity): the value of the frequency in Hz
def get_freq(self, return_unit=None) -> float:
125    def get_freq(self, return_unit = None) -> float:
126        """
127        gets the frequency of the sine wave of a specific core (see register `SPC_DDS_CORE0_FREQ` in the manual)
128        
129        Parameters
130        ----------
131        return_unit : pint.Unit = None
132            the unit of the returned frequency, by default None
133
134        Returns
135        -------
136        float | pint.Quantity
137            the value of the frequency in Hz the specific core
138        """
139
140        return_value = self.dds.card.get_d(SPC_DDS_CORE0_FREQ + self.index)
141        if return_unit is not None: return_value = UnitConversion.to_unit(return_value * units.Hz, return_unit)
142        return return_value

gets the frequency of the sine wave of a specific core (see register SPC_DDS_CORE0_FREQ in the manual)

Parameters
  • return_unit (pint.Unit = None): the unit of the returned frequency, by default None
Returns
  • float | pint.Quantity: the value of the frequency in Hz the specific core
def get_frequency(self, return_unit=None) -> float:
125    def get_freq(self, return_unit = None) -> float:
126        """
127        gets the frequency of the sine wave of a specific core (see register `SPC_DDS_CORE0_FREQ` in the manual)
128        
129        Parameters
130        ----------
131        return_unit : pint.Unit = None
132            the unit of the returned frequency, by default None
133
134        Returns
135        -------
136        float | pint.Quantity
137            the value of the frequency in Hz the specific core
138        """
139
140        return_value = self.dds.card.get_d(SPC_DDS_CORE0_FREQ + self.index)
141        if return_unit is not None: return_value = UnitConversion.to_unit(return_value * units.Hz, return_unit)
142        return return_value

gets the frequency of the sine wave of a specific core (see register SPC_DDS_CORE0_FREQ in the manual)

Parameters
  • return_unit (pint.Unit = None): the unit of the returned frequency, by default None
Returns
  • float | pint.Quantity: the value of the frequency in Hz the specific core
def phase(self, phase: float) -> None:
146    def phase(self, phase : float) -> None:
147        """
148        set the phase of the sine wave of a specific core (see register `SPC_DDS_CORE0_PHASE` in the manual)
149        
150        Parameters
151        ----------
152        phase : float | pint.Quantity
153            the value between 0 and 360 degrees of the phase
154        """
155
156        phase = UnitConversion.convert(phase, units.deg, float, rounding=None)
157        self.dds.set_d(SPC_DDS_CORE0_PHASE + self.index, float(phase))

set the phase of the sine wave of a specific core (see register SPC_DDS_CORE0_PHASE in the manual)

Parameters
  • phase (float | pint.Quantity): the value between 0 and 360 degrees of the phase
def get_phase(self, return_unit=None) -> float:
159    def get_phase(self, return_unit = None) -> float:
160        """
161        gets the phase of the sine wave of a specific core (see register `SPC_DDS_CORE0_PHASE` in the manual)
162        
163        Returns
164        -------
165        float
166            the value between 0 and 360 degrees of the phase
167        """
168
169        return_value = self.dds.card.get_d(SPC_DDS_CORE0_PHASE + self.index)
170        if return_unit is not None: return_value = UnitConversion.to_unit(return_value * units.deg, return_unit)
171        return return_value

gets the phase of the sine wave of a specific core (see register SPC_DDS_CORE0_PHASE in the manual)

Returns
  • float: the value between 0 and 360 degrees of the phase
def freq_slope(self, slope: float) -> None:
174    def freq_slope(self, slope : float) -> None:
175        """
176        set the frequency slope of the linearly changing frequency of the sine wave of a specific core (see register `SPC_DDS_CORE0_FREQ_SLOPE` in the manual)
177        
178        Parameters
179        ----------
180        slope : float | pint.Quantity
181            the rate of frequency change in Hz/s (positive or negative) or specified unit
182        """
183
184        slope = UnitConversion.convert(slope, units.Hz/units.s, float, rounding=None)
185        self.dds.set_d(SPC_DDS_CORE0_FREQ_SLOPE + self.index, float(slope))

set the frequency slope of the linearly changing frequency of the sine wave of a specific core (see register SPC_DDS_CORE0_FREQ_SLOPE in the manual)

Parameters
  • slope (float | pint.Quantity): the rate of frequency change in Hz/s (positive or negative) or specified unit
def frequency_slope(self, slope: float) -> None:
174    def freq_slope(self, slope : float) -> None:
175        """
176        set the frequency slope of the linearly changing frequency of the sine wave of a specific core (see register `SPC_DDS_CORE0_FREQ_SLOPE` in the manual)
177        
178        Parameters
179        ----------
180        slope : float | pint.Quantity
181            the rate of frequency change in Hz/s (positive or negative) or specified unit
182        """
183
184        slope = UnitConversion.convert(slope, units.Hz/units.s, float, rounding=None)
185        self.dds.set_d(SPC_DDS_CORE0_FREQ_SLOPE + self.index, float(slope))

set the frequency slope of the linearly changing frequency of the sine wave of a specific core (see register SPC_DDS_CORE0_FREQ_SLOPE in the manual)

Parameters
  • slope (float | pint.Quantity): the rate of frequency change in Hz/s (positive or negative) or specified unit
def get_freq_slope(self, return_unit=None) -> float:
189    def get_freq_slope(self, return_unit = None) -> float:
190        """
191        get the frequency slope of the linearly changing frequency of the sine wave of a specific core (see register `SPC_DDS_CORE0_FREQ_SLOPE` in the manual)
192        
193        Parameters
194        ----------
195        return_unit : pint.Unit = None
196            the unit of the returned frequency slope, by default None
197
198        Returns
199        -------
200        float
201            the rate of frequency change in Hz/s
202        """
203
204        return_value = self.dds.card.get_d(SPC_DDS_CORE0_FREQ_SLOPE + self.index)
205        if return_unit is not None: return_value = UnitConversion.to_unit(return_value * units.Hz/units.s, return_unit)
206        return return_value

get the frequency slope of the linearly changing frequency of the sine wave of a specific core (see register SPC_DDS_CORE0_FREQ_SLOPE in the manual)

Parameters
  • return_unit (pint.Unit = None): the unit of the returned frequency slope, by default None
Returns
  • float: the rate of frequency change in Hz/s
def get_frequency_slope(self, return_unit=None) -> float:
189    def get_freq_slope(self, return_unit = None) -> float:
190        """
191        get the frequency slope of the linearly changing frequency of the sine wave of a specific core (see register `SPC_DDS_CORE0_FREQ_SLOPE` in the manual)
192        
193        Parameters
194        ----------
195        return_unit : pint.Unit = None
196            the unit of the returned frequency slope, by default None
197
198        Returns
199        -------
200        float
201            the rate of frequency change in Hz/s
202        """
203
204        return_value = self.dds.card.get_d(SPC_DDS_CORE0_FREQ_SLOPE + self.index)
205        if return_unit is not None: return_value = UnitConversion.to_unit(return_value * units.Hz/units.s, return_unit)
206        return return_value

get the frequency slope of the linearly changing frequency of the sine wave of a specific core (see register SPC_DDS_CORE0_FREQ_SLOPE in the manual)

Parameters
  • return_unit (pint.Unit = None): the unit of the returned frequency slope, by default None
Returns
  • float: the rate of frequency change in Hz/s
def amp_slope(self, slope: float) -> None:
210    def amp_slope(self, slope : float) -> None:
211        """
212        set the amplitude slope of the linearly changing amplitude of the sine wave of a specific core (see register `SPC_DDS_CORE0_AMP_SLOPE` in the manual)
213        
214        Parameters
215        ----------
216        slope : float | pint.Quantity
217            the rate of amplitude change in 1/s (positive or negative) or specified unit
218        """
219
220        slope = UnitConversion.convert(slope, 1/units.s, float, rounding=None)
221        self.dds.set_d(SPC_DDS_CORE0_AMP_SLOPE + self.index, float(slope))

set the amplitude slope of the linearly changing amplitude of the sine wave of a specific core (see register SPC_DDS_CORE0_AMP_SLOPE in the manual)

Parameters
  • slope (float | pint.Quantity): the rate of amplitude change in 1/s (positive or negative) or specified unit
def amplitude_slope(self, slope: float) -> None:
210    def amp_slope(self, slope : float) -> None:
211        """
212        set the amplitude slope of the linearly changing amplitude of the sine wave of a specific core (see register `SPC_DDS_CORE0_AMP_SLOPE` in the manual)
213        
214        Parameters
215        ----------
216        slope : float | pint.Quantity
217            the rate of amplitude change in 1/s (positive or negative) or specified unit
218        """
219
220        slope = UnitConversion.convert(slope, 1/units.s, float, rounding=None)
221        self.dds.set_d(SPC_DDS_CORE0_AMP_SLOPE + self.index, float(slope))

set the amplitude slope of the linearly changing amplitude of the sine wave of a specific core (see register SPC_DDS_CORE0_AMP_SLOPE in the manual)

Parameters
  • slope (float | pint.Quantity): the rate of amplitude change in 1/s (positive or negative) or specified unit
def get_amp_slope(self, return_unit=None) -> float:
225    def get_amp_slope(self, return_unit = None) -> float:
226        """
227        set the amplitude slope of the linearly changing amplitude of the sine wave of a specific core (see register `SPC_DDS_CORE0_AMP_SLOPE` in the manual)
228        
229        Parameters
230        ----------
231        return_unit : pint.Unit = None
232            the unit of the returned amplitude slope, by default None
233
234        Returns
235        -------
236        float
237            the rate of amplitude change in 1/s
238        """
239
240
241        return_value = self.dds.card.get_d(SPC_DDS_CORE0_AMP_SLOPE + self.index)
242        if return_unit is not None: return_value = UnitConversion.to_unit(return_value / units.s, return_unit)
243        return return_value

set the amplitude slope of the linearly changing amplitude of the sine wave of a specific core (see register SPC_DDS_CORE0_AMP_SLOPE in the manual)

Parameters
  • return_unit (pint.Unit = None): the unit of the returned amplitude slope, by default None
Returns
  • float: the rate of amplitude change in 1/s
class DDSCommandList(spcm.DDS):
 13class DDSCommandList(DDS):
 14    """Abstraction of the set_ptr and register `SPC_REGISTER_LIST` for command streaming in the DDS functionality"""
 15
 16    class WRITE_MODE(IntEnum):
 17        NO_CHECK = 0
 18        EXCEPTION_IF_FULL = 1
 19        WAIT_IF_FULL = 2
 20
 21    mode : int = WRITE_MODE.NO_CHECK
 22
 23    command_list : ctypes._Pointer = None
 24    commands_transfered : int = 0
 25    current_index : int = 0
 26    
 27    _dtm : int = SPCM_DDS_DTM_SINGLE
 28    _list_size : int = KIBI(16)
 29
 30    def __init__(self, *args, **kwargs) -> None:
 31        super().__init__(*args, **kwargs)
 32
 33        self.command_list = None
 34        self.current_index = 0
 35        
 36        self._dtm = SPCM_DDS_DTM_SINGLE
 37        
 38        self.list_size = self.default_size()
 39
 40    def data_transfer_mode(self, mode : int) -> None:
 41        """
 42        set the data transfer mode of the DDS
 43
 44        Parameters
 45        ----------
 46        mode : int
 47            the data transfer mode
 48        """
 49
 50        self._dtm = mode
 51        self.card.set_i(SPC_DDS_DATA_TRANSFER_MODE, mode)
 52        self.card.set_i(SPC_DDS_CMD, SPCM_DDS_CMD_WRITE_TO_CARD)
 53        self.list_size = self.default_size()
 54    
 55    def default_size(self) -> int:
 56        """
 57        automatically determine the size of the commands list
 58        """
 59
 60        if self._dtm == SPCM_DDS_DTM_SINGLE:
 61            return self.card.get_i(SPC_DDS_QUEUE_CMD_MAX) // 2
 62        elif self._dtm == SPCM_DDS_DTM_DMA:
 63            return KIBI(16)
 64        raise SpcmException(text="Data transfer mode not supported.")
 65    
 66    def allocate(self) -> None:
 67        """
 68        allocate memory for the commands list
 69        """
 70        if self.command_list is not None:
 71            del self.command_list
 72        elems = (ST_LIST_PARAM * (self._list_size + 1))() # +1 for the write to card command at the end
 73        self.command_list = ctypes.cast(elems, ctypes.POINTER(ST_LIST_PARAM))
 74        self.current_index = 0
 75
 76    def load(self, data : dict, exec_mode : int = SPCM_DDS_CMD_EXEC_AT_TRG, repeat : int = 1) -> None:
 77        """
 78        preload the command list with data
 79
 80        Parameters
 81        ----------
 82        data : dict
 83            the data to be preloaded
 84        mode : int = SPCM_DDS_CMD_EXEC_AT_TRG
 85            the mode of execution
 86        repeat : int = 1
 87            the number of times to repeat the data, if 0 is given the buffer is filled up with the maximal number of blocks that fit in.
 88        
 89        TODO make this possible for multiple different keys
 90        """
 91
 92        key = list(data.keys())[0]
 93        value_list = data[key] # For now only take the first key
 94        size = len(value_list)
 95        index = 0
 96        if repeat == 0:
 97            # repeat the data until the block is full
 98            repeat = self.list_size // (2*size)
 99        for _ in range(repeat):
100            for value in value_list:
101                # Write value
102                self.command_list[index].lReg = key
103                self.command_list[index].lType = TYPE_DOUBLE
104                self.command_list[index].dValue = value
105                index += 1
106                # Write trigger mode
107                self.command_list[index].lReg = SPC_DDS_CMD
108                self.command_list[index].lType = TYPE_INT64
109                self.command_list[index].llValue = exec_mode
110                index += 1
111        self.write_to_card()
112        self.current_index = index
113
114    def write_to_card(self) -> None:
115        """
116        write the command list to the card
117        """
118
119        self.command_list[self.current_index].lReg = SPC_DDS_CMD
120        self.command_list[self.current_index].lType = TYPE_INT64
121        self.command_list[self.current_index].llValue = SPCM_DDS_CMD_WRITE_TO_CARD
122        self.current_index += 1
123
124    def write(self) -> None:
125        """
126        send the currently loaded data to the card
127        """
128
129        if self.mode == self.WRITE_MODE.EXCEPTION_IF_FULL:
130            if self.avail_user_len() < (self.current_index) * ctypes.sizeof(ST_LIST_PARAM):
131                raise SpcmException(text="Buffer is full")
132        elif self.mode == self.WRITE_MODE.WAIT_IF_FULL:
133            timer = 0
134            while self.avail_user_len() < (self.current_index) * ctypes.sizeof(ST_LIST_PARAM):
135                print("Waiting for buffer to empty {}".format("."*(timer//100)), end="\r")
136                timer = (timer + 1) % 400
137        self.card.set_ptr(SPC_REGISTER_LIST, self.command_list, (self.current_index) * ctypes.sizeof(ST_LIST_PARAM))
138    
139    def avail_user_len(self) -> int:
140        """
141        get the available space for commands in the hardware queue
142        """
143
144        if self._dtm == SPCM_DDS_DTM_SINGLE:
145            return self._command_max - self.card.get_i(SPC_DDS_QUEUE_CMD_COUNT)
146        elif self._dtm == SPCM_DDS_DTM_DMA:
147            return self.card.get_i(SPC_DATA_AVAIL_USER_LEN)
148        else:
149            raise SpcmException(text="Data transfer mode not supported.")
150
151
152    @property
153    def list_size(self) -> int:
154        """
155        get the size of the command list
156        """
157
158        return self._list_size
159
160    @list_size.setter
161    def list_size(self, size : int) -> None:
162        """
163        set the size of the command list
164
165        Parameters
166        ----------
167        size : int
168            the size of the command list
169        """
170
171        self._list_size = size
172        self.allocate()
173    
174    def reset(self) -> None:
175        """
176        reset the dds firmware
177        """
178
179        # The reset shouldn't be queued!
180        self.card.set_i(SPC_DDS_CMD, SPCM_DDS_CMD_RESET)

Abstraction of the set_ptr and register SPC_REGISTER_LIST for command streaming in the DDS functionality

DDSCommandList(*args, **kwargs)
30    def __init__(self, *args, **kwargs) -> None:
31        super().__init__(*args, **kwargs)
32
33        self.command_list = None
34        self.current_index = 0
35        
36        self._dtm = SPCM_DDS_DTM_SINGLE
37        
38        self.list_size = self.default_size()

Takes a Card object that is used by the functionality

Parameters
  • card (Card): a Card object on which the functionality works
mode: int = <WRITE_MODE.NO_CHECK: 0>
command_list: _ctypes._Pointer = None
commands_transfered: int = 0
current_index: int = 0
list_size: int
152    @property
153    def list_size(self) -> int:
154        """
155        get the size of the command list
156        """
157
158        return self._list_size

get the size of the command list

def data_transfer_mode(self, mode: int) -> None:
40    def data_transfer_mode(self, mode : int) -> None:
41        """
42        set the data transfer mode of the DDS
43
44        Parameters
45        ----------
46        mode : int
47            the data transfer mode
48        """
49
50        self._dtm = mode
51        self.card.set_i(SPC_DDS_DATA_TRANSFER_MODE, mode)
52        self.card.set_i(SPC_DDS_CMD, SPCM_DDS_CMD_WRITE_TO_CARD)
53        self.list_size = self.default_size()

set the data transfer mode of the DDS

Parameters
  • mode (int): the data transfer mode
def default_size(self) -> int:
55    def default_size(self) -> int:
56        """
57        automatically determine the size of the commands list
58        """
59
60        if self._dtm == SPCM_DDS_DTM_SINGLE:
61            return self.card.get_i(SPC_DDS_QUEUE_CMD_MAX) // 2
62        elif self._dtm == SPCM_DDS_DTM_DMA:
63            return KIBI(16)
64        raise SpcmException(text="Data transfer mode not supported.")

automatically determine the size of the commands list

def allocate(self) -> None:
66    def allocate(self) -> None:
67        """
68        allocate memory for the commands list
69        """
70        if self.command_list is not None:
71            del self.command_list
72        elems = (ST_LIST_PARAM * (self._list_size + 1))() # +1 for the write to card command at the end
73        self.command_list = ctypes.cast(elems, ctypes.POINTER(ST_LIST_PARAM))
74        self.current_index = 0

allocate memory for the commands list

def load(self, data: dict, exec_mode: int = 2, repeat: int = 1) -> None:
 76    def load(self, data : dict, exec_mode : int = SPCM_DDS_CMD_EXEC_AT_TRG, repeat : int = 1) -> None:
 77        """
 78        preload the command list with data
 79
 80        Parameters
 81        ----------
 82        data : dict
 83            the data to be preloaded
 84        mode : int = SPCM_DDS_CMD_EXEC_AT_TRG
 85            the mode of execution
 86        repeat : int = 1
 87            the number of times to repeat the data, if 0 is given the buffer is filled up with the maximal number of blocks that fit in.
 88        
 89        TODO make this possible for multiple different keys
 90        """
 91
 92        key = list(data.keys())[0]
 93        value_list = data[key] # For now only take the first key
 94        size = len(value_list)
 95        index = 0
 96        if repeat == 0:
 97            # repeat the data until the block is full
 98            repeat = self.list_size // (2*size)
 99        for _ in range(repeat):
100            for value in value_list:
101                # Write value
102                self.command_list[index].lReg = key
103                self.command_list[index].lType = TYPE_DOUBLE
104                self.command_list[index].dValue = value
105                index += 1
106                # Write trigger mode
107                self.command_list[index].lReg = SPC_DDS_CMD
108                self.command_list[index].lType = TYPE_INT64
109                self.command_list[index].llValue = exec_mode
110                index += 1
111        self.write_to_card()
112        self.current_index = index

preload the command list with data

Parameters
  • data (dict): the data to be preloaded
  • mode (int = SPCM_DDS_CMD_EXEC_AT_TRG): the mode of execution
  • repeat (int = 1): the number of times to repeat the data, if 0 is given the buffer is filled up with the maximal number of blocks that fit in.
  • TODO make this possible for multiple different keys
def write_to_card(self) -> None:
114    def write_to_card(self) -> None:
115        """
116        write the command list to the card
117        """
118
119        self.command_list[self.current_index].lReg = SPC_DDS_CMD
120        self.command_list[self.current_index].lType = TYPE_INT64
121        self.command_list[self.current_index].llValue = SPCM_DDS_CMD_WRITE_TO_CARD
122        self.current_index += 1

write the command list to the card

def write(self) -> None:
124    def write(self) -> None:
125        """
126        send the currently loaded data to the card
127        """
128
129        if self.mode == self.WRITE_MODE.EXCEPTION_IF_FULL:
130            if self.avail_user_len() < (self.current_index) * ctypes.sizeof(ST_LIST_PARAM):
131                raise SpcmException(text="Buffer is full")
132        elif self.mode == self.WRITE_MODE.WAIT_IF_FULL:
133            timer = 0
134            while self.avail_user_len() < (self.current_index) * ctypes.sizeof(ST_LIST_PARAM):
135                print("Waiting for buffer to empty {}".format("."*(timer//100)), end="\r")
136                timer = (timer + 1) % 400
137        self.card.set_ptr(SPC_REGISTER_LIST, self.command_list, (self.current_index) * ctypes.sizeof(ST_LIST_PARAM))

send the currently loaded data to the card

def avail_user_len(self) -> int:
139    def avail_user_len(self) -> int:
140        """
141        get the available space for commands in the hardware queue
142        """
143
144        if self._dtm == SPCM_DDS_DTM_SINGLE:
145            return self._command_max - self.card.get_i(SPC_DDS_QUEUE_CMD_COUNT)
146        elif self._dtm == SPCM_DDS_DTM_DMA:
147            return self.card.get_i(SPC_DATA_AVAIL_USER_LEN)
148        else:
149            raise SpcmException(text="Data transfer mode not supported.")

get the available space for commands in the hardware queue

def reset(self) -> None:
174    def reset(self) -> None:
175        """
176        reset the dds firmware
177        """
178
179        # The reset shouldn't be queued!
180        self.card.set_i(SPC_DDS_CMD, SPCM_DDS_CMD_RESET)

reset the dds firmware

class DDSCommandList.WRITE_MODE(enum.IntEnum):
16    class WRITE_MODE(IntEnum):
17        NO_CHECK = 0
18        EXCEPTION_IF_FULL = 1
19        WAIT_IF_FULL = 2

An enumeration.

NO_CHECK = <WRITE_MODE.NO_CHECK: 0>
EXCEPTION_IF_FULL = <WRITE_MODE.EXCEPTION_IF_FULL: 1>
WAIT_IF_FULL = <WRITE_MODE.WAIT_IF_FULL: 2>
class DDSCommandQueue(spcm.DDSCommandList):
  8class DDSCommandQueue(DDSCommandList):
  9    """
 10    Abstraction class of the set_ptr and register `SPC_REGISTER_LIST` for command streaming in the DDS functionality.
 11    This class is used to write commands to the card in a more efficient way using a queuing mechanism that writes
 12    to the card when the queue is filled-up.
 13    """
 14
 15    def __init__(self, *args, **kwargs) -> None:
 16        super().__init__(*args, **kwargs)
 17        self.mode = self.WRITE_MODE.NO_CHECK
 18
 19    def set_i(self, reg : int, value : int) -> None:
 20        """
 21        set an integer value to a register
 22
 23        Parameters
 24        ----------
 25        reg : int
 26            the register to be changed
 27        value : int
 28            the value to be set
 29        """
 30        
 31        self.command_list[self.current_index].lReg = reg
 32        self.command_list[self.current_index].lType = TYPE_INT64
 33        self.command_list[self.current_index].llValue = value
 34        self.current_index += 1
 35
 36        if self.current_index >= self.list_size:
 37            self.write_to_card()
 38    
 39    def set_d(self, reg : int, value) -> None:
 40        """
 41        set a double value to a register
 42
 43        Parameters
 44        ----------
 45        reg : int
 46            the register to be changed
 47        value : np._float64
 48            the value to be set
 49        """
 50        
 51        self.command_list[self.current_index].lReg = reg
 52        self.command_list[self.current_index].lType = TYPE_DOUBLE
 53        self.command_list[self.current_index].dValue = value
 54        self.current_index += 1
 55
 56        if self.current_index >= self.list_size:
 57            self.write_to_card()
 58
 59    def write_to_card(self) -> None:
 60        """
 61        write the current list of commands to the card
 62        """
 63
 64        super().write_to_card()
 65        self.write()
 66
 67    def write(self) -> None:
 68        """
 69        write the current list of commands to the card and reset the current command index
 70        """
 71
 72        super().write()
 73        self.current_index = 0
 74
 75    # DDS "static" parameters
 76    def amp(self, index : int, amplitude : float) -> None:
 77        """
 78        set the amplitude of the sine wave of a specific core (see register `SPC_DDS_CORE0_AMP` in the manual)
 79        
 80        Parameters
 81        ----------
 82        index : int
 83            the core index
 84        amplitude : float
 85            the value between 0 and 1 corresponding to the amplitude
 86        """
 87
 88        self.set_d(SPC_DDS_CORE0_AMP + index, amplitude)
 89
 90    def freq(self, index : int, frequency : float) -> None:
 91        """
 92        set the frequency of the sine wave of a specific core (see register `SPC_DDS_CORE0_FREQ` in the manual)
 93        
 94        Parameters
 95        ----------
 96        index : int
 97            the core index
 98        frequency : float
 99            the value of the frequency in Hz
100        """
101
102        self.set_d(SPC_DDS_CORE0_FREQ + index, frequency)
103    
104    def phase(self, index : int, phase : float) -> None:
105        """
106        set the phase of the sine wave of a specific core (see register `SPC_DDS_CORE0_PHASE` in the manual)
107        
108        Parameters
109        ----------
110        core_index : int
111            the index of the core to be changed
112        phase : float
113            the value between 0 and 360 degrees of the phase
114        """
115
116        self.set_d(SPC_DDS_CORE0_PHASE + index, phase)
117    
118    def freq_slope(self, core_index : int, slope : float) -> None:
119        """
120        set the frequency slope of the linearly changing frequency of the sine wave of a specific core (see register `SPC_DDS_CORE0_FREQ_SLOPE` in the manual)
121        
122        Parameters
123        ----------
124        core_index : int
125            the index of the core to be changed
126        slope : float
127            the rate of frequency change in Hz/s
128        """
129
130        self.set_d(SPC_DDS_CORE0_FREQ_SLOPE + core_index, slope)
131
132    def amp_slope(self, core_index : int, slope : float) -> None:
133        """
134        set the amplitude slope of the linearly changing amplitude of the sine wave of a specific core (see register `SPC_DDS_CORE0_AMP_SLOPE` in the manual)
135        
136        Parameters
137        ----------
138        core_index : int
139            the index of the core to be changed
140        slope : float
141            the rate of amplitude change in 1/s
142        """
143
144        self.set_d(SPC_DDS_CORE0_AMP_SLOPE + core_index, slope)

Abstraction class of the set_ptr and register SPC_REGISTER_LIST for command streaming in the DDS functionality. This class is used to write commands to the card in a more efficient way using a queuing mechanism that writes to the card when the queue is filled-up.

DDSCommandQueue(*args, **kwargs)
15    def __init__(self, *args, **kwargs) -> None:
16        super().__init__(*args, **kwargs)
17        self.mode = self.WRITE_MODE.NO_CHECK

Takes a Card object that is used by the functionality

Parameters
  • card (Card): a Card object on which the functionality works
mode = <WRITE_MODE.NO_CHECK: 0>
def set_i(self, reg: int, value: int) -> None:
19    def set_i(self, reg : int, value : int) -> None:
20        """
21        set an integer value to a register
22
23        Parameters
24        ----------
25        reg : int
26            the register to be changed
27        value : int
28            the value to be set
29        """
30        
31        self.command_list[self.current_index].lReg = reg
32        self.command_list[self.current_index].lType = TYPE_INT64
33        self.command_list[self.current_index].llValue = value
34        self.current_index += 1
35
36        if self.current_index >= self.list_size:
37            self.write_to_card()

set an integer value to a register

Parameters
  • reg (int): the register to be changed
  • value (int): the value to be set
def set_d(self, reg: int, value) -> None:
39    def set_d(self, reg : int, value) -> None:
40        """
41        set a double value to a register
42
43        Parameters
44        ----------
45        reg : int
46            the register to be changed
47        value : np._float64
48            the value to be set
49        """
50        
51        self.command_list[self.current_index].lReg = reg
52        self.command_list[self.current_index].lType = TYPE_DOUBLE
53        self.command_list[self.current_index].dValue = value
54        self.current_index += 1
55
56        if self.current_index >= self.list_size:
57            self.write_to_card()

set a double value to a register

Parameters
  • reg (int): the register to be changed
  • value (np._float64): the value to be set
def write_to_card(self) -> None:
59    def write_to_card(self) -> None:
60        """
61        write the current list of commands to the card
62        """
63
64        super().write_to_card()
65        self.write()

write the current list of commands to the card

def write(self) -> None:
67    def write(self) -> None:
68        """
69        write the current list of commands to the card and reset the current command index
70        """
71
72        super().write()
73        self.current_index = 0

write the current list of commands to the card and reset the current command index

def amp(self, index: int, amplitude: float) -> None:
76    def amp(self, index : int, amplitude : float) -> None:
77        """
78        set the amplitude of the sine wave of a specific core (see register `SPC_DDS_CORE0_AMP` in the manual)
79        
80        Parameters
81        ----------
82        index : int
83            the core index
84        amplitude : float
85            the value between 0 and 1 corresponding to the amplitude
86        """
87
88        self.set_d(SPC_DDS_CORE0_AMP + index, amplitude)

set the amplitude of the sine wave of a specific core (see register SPC_DDS_CORE0_AMP in the manual)

Parameters
  • index (int): the core index
  • amplitude (float): the value between 0 and 1 corresponding to the amplitude
def freq(self, index: int, frequency: float) -> None:
 90    def freq(self, index : int, frequency : float) -> None:
 91        """
 92        set the frequency of the sine wave of a specific core (see register `SPC_DDS_CORE0_FREQ` in the manual)
 93        
 94        Parameters
 95        ----------
 96        index : int
 97            the core index
 98        frequency : float
 99            the value of the frequency in Hz
100        """
101
102        self.set_d(SPC_DDS_CORE0_FREQ + index, frequency)

set the frequency of the sine wave of a specific core (see register SPC_DDS_CORE0_FREQ in the manual)

Parameters
  • index (int): the core index
  • frequency (float): the value of the frequency in Hz
def phase(self, index: int, phase: float) -> None:
104    def phase(self, index : int, phase : float) -> None:
105        """
106        set the phase of the sine wave of a specific core (see register `SPC_DDS_CORE0_PHASE` in the manual)
107        
108        Parameters
109        ----------
110        core_index : int
111            the index of the core to be changed
112        phase : float
113            the value between 0 and 360 degrees of the phase
114        """
115
116        self.set_d(SPC_DDS_CORE0_PHASE + index, phase)

set the phase of the sine wave of a specific core (see register SPC_DDS_CORE0_PHASE in the manual)

Parameters
  • core_index (int): the index of the core to be changed
  • phase (float): the value between 0 and 360 degrees of the phase
def freq_slope(self, core_index: int, slope: float) -> None:
118    def freq_slope(self, core_index : int, slope : float) -> None:
119        """
120        set the frequency slope of the linearly changing frequency of the sine wave of a specific core (see register `SPC_DDS_CORE0_FREQ_SLOPE` in the manual)
121        
122        Parameters
123        ----------
124        core_index : int
125            the index of the core to be changed
126        slope : float
127            the rate of frequency change in Hz/s
128        """
129
130        self.set_d(SPC_DDS_CORE0_FREQ_SLOPE + core_index, slope)

set the frequency slope of the linearly changing frequency of the sine wave of a specific core (see register SPC_DDS_CORE0_FREQ_SLOPE in the manual)

Parameters
  • core_index (int): the index of the core to be changed
  • slope (float): the rate of frequency change in Hz/s
def amp_slope(self, core_index: int, slope: float) -> None:
132    def amp_slope(self, core_index : int, slope : float) -> None:
133        """
134        set the amplitude slope of the linearly changing amplitude of the sine wave of a specific core (see register `SPC_DDS_CORE0_AMP_SLOPE` in the manual)
135        
136        Parameters
137        ----------
138        core_index : int
139            the index of the core to be changed
140        slope : float
141            the rate of amplitude change in 1/s
142        """
143
144        self.set_d(SPC_DDS_CORE0_AMP_SLOPE + core_index, slope)

set the amplitude slope of the linearly changing amplitude of the sine wave of a specific core (see register SPC_DDS_CORE0_AMP_SLOPE in the manual)

Parameters
  • core_index (int): the index of the core to be changed
  • slope (float): the rate of amplitude change in 1/s
class PulseGenerator:
 16class PulseGenerator:
 17    """
 18    a class to implement a single pulse generator
 19    
 20    Parameters
 21    ----------
 22    card : Card
 23        the card object that is used by the functionality
 24    pg_index : int
 25        the index of the used pulse generator
 26    """
 27
 28    card : Card
 29    pg_index : int
 30    """The index of the pulse generator"""
 31
 32    _reg_distance : int = 100
 33
 34    def __init__(self, card : Card, pg_index : int, *args, **kwargs) -> None:
 35        """
 36        The constructor of the PulseGenerator class
 37
 38        Parameters
 39        ----------
 40        card : Card
 41            the card object that is used by the functionality
 42        pg_index : int
 43            the index of the used pulse generator
 44        """
 45        
 46        self.card = card
 47        self.pg_index = pg_index
 48
 49    def __str__(self) -> str:
 50        """
 51        String representation of the PulseGenerator class
 52    
 53        Returns
 54        -------
 55        str
 56            String representation of the PulseGenerator class
 57        """
 58        
 59        return f"PulseGenerator(card={self.card}, pg_index={self.pg_index})"
 60    
 61    __repr__ = __str__
 62    
 63    # The trigger behavior of the pulse generator
 64    def mode(self, mode : int = None) -> int:
 65        """
 66        Set the trigger mode of the pulse generator (see register 'SPC_XIO_PULSEGEN0_MODE' in chapter `Pulse Generator` in the manual)
 67
 68        Parameters
 69        ----------
 70        mode : int
 71            The trigger mode
 72        
 73        Returns
 74        -------
 75        int
 76            The trigger mode
 77        """
 78
 79        if mode is not None:
 80            self.card.set_i(SPC_XIO_PULSEGEN0_MODE + self._reg_distance*self.pg_index, mode)
 81        return self.card.get_i(SPC_XIO_PULSEGEN0_MODE + self._reg_distance*self.pg_index)
 82    
 83    # The duration of a single period
 84    def period_length(self, length : int = None) -> int:
 85        """
 86        Set the period length of the pulse generator (see register 'SPC_XIO_PULSEGEN0_LEN' in chapter `Pulse Generator` in the manual)
 87
 88        Parameters
 89        ----------
 90        length : int
 91            The period length in clock cycles
 92        
 93        Returns
 94        -------
 95        int
 96            The period length in clock cycles
 97        """
 98
 99        if length is not None:
100            self.card.set_i(SPC_XIO_PULSEGEN0_LEN + self._reg_distance*self.pg_index, length)
101        return self.card.get_i(SPC_XIO_PULSEGEN0_LEN + self._reg_distance*self.pg_index)
102
103    def avail_length_min(self) -> int:
104        """
105        Returns the minimum length (period) of the pulse generator’s output pulses in clock cycles. (see register 'SPC_XIO_PULSEGEN_AVAILLEN_MIN' in chapter `Pulse Generator` in the manual)
106
107        Returns
108        -------
109        int
110            The available minimal length in clock cycles
111        """
112        return self.card.get_i(SPC_XIO_PULSEGEN_AVAILLEN_MIN)
113    
114    def avail_length_max(self) -> int:
115        """
116        Returns the maximum length (period) of the pulse generator’s output pulses in clock cycles. (see register 'SPC_XIO_PULSEGEN_AVAILLEN_MAX' in chapter `Pulse Generator` in the manual)
117
118        Returns
119        -------
120        int
121            The available maximal length in clock cycles
122        """
123        return self.card.get_i(SPC_XIO_PULSEGEN_AVAILLEN_MAX)
124    
125    def avail_length_step(self) -> int:
126        """
127        Returns the step size of the pulse generator’s output pulses in clock cycles. (see register 'SPC_XIO_PULSEGEN_AVAILLEN_STEP' in chapter `Pulse Generator` in the manual)
128
129        Returns
130        -------
131        int
132            The available step size in clock cycles
133        """
134        return self.card.get_i(SPC_XIO_PULSEGEN_AVAILLEN_STEP)
135    
136    # The time that the signal is high during one period
137    def high_length(self, length : int = None) -> int:
138        """
139        Set the high length of the pulse generator (see register 'SPC_XIO_PULSEGEN0_HIGH' in chapter `Pulse Generator` in the manual)
140
141        Parameters
142        ----------
143        pg_index : int
144            The index of the pulse generator
145        length : int
146            The high length in clock cycles
147        
148        Returns
149        -------
150        int
151            The high length in clock cycles
152        """
153
154        if length is not None:
155            self.card.set_i(SPC_XIO_PULSEGEN0_HIGH + self._reg_distance*self.pg_index, length)
156        return self.card.get_i(SPC_XIO_PULSEGEN0_HIGH + self._reg_distance*self.pg_index)
157    
158    def avail_high_min(self) -> int:
159        """
160        Returns the minimum high length of the pulse generator’s output pulses in clock cycles. (see register 'SPC_XIO_PULSEGEN_AVAILHIGH_MIN' in chapter `Pulse Generator` in the manual)
161
162        Returns
163        -------
164        int
165            The available minimal high length in clock cycles
166        """
167        return self.card.get_i(SPC_XIO_PULSEGEN_AVAILHIGH_MIN)
168    
169    def avail_high_max(self) -> int:
170        """
171        Returns the maximum high length of the pulse generator’s output pulses in clock cycles. (see register 'SPC_XIO_PULSEGEN_AVAILHIGH_MAX' in chapter `Pulse Generator` in the manual)
172
173        Returns
174        -------
175        int
176            The available maximal high length in clock cycles
177        """
178        return self.card.get_i(SPC_XIO_PULSEGEN_AVAILHIGH_MAX)
179    
180    def avail_high_step(self) -> int:
181        """
182        Returns the step size of the pulse generator’s output pulses in clock cycles. (see register 'SPC_XIO_PULSEGEN_AVAILHIGH_STEP' in chapter `Pulse Generator` in the manual)
183
184        Returns
185        -------
186        int
187            The available step size in clock cycles
188        """
189        return self.card.get_i(SPC_XIO_PULSEGEN_AVAILHIGH_STEP)
190    
191    # The number of times that a single period is repeated
192    def num_loops(self, loops : int = None) -> int:
193        """
194        Set the number of loops of a single period on the pulse generator (see register 'SPC_XIO_PULSEGEN0_LOOPS' in chapter `Pulse Generator` in the manual)
195
196        Parameters
197        ----------
198        loops : int
199            The number of loops
200        
201        Returns
202        -------
203        int
204            The number of loops
205        """
206
207        if loops is not None:
208            self.card.set_i(SPC_XIO_PULSEGEN0_LOOPS + self._reg_distance*self.pg_index, loops)
209        return self.card.get_i(SPC_XIO_PULSEGEN0_LOOPS + self._reg_distance*self.pg_index)
210    
211    def avail_loops_min(self) -> int:
212        """
213        Returns the minimum number of loops of the pulse generator’s output pulses. (see register 'SPC_XIO_PULSEGEN_AVAILLOOPS_MIN' in chapter `Pulse Generator` in the manual)
214
215        Returns
216        -------
217        int
218            The available minimal number of loops
219        """
220        return self.card.get_i(SPC_XIO_PULSEGEN_AVAILLOOPS_MIN)
221    
222    def avail_loops_max(self) -> int:
223        """
224        Returns the maximum number of loops of the pulse generator’s output pulses. (see register 'SPC_XIO_PULSEGEN_AVAILLOOPS_MAX' in chapter `Pulse Generator` in the manual)
225
226        Returns
227        -------
228        int
229            The available maximal number of loops
230        """
231        return self.card.get_i(SPC_XIO_PULSEGEN_AVAILLOOPS_MAX)
232    
233    def avail_loops_step(self) -> int:
234        """
235        Returns the step size of the pulse generator’s output pulses. (see register 'SPC_XIO_PULSEGEN_AVAILLOOPS_STEP' in chapter `Pulse Generator` in the manual)
236
237        Returns
238        -------
239        int
240            Returns the step size when defining the repetition of pulse generator’s output.
241        """
242        return self.card.get_i(SPC_XIO_PULSEGEN_AVAILLOOPS_STEP)
243    
244    # The delay between the start of the pulse generator and the first pulse
245    def delay(self, delay : int = None) -> int:
246        """
247        Set the delay of the pulse generator (see register 'SPC_XIO_PULSEGEN0_DELAY' in chapter `Pulse Generator` in the manual)
248
249        Parameters
250        ----------
251        delay : int
252            The delay in clock cycles
253        
254        Returns
255        -------
256        int
257            The delay in clock cycles
258        """
259
260        if delay is not None:
261            self.card.set_i(SPC_XIO_PULSEGEN0_DELAY + self._reg_distance*self.pg_index, delay)
262        return self.card.get_i(SPC_XIO_PULSEGEN0_DELAY + self._reg_distance*self.pg_index)
263    
264    def avail_delay_min(self) -> int:
265        """
266        Returns the minimum delay of the pulse generator’s output pulses in clock cycles. (see register 'SPC_XIO_PULSEGEN_AVAILDELAY_MIN' in chapter `Pulse Generator` in the manual)
267
268        Returns
269        -------
270        int
271            The available minimal delay in clock cycles
272        """
273        return self.card.get_i(SPC_XIO_PULSEGEN_AVAILDELAY_MIN)
274    
275    def avail_delay_max(self) -> int:
276        """
277        Returns the maximum delay of the pulse generator’s output pulses in clock cycles. (see register 'SPC_XIO_PULSEGEN_AVAILDELAY_MAX' in chapter `Pulse Generator` in the manual)
278
279        Returns
280        -------
281        int
282            The available maximal delay in clock cycles
283        """
284        return self.card.get_i(SPC_XIO_PULSEGEN_AVAILDELAY_MAX)
285    
286    def avail_delay_step(self) -> int:
287        """
288        Returns the step size of the delay of the pulse generator’s output pulses in clock cycles. (see register 'SPC_XIO_PULSEGEN_AVAILDELAY_STEP' in chapter `Pulse Generator` in the manual)
289
290        Returns
291        -------
292        int
293            The available step size of the delay in clock cycles
294        """
295        return self.card.get_i(SPC_XIO_PULSEGEN_AVAILDELAY_STEP)
296
297    # Trigger muxes
298    def mux1(self, mux : int = None) -> int:
299        """
300        Set the trigger mux 1 of the pulse generator (see register 'SPC_XIO_PULSEGEN0_MUX1_SRC' in chapter `Pulse Generator` in the manual)
301
302        Parameters
303        ----------
304        mux : int
305            The trigger mux 1
306        
307        Returns
308        -------
309        int
310            The trigger mux 1
311        """
312
313        if mux is not None:
314            self.card.set_i(SPC_XIO_PULSEGEN0_MUX1_SRC + self._reg_distance*self.pg_index, mux)
315        return self.card.get_i(SPC_XIO_PULSEGEN0_MUX1_SRC + self._reg_distance*self.pg_index)
316    
317    def mux2(self, mux : int = None) -> int:
318        """
319        Set the trigger mux 2 of the pulse generator (see register 'SPC_XIO_PULSEGEN0_MUX2_SRC' in chapter `Pulse Generator` in the manual)
320
321        Parameters
322        ----------
323        mux : int
324            The trigger mux 2
325
326        Returns
327        -------
328        int
329            The trigger mux 2
330        """
331
332        if mux is not None:
333            self.card.set_i(SPC_XIO_PULSEGEN0_MUX2_SRC + self._reg_distance*self.pg_index, mux)
334        return self.card.get_i(SPC_XIO_PULSEGEN0_MUX2_SRC + self._reg_distance*self.pg_index)
335    
336    def config(self, config : int = None) -> int:
337        """
338        Set the configuration of the pulse generator (see register 'SPC_XIO_PULSEGEN0_CONFIG' in chapter `Pulse Generator` in the manual)
339
340        Parameters
341        ----------
342        config : int
343            The configuration of the pulse generator
344
345        Returns
346        -------
347        int
348            The configuration of the pulse generator
349        """
350
351        if config is not None:
352            self.card.set_i(SPC_XIO_PULSEGEN0_CONFIG + self._reg_distance*self.pg_index, config)
353        return self.card.get_i(SPC_XIO_PULSEGEN0_CONFIG + self._reg_distance*self.pg_index)
354    
355    def _get_clock(self, return_unit : pint.Unit = None) -> int:
356        """
357        Get the clock rate of the pulse generator (see register 'SPC_XIO_PULSEGEN_CLOCK' in chapter `Pulse Generator` in the manual)
358
359        Returns
360        -------
361        int
362            The clock rate in Hz
363        """
364
365        return_value = self.card.get_i(SPC_XIO_PULSEGEN_CLOCK)
366        return_value = UnitConversion.to_unit(return_value * units.Hz, return_unit)
367        return return_value
368    
369    # Higher abtraction functions
370
371    def pulse_period(self, period : pint.Quantity = None, return_unit : pint.Unit = units.s) -> pint.Quantity:
372        """
373        Set the period length of the pulse generator signal in a time unit
374
375        Parameters
376        ----------
377        period : pint.Quantity
378            The period length in seconds
379        
380        Returns
381        -------
382        pint.Quantity
383            The period length in seconds
384        """
385
386        if period is not None:
387            if isinstance(period, pint.Quantity):
388                period = int((period * self._get_clock(units.Hz)).to_base_units().magnitude)
389            else:
390                raise ValueError("The period must be a pint.Quantity")
391            self.period_length(period)
392        return_value = self.period_length()
393        return_value = UnitConversion.to_unit((return_value / self._get_clock(units.Hz)), return_unit)
394        return return_value
395    
396    def repetition_rate(self, rate : pint.Quantity = None, return_unit : pint.Unit = units.Hz) -> pint.Quantity:
397        """
398        Set the repetition rate of the pulse generator signal in a frequency unit
399
400        Parameters
401        ----------
402        rate : pint.Quantity
403            The repetition rate in Hz
404        
405        Returns
406        -------
407        pint.Quantity
408            The repetition rate in Hz
409        """
410
411        if rate is not None:
412            if isinstance(rate, pint.Quantity):
413                period = int(np.rint((self._get_clock(units.Hz) / rate).to_base_units().magnitude))
414            else:
415                raise ValueError("The rate must be a pint.Quantity")
416            self.period_length(period)
417        return_value = self.period_length()
418        return_value = UnitConversion.to_unit((self._get_clock(units.Hz) / return_value), return_unit)
419        return return_value
420    
421    def pulse_length(self, length : pint.Quantity, return_unit : pint.Unit = units.s) -> pint.Quantity:
422        """
423        Set the pulse length of the pulse generator signal in a time unit
424
425        Parameters
426        ----------
427        length : pint.Quantity
428            The pulse length in seconds
429        
430        Returns
431        -------
432        pint.Quantity
433            The pulse length in seconds
434        """
435
436        if length is not None:
437            if isinstance(length, pint.Quantity):
438                length = int((length * self._get_clock(units.Hz)).to_base_units().magnitude)
439            else:
440                raise ValueError("The length must be a pint.Quantity")
441            self.high_length(length)
442        return_value = self.high_length()
443        return_value = UnitConversion.to_unit((return_value / self._get_clock(units.Hz)), return_unit)
444        return return_value
445    
446    def duty_cycle(self, duty_cycle : pint.Quantity = None, return_unit : pint.Unit = units.percent) -> pint.Quantity:
447        """
448        Set the duty cycle of the pulse generator signal in a percentage unit
449
450        Parameters
451        ----------
452        duty_cycle : pint.Quantity
453            The duty cycle in percentage
454        
455        Returns
456        -------
457        pint.Quantity
458            The duty cycle in percentage
459        """
460
461        period_length = self.period_length()
462        if duty_cycle is not None:
463            if isinstance(duty_cycle, pint.Quantity):
464                high_length = int(np.rint(period_length * duty_cycle))
465            else:
466                raise ValueError("The cycle must be a pint.Quantity")
467            self.high_length(high_length)
468        return_value = self.high_length()
469        return_value = UnitConversion.to_unit((return_value / period_length) * 100 * units.percent, return_unit)
470        return return_value
471    
472    def start_delay(self, delay : pint.Unit = None, return_unit : pint.Unit = units.s) -> pint.Unit:
473        """
474        Set the start delay of the pulse generator signal in a time unit
475
476        Parameters
477        ----------
478        delay : pint.Unit
479            The start delay in a pint quantity with time unit
480        
481        Returns
482        -------
483        pint.Unit
484            The start delay in a pint quantity with time unit
485        """
486
487        if delay is not None:
488            if isinstance(delay, pint.Quantity):
489                delay = int((delay * self._get_clock(units.Hz)).to_base_units().magnitude)
490            else:
491                raise ValueError("The delay must be a pint.Quantity")
492        return_value = self.delay(delay)
493        return_value = UnitConversion.to_unit((return_value / self._get_clock(units.Hz)), return_unit)
494        return return_value
495    
496    repetitions = num_loops
497
498    def start_condition_state_signal(self, signal : int = 0, invert : bool = False) -> int:
499        """
500        Set the start condition state signal of the pulse generator (see register 'SPC_XIO_PULSEGEN0_MUX1' in chapter `Pulse Generator` in the manual)
501
502        NOTE
503        ----
504        The Pulse Generator is started when the combined signal of both start condition signals are true and a rising edge
505        is detected. The invert parameter inverts the start condition state signal.
506        
507        Parameters
508        ----------
509        signal : int
510            The start condition state signal
511        invert : bool
512            Invert the start condition state signal
513        
514        Returns
515        -------
516        int
517            The start condition state signal
518        """
519
520        return_signal = self.mux1(signal)
521        return_invert = self.config()
522        if invert:
523            return_invert |= SPCM_PULSEGEN_CONFIG_MUX1_INVERT
524        else:
525            return_invert &= ~SPCM_PULSEGEN_CONFIG_MUX1_INVERT
526        return_invert = self.config(return_invert)
527        return return_signal, ((return_invert & SPCM_PULSEGEN_CONFIG_MUX1_INVERT) != 0)
528
529    def start_condition_trigger_signal(self, signal : int = 0, invert : bool = False) -> int:
530        """
531        Set the start condition trigger signal of the pulse generator (see register 'SPC_XIO_PULSEGEN0_MUX2' in chapter `Pulse Generator` in the manual)
532
533        NOTE
534        ----
535        The Pulse Generator is started when the combined signal of both start condition signals are true and a rising edge
536        is detected. The invert parameter inverts the start condition state signal.
537        
538        Parameters
539        ----------
540        signal : int
541            The start condition trigger signal
542        invert : bool
543            Invert the start condition trigger signal
544        
545        Returns
546        -------
547        int
548            The start condition trigger signal
549        """
550
551        return_signal = self.mux2(signal)
552        return_invert = self.config()
553        if invert:
554            return_invert |= SPCM_PULSEGEN_CONFIG_MUX2_INVERT
555        else:
556            return_invert &= ~SPCM_PULSEGEN_CONFIG_MUX2_INVERT
557        return_invert = self.config(return_invert)
558        return return_signal, ((return_invert & SPCM_PULSEGEN_CONFIG_MUX2_INVERT) != 0)
559    
560    def invert_start_condition(self, invert : bool = None) -> bool:
561        """
562        Invert the start condition of the pulse generator
563
564        Parameters
565        ----------
566        invert : bool
567            Invert the start condition
568        
569        Returns
570        -------
571        bool
572            The start condition inversion
573        """
574
575        if invert is not None:
576            return_invert = self.config()
577            if invert:
578                return_invert |= SPCM_PULSEGEN_CONFIG_INVERT
579            else:
580                return_invert &= ~SPCM_PULSEGEN_CONFIG_INVERT
581            self.config(return_invert)
582        return ((self.config() & SPCM_PULSEGEN_CONFIG_INVERT) != 0)

a class to implement a single pulse generator

Parameters
  • card (Card): the card object that is used by the functionality
  • pg_index (int): the index of the used pulse generator
PulseGenerator(card: Card, pg_index: int, *args, **kwargs)
34    def __init__(self, card : Card, pg_index : int, *args, **kwargs) -> None:
35        """
36        The constructor of the PulseGenerator class
37
38        Parameters
39        ----------
40        card : Card
41            the card object that is used by the functionality
42        pg_index : int
43            the index of the used pulse generator
44        """
45        
46        self.card = card
47        self.pg_index = pg_index

The constructor of the PulseGenerator class

Parameters
  • card (Card): the card object that is used by the functionality
  • pg_index (int): the index of the used pulse generator
card: Card
pg_index: int

The index of the pulse generator

def mode(self, mode: int = None) -> int:
64    def mode(self, mode : int = None) -> int:
65        """
66        Set the trigger mode of the pulse generator (see register 'SPC_XIO_PULSEGEN0_MODE' in chapter `Pulse Generator` in the manual)
67
68        Parameters
69        ----------
70        mode : int
71            The trigger mode
72        
73        Returns
74        -------
75        int
76            The trigger mode
77        """
78
79        if mode is not None:
80            self.card.set_i(SPC_XIO_PULSEGEN0_MODE + self._reg_distance*self.pg_index, mode)
81        return self.card.get_i(SPC_XIO_PULSEGEN0_MODE + self._reg_distance*self.pg_index)

Set the trigger mode of the pulse generator (see register 'SPC_XIO_PULSEGEN0_MODE' in chapter Pulse Generator in the manual)

Parameters
  • mode (int): The trigger mode
Returns
  • int: The trigger mode
def period_length(self, length: int = None) -> int:
 84    def period_length(self, length : int = None) -> int:
 85        """
 86        Set the period length of the pulse generator (see register 'SPC_XIO_PULSEGEN0_LEN' in chapter `Pulse Generator` in the manual)
 87
 88        Parameters
 89        ----------
 90        length : int
 91            The period length in clock cycles
 92        
 93        Returns
 94        -------
 95        int
 96            The period length in clock cycles
 97        """
 98
 99        if length is not None:
100            self.card.set_i(SPC_XIO_PULSEGEN0_LEN + self._reg_distance*self.pg_index, length)
101        return self.card.get_i(SPC_XIO_PULSEGEN0_LEN + self._reg_distance*self.pg_index)

Set the period length of the pulse generator (see register 'SPC_XIO_PULSEGEN0_LEN' in chapter Pulse Generator in the manual)

Parameters
  • length (int): The period length in clock cycles
Returns
  • int: The period length in clock cycles
def avail_length_min(self) -> int:
103    def avail_length_min(self) -> int:
104        """
105        Returns the minimum length (period) of the pulse generator’s output pulses in clock cycles. (see register 'SPC_XIO_PULSEGEN_AVAILLEN_MIN' in chapter `Pulse Generator` in the manual)
106
107        Returns
108        -------
109        int
110            The available minimal length in clock cycles
111        """
112        return self.card.get_i(SPC_XIO_PULSEGEN_AVAILLEN_MIN)

Returns the minimum length (period) of the pulse generator’s output pulses in clock cycles. (see register 'SPC_XIO_PULSEGEN_AVAILLEN_MIN' in chapter Pulse Generator in the manual)

Returns
  • int: The available minimal length in clock cycles
def avail_length_max(self) -> int:
114    def avail_length_max(self) -> int:
115        """
116        Returns the maximum length (period) of the pulse generator’s output pulses in clock cycles. (see register 'SPC_XIO_PULSEGEN_AVAILLEN_MAX' in chapter `Pulse Generator` in the manual)
117
118        Returns
119        -------
120        int
121            The available maximal length in clock cycles
122        """
123        return self.card.get_i(SPC_XIO_PULSEGEN_AVAILLEN_MAX)

Returns the maximum length (period) of the pulse generator’s output pulses in clock cycles. (see register 'SPC_XIO_PULSEGEN_AVAILLEN_MAX' in chapter Pulse Generator in the manual)

Returns
  • int: The available maximal length in clock cycles
def avail_length_step(self) -> int:
125    def avail_length_step(self) -> int:
126        """
127        Returns the step size of the pulse generator’s output pulses in clock cycles. (see register 'SPC_XIO_PULSEGEN_AVAILLEN_STEP' in chapter `Pulse Generator` in the manual)
128
129        Returns
130        -------
131        int
132            The available step size in clock cycles
133        """
134        return self.card.get_i(SPC_XIO_PULSEGEN_AVAILLEN_STEP)

Returns the step size of the pulse generator’s output pulses in clock cycles. (see register 'SPC_XIO_PULSEGEN_AVAILLEN_STEP' in chapter Pulse Generator in the manual)

Returns
  • int: The available step size in clock cycles
def high_length(self, length: int = None) -> int:
137    def high_length(self, length : int = None) -> int:
138        """
139        Set the high length of the pulse generator (see register 'SPC_XIO_PULSEGEN0_HIGH' in chapter `Pulse Generator` in the manual)
140
141        Parameters
142        ----------
143        pg_index : int
144            The index of the pulse generator
145        length : int
146            The high length in clock cycles
147        
148        Returns
149        -------
150        int
151            The high length in clock cycles
152        """
153
154        if length is not None:
155            self.card.set_i(SPC_XIO_PULSEGEN0_HIGH + self._reg_distance*self.pg_index, length)
156        return self.card.get_i(SPC_XIO_PULSEGEN0_HIGH + self._reg_distance*self.pg_index)

Set the high length of the pulse generator (see register 'SPC_XIO_PULSEGEN0_HIGH' in chapter Pulse Generator in the manual)

Parameters
  • pg_index (int): The index of the pulse generator
  • length (int): The high length in clock cycles
Returns
  • int: The high length in clock cycles
def avail_high_min(self) -> int:
158    def avail_high_min(self) -> int:
159        """
160        Returns the minimum high length of the pulse generator’s output pulses in clock cycles. (see register 'SPC_XIO_PULSEGEN_AVAILHIGH_MIN' in chapter `Pulse Generator` in the manual)
161
162        Returns
163        -------
164        int
165            The available minimal high length in clock cycles
166        """
167        return self.card.get_i(SPC_XIO_PULSEGEN_AVAILHIGH_MIN)

Returns the minimum high length of the pulse generator’s output pulses in clock cycles. (see register 'SPC_XIO_PULSEGEN_AVAILHIGH_MIN' in chapter Pulse Generator in the manual)

Returns
  • int: The available minimal high length in clock cycles
def avail_high_max(self) -> int:
169    def avail_high_max(self) -> int:
170        """
171        Returns the maximum high length of the pulse generator’s output pulses in clock cycles. (see register 'SPC_XIO_PULSEGEN_AVAILHIGH_MAX' in chapter `Pulse Generator` in the manual)
172
173        Returns
174        -------
175        int
176            The available maximal high length in clock cycles
177        """
178        return self.card.get_i(SPC_XIO_PULSEGEN_AVAILHIGH_MAX)

Returns the maximum high length of the pulse generator’s output pulses in clock cycles. (see register 'SPC_XIO_PULSEGEN_AVAILHIGH_MAX' in chapter Pulse Generator in the manual)

Returns
  • int: The available maximal high length in clock cycles
def avail_high_step(self) -> int:
180    def avail_high_step(self) -> int:
181        """
182        Returns the step size of the pulse generator’s output pulses in clock cycles. (see register 'SPC_XIO_PULSEGEN_AVAILHIGH_STEP' in chapter `Pulse Generator` in the manual)
183
184        Returns
185        -------
186        int
187            The available step size in clock cycles
188        """
189        return self.card.get_i(SPC_XIO_PULSEGEN_AVAILHIGH_STEP)

Returns the step size of the pulse generator’s output pulses in clock cycles. (see register 'SPC_XIO_PULSEGEN_AVAILHIGH_STEP' in chapter Pulse Generator in the manual)

Returns
  • int: The available step size in clock cycles
def num_loops(self, loops: int = None) -> int:
192    def num_loops(self, loops : int = None) -> int:
193        """
194        Set the number of loops of a single period on the pulse generator (see register 'SPC_XIO_PULSEGEN0_LOOPS' in chapter `Pulse Generator` in the manual)
195
196        Parameters
197        ----------
198        loops : int
199            The number of loops
200        
201        Returns
202        -------
203        int
204            The number of loops
205        """
206
207        if loops is not None:
208            self.card.set_i(SPC_XIO_PULSEGEN0_LOOPS + self._reg_distance*self.pg_index, loops)
209        return self.card.get_i(SPC_XIO_PULSEGEN0_LOOPS + self._reg_distance*self.pg_index)

Set the number of loops of a single period on the pulse generator (see register 'SPC_XIO_PULSEGEN0_LOOPS' in chapter Pulse Generator in the manual)

Parameters
  • loops (int): The number of loops
Returns
  • int: The number of loops
def avail_loops_min(self) -> int:
211    def avail_loops_min(self) -> int:
212        """
213        Returns the minimum number of loops of the pulse generator’s output pulses. (see register 'SPC_XIO_PULSEGEN_AVAILLOOPS_MIN' in chapter `Pulse Generator` in the manual)
214
215        Returns
216        -------
217        int
218            The available minimal number of loops
219        """
220        return self.card.get_i(SPC_XIO_PULSEGEN_AVAILLOOPS_MIN)

Returns the minimum number of loops of the pulse generator’s output pulses. (see register 'SPC_XIO_PULSEGEN_AVAILLOOPS_MIN' in chapter Pulse Generator in the manual)

Returns
  • int: The available minimal number of loops
def avail_loops_max(self) -> int:
222    def avail_loops_max(self) -> int:
223        """
224        Returns the maximum number of loops of the pulse generator’s output pulses. (see register 'SPC_XIO_PULSEGEN_AVAILLOOPS_MAX' in chapter `Pulse Generator` in the manual)
225
226        Returns
227        -------
228        int
229            The available maximal number of loops
230        """
231        return self.card.get_i(SPC_XIO_PULSEGEN_AVAILLOOPS_MAX)

Returns the maximum number of loops of the pulse generator’s output pulses. (see register 'SPC_XIO_PULSEGEN_AVAILLOOPS_MAX' in chapter Pulse Generator in the manual)

Returns
  • int: The available maximal number of loops
def avail_loops_step(self) -> int:
233    def avail_loops_step(self) -> int:
234        """
235        Returns the step size of the pulse generator’s output pulses. (see register 'SPC_XIO_PULSEGEN_AVAILLOOPS_STEP' in chapter `Pulse Generator` in the manual)
236
237        Returns
238        -------
239        int
240            Returns the step size when defining the repetition of pulse generator’s output.
241        """
242        return self.card.get_i(SPC_XIO_PULSEGEN_AVAILLOOPS_STEP)

Returns the step size of the pulse generator’s output pulses. (see register 'SPC_XIO_PULSEGEN_AVAILLOOPS_STEP' in chapter Pulse Generator in the manual)

Returns
  • int: Returns the step size when defining the repetition of pulse generator’s output.
def delay(self, delay: int = None) -> int:
245    def delay(self, delay : int = None) -> int:
246        """
247        Set the delay of the pulse generator (see register 'SPC_XIO_PULSEGEN0_DELAY' in chapter `Pulse Generator` in the manual)
248
249        Parameters
250        ----------
251        delay : int
252            The delay in clock cycles
253        
254        Returns
255        -------
256        int
257            The delay in clock cycles
258        """
259
260        if delay is not None:
261            self.card.set_i(SPC_XIO_PULSEGEN0_DELAY + self._reg_distance*self.pg_index, delay)
262        return self.card.get_i(SPC_XIO_PULSEGEN0_DELAY + self._reg_distance*self.pg_index)

Set the delay of the pulse generator (see register 'SPC_XIO_PULSEGEN0_DELAY' in chapter Pulse Generator in the manual)

Parameters
  • delay (int): The delay in clock cycles
Returns
  • int: The delay in clock cycles
def avail_delay_min(self) -> int:
264    def avail_delay_min(self) -> int:
265        """
266        Returns the minimum delay of the pulse generator’s output pulses in clock cycles. (see register 'SPC_XIO_PULSEGEN_AVAILDELAY_MIN' in chapter `Pulse Generator` in the manual)
267
268        Returns
269        -------
270        int
271            The available minimal delay in clock cycles
272        """
273        return self.card.get_i(SPC_XIO_PULSEGEN_AVAILDELAY_MIN)

Returns the minimum delay of the pulse generator’s output pulses in clock cycles. (see register 'SPC_XIO_PULSEGEN_AVAILDELAY_MIN' in chapter Pulse Generator in the manual)

Returns
  • int: The available minimal delay in clock cycles
def avail_delay_max(self) -> int:
275    def avail_delay_max(self) -> int:
276        """
277        Returns the maximum delay of the pulse generator’s output pulses in clock cycles. (see register 'SPC_XIO_PULSEGEN_AVAILDELAY_MAX' in chapter `Pulse Generator` in the manual)
278
279        Returns
280        -------
281        int
282            The available maximal delay in clock cycles
283        """
284        return self.card.get_i(SPC_XIO_PULSEGEN_AVAILDELAY_MAX)

Returns the maximum delay of the pulse generator’s output pulses in clock cycles. (see register 'SPC_XIO_PULSEGEN_AVAILDELAY_MAX' in chapter Pulse Generator in the manual)

Returns
  • int: The available maximal delay in clock cycles
def avail_delay_step(self) -> int:
286    def avail_delay_step(self) -> int:
287        """
288        Returns the step size of the delay of the pulse generator’s output pulses in clock cycles. (see register 'SPC_XIO_PULSEGEN_AVAILDELAY_STEP' in chapter `Pulse Generator` in the manual)
289
290        Returns
291        -------
292        int
293            The available step size of the delay in clock cycles
294        """
295        return self.card.get_i(SPC_XIO_PULSEGEN_AVAILDELAY_STEP)

Returns the step size of the delay of the pulse generator’s output pulses in clock cycles. (see register 'SPC_XIO_PULSEGEN_AVAILDELAY_STEP' in chapter Pulse Generator in the manual)

Returns
  • int: The available step size of the delay in clock cycles
def mux1(self, mux: int = None) -> int:
298    def mux1(self, mux : int = None) -> int:
299        """
300        Set the trigger mux 1 of the pulse generator (see register 'SPC_XIO_PULSEGEN0_MUX1_SRC' in chapter `Pulse Generator` in the manual)
301
302        Parameters
303        ----------
304        mux : int
305            The trigger mux 1
306        
307        Returns
308        -------
309        int
310            The trigger mux 1
311        """
312
313        if mux is not None:
314            self.card.set_i(SPC_XIO_PULSEGEN0_MUX1_SRC + self._reg_distance*self.pg_index, mux)
315        return self.card.get_i(SPC_XIO_PULSEGEN0_MUX1_SRC + self._reg_distance*self.pg_index)

Set the trigger mux 1 of the pulse generator (see register 'SPC_XIO_PULSEGEN0_MUX1_SRC' in chapter Pulse Generator in the manual)

Parameters
  • mux (int): The trigger mux 1
Returns
  • int: The trigger mux 1
def mux2(self, mux: int = None) -> int:
317    def mux2(self, mux : int = None) -> int:
318        """
319        Set the trigger mux 2 of the pulse generator (see register 'SPC_XIO_PULSEGEN0_MUX2_SRC' in chapter `Pulse Generator` in the manual)
320
321        Parameters
322        ----------
323        mux : int
324            The trigger mux 2
325
326        Returns
327        -------
328        int
329            The trigger mux 2
330        """
331
332        if mux is not None:
333            self.card.set_i(SPC_XIO_PULSEGEN0_MUX2_SRC + self._reg_distance*self.pg_index, mux)
334        return self.card.get_i(SPC_XIO_PULSEGEN0_MUX2_SRC + self._reg_distance*self.pg_index)

Set the trigger mux 2 of the pulse generator (see register 'SPC_XIO_PULSEGEN0_MUX2_SRC' in chapter Pulse Generator in the manual)

Parameters
  • mux (int): The trigger mux 2
Returns
  • int: The trigger mux 2
def config(self, config: int = None) -> int:
336    def config(self, config : int = None) -> int:
337        """
338        Set the configuration of the pulse generator (see register 'SPC_XIO_PULSEGEN0_CONFIG' in chapter `Pulse Generator` in the manual)
339
340        Parameters
341        ----------
342        config : int
343            The configuration of the pulse generator
344
345        Returns
346        -------
347        int
348            The configuration of the pulse generator
349        """
350
351        if config is not None:
352            self.card.set_i(SPC_XIO_PULSEGEN0_CONFIG + self._reg_distance*self.pg_index, config)
353        return self.card.get_i(SPC_XIO_PULSEGEN0_CONFIG + self._reg_distance*self.pg_index)

Set the configuration of the pulse generator (see register 'SPC_XIO_PULSEGEN0_CONFIG' in chapter Pulse Generator in the manual)

Parameters
  • config (int): The configuration of the pulse generator
Returns
  • int: The configuration of the pulse generator
def pulse_period( self, period: pint.registry.Quantity = None, return_unit: pint.registry.Unit = <Unit('second')>) -> pint.registry.Quantity:
371    def pulse_period(self, period : pint.Quantity = None, return_unit : pint.Unit = units.s) -> pint.Quantity:
372        """
373        Set the period length of the pulse generator signal in a time unit
374
375        Parameters
376        ----------
377        period : pint.Quantity
378            The period length in seconds
379        
380        Returns
381        -------
382        pint.Quantity
383            The period length in seconds
384        """
385
386        if period is not None:
387            if isinstance(period, pint.Quantity):
388                period = int((period * self._get_clock(units.Hz)).to_base_units().magnitude)
389            else:
390                raise ValueError("The period must be a pint.Quantity")
391            self.period_length(period)
392        return_value = self.period_length()
393        return_value = UnitConversion.to_unit((return_value / self._get_clock(units.Hz)), return_unit)
394        return return_value

Set the period length of the pulse generator signal in a time unit

Parameters
  • period (pint.Quantity): The period length in seconds
Returns
  • pint.Quantity: The period length in seconds
def repetition_rate( self, rate: pint.registry.Quantity = None, return_unit: pint.registry.Unit = <Unit('hertz')>) -> pint.registry.Quantity:
396    def repetition_rate(self, rate : pint.Quantity = None, return_unit : pint.Unit = units.Hz) -> pint.Quantity:
397        """
398        Set the repetition rate of the pulse generator signal in a frequency unit
399
400        Parameters
401        ----------
402        rate : pint.Quantity
403            The repetition rate in Hz
404        
405        Returns
406        -------
407        pint.Quantity
408            The repetition rate in Hz
409        """
410
411        if rate is not None:
412            if isinstance(rate, pint.Quantity):
413                period = int(np.rint((self._get_clock(units.Hz) / rate).to_base_units().magnitude))
414            else:
415                raise ValueError("The rate must be a pint.Quantity")
416            self.period_length(period)
417        return_value = self.period_length()
418        return_value = UnitConversion.to_unit((self._get_clock(units.Hz) / return_value), return_unit)
419        return return_value

Set the repetition rate of the pulse generator signal in a frequency unit

Parameters
  • rate (pint.Quantity): The repetition rate in Hz
Returns
  • pint.Quantity: The repetition rate in Hz
def pulse_length( self, length: pint.registry.Quantity, return_unit: pint.registry.Unit = <Unit('second')>) -> pint.registry.Quantity:
421    def pulse_length(self, length : pint.Quantity, return_unit : pint.Unit = units.s) -> pint.Quantity:
422        """
423        Set the pulse length of the pulse generator signal in a time unit
424
425        Parameters
426        ----------
427        length : pint.Quantity
428            The pulse length in seconds
429        
430        Returns
431        -------
432        pint.Quantity
433            The pulse length in seconds
434        """
435
436        if length is not None:
437            if isinstance(length, pint.Quantity):
438                length = int((length * self._get_clock(units.Hz)).to_base_units().magnitude)
439            else:
440                raise ValueError("The length must be a pint.Quantity")
441            self.high_length(length)
442        return_value = self.high_length()
443        return_value = UnitConversion.to_unit((return_value / self._get_clock(units.Hz)), return_unit)
444        return return_value

Set the pulse length of the pulse generator signal in a time unit

Parameters
  • length (pint.Quantity): The pulse length in seconds
Returns
  • pint.Quantity: The pulse length in seconds
def duty_cycle( self, duty_cycle: pint.registry.Quantity = None, return_unit: pint.registry.Unit = <Unit('percent')>) -> pint.registry.Quantity:
446    def duty_cycle(self, duty_cycle : pint.Quantity = None, return_unit : pint.Unit = units.percent) -> pint.Quantity:
447        """
448        Set the duty cycle of the pulse generator signal in a percentage unit
449
450        Parameters
451        ----------
452        duty_cycle : pint.Quantity
453            The duty cycle in percentage
454        
455        Returns
456        -------
457        pint.Quantity
458            The duty cycle in percentage
459        """
460
461        period_length = self.period_length()
462        if duty_cycle is not None:
463            if isinstance(duty_cycle, pint.Quantity):
464                high_length = int(np.rint(period_length * duty_cycle))
465            else:
466                raise ValueError("The cycle must be a pint.Quantity")
467            self.high_length(high_length)
468        return_value = self.high_length()
469        return_value = UnitConversion.to_unit((return_value / period_length) * 100 * units.percent, return_unit)
470        return return_value

Set the duty cycle of the pulse generator signal in a percentage unit

Parameters
  • duty_cycle (pint.Quantity): The duty cycle in percentage
Returns
  • pint.Quantity: The duty cycle in percentage
def start_delay( self, delay: pint.registry.Unit = None, return_unit: pint.registry.Unit = <Unit('second')>) -> pint.registry.Unit:
472    def start_delay(self, delay : pint.Unit = None, return_unit : pint.Unit = units.s) -> pint.Unit:
473        """
474        Set the start delay of the pulse generator signal in a time unit
475
476        Parameters
477        ----------
478        delay : pint.Unit
479            The start delay in a pint quantity with time unit
480        
481        Returns
482        -------
483        pint.Unit
484            The start delay in a pint quantity with time unit
485        """
486
487        if delay is not None:
488            if isinstance(delay, pint.Quantity):
489                delay = int((delay * self._get_clock(units.Hz)).to_base_units().magnitude)
490            else:
491                raise ValueError("The delay must be a pint.Quantity")
492        return_value = self.delay(delay)
493        return_value = UnitConversion.to_unit((return_value / self._get_clock(units.Hz)), return_unit)
494        return return_value

Set the start delay of the pulse generator signal in a time unit

Parameters
  • delay (pint.Unit): The start delay in a pint quantity with time unit
Returns
  • pint.Unit: The start delay in a pint quantity with time unit
def repetitions(self, loops: int = None) -> int:
192    def num_loops(self, loops : int = None) -> int:
193        """
194        Set the number of loops of a single period on the pulse generator (see register 'SPC_XIO_PULSEGEN0_LOOPS' in chapter `Pulse Generator` in the manual)
195
196        Parameters
197        ----------
198        loops : int
199            The number of loops
200        
201        Returns
202        -------
203        int
204            The number of loops
205        """
206
207        if loops is not None:
208            self.card.set_i(SPC_XIO_PULSEGEN0_LOOPS + self._reg_distance*self.pg_index, loops)
209        return self.card.get_i(SPC_XIO_PULSEGEN0_LOOPS + self._reg_distance*self.pg_index)

Set the number of loops of a single period on the pulse generator (see register 'SPC_XIO_PULSEGEN0_LOOPS' in chapter Pulse Generator in the manual)

Parameters
  • loops (int): The number of loops
Returns
  • int: The number of loops
def start_condition_state_signal(self, signal: int = 0, invert: bool = False) -> int:
498    def start_condition_state_signal(self, signal : int = 0, invert : bool = False) -> int:
499        """
500        Set the start condition state signal of the pulse generator (see register 'SPC_XIO_PULSEGEN0_MUX1' in chapter `Pulse Generator` in the manual)
501
502        NOTE
503        ----
504        The Pulse Generator is started when the combined signal of both start condition signals are true and a rising edge
505        is detected. The invert parameter inverts the start condition state signal.
506        
507        Parameters
508        ----------
509        signal : int
510            The start condition state signal
511        invert : bool
512            Invert the start condition state signal
513        
514        Returns
515        -------
516        int
517            The start condition state signal
518        """
519
520        return_signal = self.mux1(signal)
521        return_invert = self.config()
522        if invert:
523            return_invert |= SPCM_PULSEGEN_CONFIG_MUX1_INVERT
524        else:
525            return_invert &= ~SPCM_PULSEGEN_CONFIG_MUX1_INVERT
526        return_invert = self.config(return_invert)
527        return return_signal, ((return_invert & SPCM_PULSEGEN_CONFIG_MUX1_INVERT) != 0)

Set the start condition state signal of the pulse generator (see register 'SPC_XIO_PULSEGEN0_MUX1' in chapter Pulse Generator in the manual)

NOTE

The Pulse Generator is started when the combined signal of both start condition signals are true and a rising edge is detected. The invert parameter inverts the start condition state signal.

Parameters
  • signal (int): The start condition state signal
  • invert (bool): Invert the start condition state signal
Returns
  • int: The start condition state signal
def start_condition_trigger_signal(self, signal: int = 0, invert: bool = False) -> int:
529    def start_condition_trigger_signal(self, signal : int = 0, invert : bool = False) -> int:
530        """
531        Set the start condition trigger signal of the pulse generator (see register 'SPC_XIO_PULSEGEN0_MUX2' in chapter `Pulse Generator` in the manual)
532
533        NOTE
534        ----
535        The Pulse Generator is started when the combined signal of both start condition signals are true and a rising edge
536        is detected. The invert parameter inverts the start condition state signal.
537        
538        Parameters
539        ----------
540        signal : int
541            The start condition trigger signal
542        invert : bool
543            Invert the start condition trigger signal
544        
545        Returns
546        -------
547        int
548            The start condition trigger signal
549        """
550
551        return_signal = self.mux2(signal)
552        return_invert = self.config()
553        if invert:
554            return_invert |= SPCM_PULSEGEN_CONFIG_MUX2_INVERT
555        else:
556            return_invert &= ~SPCM_PULSEGEN_CONFIG_MUX2_INVERT
557        return_invert = self.config(return_invert)
558        return return_signal, ((return_invert & SPCM_PULSEGEN_CONFIG_MUX2_INVERT) != 0)

Set the start condition trigger signal of the pulse generator (see register 'SPC_XIO_PULSEGEN0_MUX2' in chapter Pulse Generator in the manual)

NOTE

The Pulse Generator is started when the combined signal of both start condition signals are true and a rising edge is detected. The invert parameter inverts the start condition state signal.

Parameters
  • signal (int): The start condition trigger signal
  • invert (bool): Invert the start condition trigger signal
Returns
  • int: The start condition trigger signal
def invert_start_condition(self, invert: bool = None) -> bool:
560    def invert_start_condition(self, invert : bool = None) -> bool:
561        """
562        Invert the start condition of the pulse generator
563
564        Parameters
565        ----------
566        invert : bool
567            Invert the start condition
568        
569        Returns
570        -------
571        bool
572            The start condition inversion
573        """
574
575        if invert is not None:
576            return_invert = self.config()
577            if invert:
578                return_invert |= SPCM_PULSEGEN_CONFIG_INVERT
579            else:
580                return_invert &= ~SPCM_PULSEGEN_CONFIG_INVERT
581            self.config(return_invert)
582        return ((self.config() & SPCM_PULSEGEN_CONFIG_INVERT) != 0)

Invert the start condition of the pulse generator

Parameters
  • invert (bool): Invert the start condition
Returns
  • bool: The start condition inversion
class PulseGenerators(spcm.CardFunctionality):
584class PulseGenerators(CardFunctionality):
585    """
586    a higher-level abstraction of the CardFunctionality class to implement Pulse generator functionality
587
588    Parameters
589    ----------
590    generators : list[PulseGenerator]
591        a list of pulse generators
592    num_generators : int
593        the number of pulse generators on the card
594    """
595    
596    generators : list[PulseGenerator]
597    num_generators = 4
598
599    def __init__(self, card : Card, enable : int = 0, *args, **kwargs) -> None:
600        """
601        The constructor of the PulseGenerators class
602
603        Parameters
604        ----------
605        card : Card
606            the card object that is used by the functionality
607        enable : int or bool
608            Enable or disable (all) the different pulse generators, by default all are turned off
609        
610        Raises
611        ------
612        SpcmException
613        """
614
615        super().__init__(card, *args, **kwargs)
616        # Check for the pulse generator option on the card
617        features = self.card.get_i(SPC_PCIEXTFEATURES)
618        if features & SPCM_FEAT_EXTFW_PULSEGEN:
619            self.card._print(f"Pulse generator option available")
620        else:
621            raise SpcmException(text="This card doesn't have the pulse generator functionality installed. Please contact sales@spec.de to get more information about this functionality.")
622        
623        self.load()
624        self.enable(enable)
625    
626    def load(self) -> None:
627        """Load the pulse generators"""
628        self.generators = [PulseGenerator(self.card, i) for i in range(self.num_generators)]
629
630    # TODO in the next driver release there will be a register to get the number of pulse generators
631    def get_num_generators(self) -> int:
632        """
633        Get the number of pulse generators on the card
634
635        Returns
636        -------
637        int
638            The number of pulse generators
639        """
640
641        return self.num_generators
642    
643    def __str__(self) -> str:
644        """
645        String representation of the PulseGenerators class
646    
647        Returns
648        -------
649        str
650            String representation of the PulseGenerators class
651        """
652        
653        return f"PulseGenerators(card={self.card})"
654    
655    __repr__ = __str__
656    
657
658    def __iter__(self) -> "PulseGenerators":
659        """Define this class as an iterator"""
660        return self
661    
662    def __getitem__(self, index : int) -> PulseGenerator:
663        """
664        Get the pulse generator at the given index
665
666        Parameters
667        ----------
668        index : int
669            The index of the pulse generator
670        
671        Returns
672        -------
673        PulseGenerator
674            The pulse generator at the given index
675        """
676
677        return self.generators[index]
678    
679    _generator_iterator_index = -1
680    def __next__(self) -> PulseGenerator:
681        """
682        This method is called when the next element is requested from the iterator
683
684        Returns
685        -------
686        PulseGenerator
687            the next available pulse generator
688        
689        Raises
690        ------
691        StopIteration
692        """
693        self._generator_iterator_index += 1
694        if self._generator_iterator_index >= len(self.channels):
695            self._generator_iterator_index = -1
696            raise StopIteration
697        return self.generators[self._generator_iterator_index]
698    
699    def __len__(self) -> int:
700        """Returns the number of available pulse generators"""
701        return len(self.generators)
702    
703    def write_setup(self) -> None:
704        """Write the setup to the card"""
705        self.card.write_setup()
706
707    # The pulse generator can be enabled or disabled
708    def enable(self, enable : int = None) -> int:
709        """
710        Enable or disable (all) the pulse generators (see register 'SPC_XIO_PULSEGEN_ENABLE' in chapter `Pulse Generator` in the manual)
711
712        Parameters
713        ----------
714        enable : int or bool
715            Enable or disable (all) the different pulse generators, by default all are turned off
716        
717        Returns
718        -------
719        int
720            The enable state of the pulse generators
721        """
722
723        
724        if isinstance(enable, bool):
725            enable = (1 << self.num_generators) - 1 if enable else 0
726        if enable is not None:
727            self.card.set_i(SPC_XIO_PULSEGEN_ENABLE, enable)
728        return self.card.get_i(SPC_XIO_PULSEGEN_ENABLE)
729    
730    def cmd(self, cmd : int) -> None:
731        """
732        Execute a command on the pulse generator (see register 'SPC_XIO_PULSEGEN_COMMAND' in chapter `Pulse Generator` in the manual)
733
734        Parameters
735        ----------
736        cmd : int
737            The command to execute
738        """
739        self.card.set_i(SPC_XIO_PULSEGEN_COMMAND, cmd)
740    
741    def force(self) -> None:
742        """
743        Generate a single rising edge, that is common for all pulse generator engines. This allows to start/trigger the output 
744        of all enabled pulse generators synchronously by issuing a software command. (see register 'SPC_XIO_PULSEGEN_COMMAND' in chapter `Pulse Generator` in the manual)
745        """
746        self.cmd(SPCM_PULSEGEN_CMD_FORCE)
747    
748    def write_setup(self) -> None:
749        """Write the setup of the pulse generator to the card"""
750        self.card.write_setup()
751    
752    # The clock rate in Hz of the pulse generator
753    def get_clock(self) -> int:
754        """
755        Get the clock rate of the pulse generator (see register 'SPC_XIO_PULSEGEN_CLOCK' in chapter `Pulse Generator` in the manual)
756
757        Returns
758        -------
759        int
760            The clock rate in Hz
761        """
762        return self.card.get_i(SPC_XIO_PULSEGEN_CLOCK)

a higher-level abstraction of the CardFunctionality class to implement Pulse generator functionality

Parameters
  • generators (list[PulseGenerator]): a list of pulse generators
  • num_generators (int): the number of pulse generators on the card
PulseGenerators(card: Card, enable: int = 0, *args, **kwargs)
599    def __init__(self, card : Card, enable : int = 0, *args, **kwargs) -> None:
600        """
601        The constructor of the PulseGenerators class
602
603        Parameters
604        ----------
605        card : Card
606            the card object that is used by the functionality
607        enable : int or bool
608            Enable or disable (all) the different pulse generators, by default all are turned off
609        
610        Raises
611        ------
612        SpcmException
613        """
614
615        super().__init__(card, *args, **kwargs)
616        # Check for the pulse generator option on the card
617        features = self.card.get_i(SPC_PCIEXTFEATURES)
618        if features & SPCM_FEAT_EXTFW_PULSEGEN:
619            self.card._print(f"Pulse generator option available")
620        else:
621            raise SpcmException(text="This card doesn't have the pulse generator functionality installed. Please contact sales@spec.de to get more information about this functionality.")
622        
623        self.load()
624        self.enable(enable)

The constructor of the PulseGenerators class

Parameters
  • card (Card): the card object that is used by the functionality
  • enable (int or bool): Enable or disable (all) the different pulse generators, by default all are turned off
Raises
  • SpcmException
generators: list[PulseGenerator]
num_generators = 4
def load(self) -> None:
626    def load(self) -> None:
627        """Load the pulse generators"""
628        self.generators = [PulseGenerator(self.card, i) for i in range(self.num_generators)]

Load the pulse generators

def get_num_generators(self) -> int:
631    def get_num_generators(self) -> int:
632        """
633        Get the number of pulse generators on the card
634
635        Returns
636        -------
637        int
638            The number of pulse generators
639        """
640
641        return self.num_generators

Get the number of pulse generators on the card

Returns
  • int: The number of pulse generators
def write_setup(self) -> None:
748    def write_setup(self) -> None:
749        """Write the setup of the pulse generator to the card"""
750        self.card.write_setup()

Write the setup of the pulse generator to the card

def enable(self, enable: int = None) -> int:
708    def enable(self, enable : int = None) -> int:
709        """
710        Enable or disable (all) the pulse generators (see register 'SPC_XIO_PULSEGEN_ENABLE' in chapter `Pulse Generator` in the manual)
711
712        Parameters
713        ----------
714        enable : int or bool
715            Enable or disable (all) the different pulse generators, by default all are turned off
716        
717        Returns
718        -------
719        int
720            The enable state of the pulse generators
721        """
722
723        
724        if isinstance(enable, bool):
725            enable = (1 << self.num_generators) - 1 if enable else 0
726        if enable is not None:
727            self.card.set_i(SPC_XIO_PULSEGEN_ENABLE, enable)
728        return self.card.get_i(SPC_XIO_PULSEGEN_ENABLE)

Enable or disable (all) the pulse generators (see register 'SPC_XIO_PULSEGEN_ENABLE' in chapter Pulse Generator in the manual)

Parameters
  • enable (int or bool): Enable or disable (all) the different pulse generators, by default all are turned off
Returns
  • int: The enable state of the pulse generators
def cmd(self, cmd: int) -> None:
730    def cmd(self, cmd : int) -> None:
731        """
732        Execute a command on the pulse generator (see register 'SPC_XIO_PULSEGEN_COMMAND' in chapter `Pulse Generator` in the manual)
733
734        Parameters
735        ----------
736        cmd : int
737            The command to execute
738        """
739        self.card.set_i(SPC_XIO_PULSEGEN_COMMAND, cmd)

Execute a command on the pulse generator (see register 'SPC_XIO_PULSEGEN_COMMAND' in chapter Pulse Generator in the manual)

Parameters
  • cmd (int): The command to execute
def force(self) -> None:
741    def force(self) -> None:
742        """
743        Generate a single rising edge, that is common for all pulse generator engines. This allows to start/trigger the output 
744        of all enabled pulse generators synchronously by issuing a software command. (see register 'SPC_XIO_PULSEGEN_COMMAND' in chapter `Pulse Generator` in the manual)
745        """
746        self.cmd(SPCM_PULSEGEN_CMD_FORCE)

Generate a single rising edge, that is common for all pulse generator engines. This allows to start/trigger the output of all enabled pulse generators synchronously by issuing a software command. (see register 'SPC_XIO_PULSEGEN_COMMAND' in chapter Pulse Generator in the manual)

def get_clock(self) -> int:
753    def get_clock(self) -> int:
754        """
755        Get the clock rate of the pulse generator (see register 'SPC_XIO_PULSEGEN_CLOCK' in chapter `Pulse Generator` in the manual)
756
757        Returns
758        -------
759        int
760            The clock rate in Hz
761        """
762        return self.card.get_i(SPC_XIO_PULSEGEN_CLOCK)

Get the clock rate of the pulse generator (see register 'SPC_XIO_PULSEGEN_CLOCK' in chapter Pulse Generator in the manual)

Returns
  • int: The clock rate in Hz
class Multi(spcm.DataTransfer):
 16class Multi(DataTransfer):
 17    """a high-level class to control Multiple Recording and Replay functionality on Spectrum Instrumentation cards
 18
 19    For more information about what setups are available, please have a look at the user manual
 20    for your specific card.
 21
 22    """
 23
 24    # Private
 25    _segment_size : int
 26    _num_segments : int
 27
 28    def __init__(self, card, *args, **kwargs) -> None:
 29        super().__init__(card, *args, **kwargs)
 30        self.pre_trigger = None
 31        self._segment_size = 0
 32        self._num_segments = 0
 33
 34    def segment_samples(self, segment_size : int = None) -> None:
 35        """
 36        Sets the memory size in samples per channel. The memory size setting must be set before transferring 
 37        data to the card. (see register `SPC_MEMSIZE` in the manual)
 38        
 39        Parameters
 40        ----------
 41        segment_size : int | pint.Quantity
 42            the size of a single segment in memory in Samples
 43        """
 44
 45        if segment_size is not None:
 46            segment_size = UnitConversion.convert(segment_size, units.S, int)
 47            self.card.set_i(SPC_SEGMENTSIZE, segment_size)
 48        segment_size = self.card.get_i(SPC_SEGMENTSIZE)
 49        self._segment_size = segment_size
 50    
 51    def post_trigger(self, num_samples : int = None) -> int:
 52        """
 53        Set the number of post trigger samples (see register `SPC_POSTTRIGGER` in the manual)
 54        
 55        Parameters
 56        ----------
 57        num_samples : int | pint.Quantity
 58            the number of post trigger samples
 59        
 60        Returns
 61        -------
 62        int
 63            the number of post trigger samples
 64        """
 65
 66        post_trigger = super().post_trigger(num_samples)
 67        self._pre_trigger = self._segment_size - post_trigger
 68        return post_trigger
 69
 70    def allocate_buffer(self, segment_samples : int, num_segments : int = None) -> None:
 71        """
 72        Memory allocation for the buffer that is used for communicating with the card
 73
 74        Parameters
 75        ----------
 76        segment_samples : int | pint.Quantity
 77            use the number of samples and get the number of active channels and bytes per samples directly from the card
 78        num_segments : int = None
 79            the number of segments that are used for the multiple recording mode
 80        """
 81        
 82        segment_samples = UnitConversion.convert(segment_samples, units.S, int)
 83        num_segments = UnitConversion.convert(num_segments, units.S, int)
 84        self.segment_samples(segment_samples)
 85        if num_segments is None:
 86            self._num_segments = self._memory_size // segment_samples
 87        else:
 88            self._num_segments = num_segments
 89        super().allocate_buffer(segment_samples * self._num_segments)
 90        num_channels = self.card.active_channels()
 91        if self.bits_per_sample > 1 and not self._12bit_mode:
 92            self.buffer = self.buffer.reshape((self._num_segments, segment_samples, num_channels), order='C') # index definition: [segment, sample, channel] !
 93    
 94    def time_data(self, total_num_samples : int = None) -> npt.NDArray:
 95        """
 96        Get the time array for the data buffer
 97        
 98        Returns
 99        -------
100        numpy array
101            the time array
102        """
103
104        sample_rate = self._sample_rate()
105        if total_num_samples is None:
106            total_num_samples = self._buffer_samples // self._num_segments
107        total_num_samples = UnitConversion.convert(total_num_samples, units.Sa, int)
108        return (np.arange(total_num_samples) - self._pre_trigger) / sample_rate
109
110    def unpack_12bit_buffer(self) -> npt.NDArray[np.int_]:
111        """
112        Unpacks the 12-bit packed data to 16-bit data
113
114        Returns
115        -------
116        npt.NDArray[np.int_]
117            the unpacked data
118        """
119        buffer_12bit = super().unpack_12bit_buffer()
120        return buffer_12bit.reshape((self._num_segments, self.num_channels, self._segment_size), order='C')
121
122    
123    def __next__(self) -> npt.ArrayLike:
124        """
125        This method is called when the next element is requested from the iterator
126
127        Returns
128        -------
129        npt.ArrayLike
130            the next data block
131        
132        Raises
133        ------
134        StopIteration
135        """
136        
137        timeout_counter = 0
138        # notify the card that data is available or read, but only after the first block
139        if self._current_samples >= self._notify_samples:
140            self.avail_card_len(self._notify_samples)
141        while True:
142            try:
143                self.wait_dma()
144            except SpcmTimeout:
145                self.card._print("... Timeout ({})".format(timeout_counter), end='\r')
146                timeout_counter += 1
147                if timeout_counter > self._max_timeout:
148                    raise StopIteration
149            else:
150                user_len = self.avail_user_len()
151                user_pos = self.avail_user_pos()
152
153                current_segment = user_pos // self._segment_size
154                current_pos_in_segment = user_pos % self._segment_size
155                final_segment = ((user_pos+user_len) // self._segment_size)
156                final_pos_in_segment = (user_pos+user_len) % self._segment_size
157
158                self.card._print("NumSamples = {}, CurrentSegment = {}, CurrentPos = {},  FinalSegment = {}, FinalPos = {}, UserLen = {}".format(self._notify_samples, current_segment, current_pos_in_segment, final_segment, final_pos_in_segment, user_len))
159
160                self._current_samples += self._notify_samples
161                if self._to_transfer_samples != 0 and self._to_transfer_samples < self._current_samples:
162                    raise StopIteration
163
164                fill_size = self.fill_size_promille()
165                self.card._print("Fill size: {}%  Pos:{:08x} Len:{:08x} Total:{:.2f} MiS / {:.2f} MiS".format(fill_size/10, user_pos, user_len, self._current_samples / MEBI(1), self._to_transfer_samples / MEBI(1)), end='\r')
166
167                return self.buffer[current_segment:final_segment, :, :]

a high-level class to control Multiple Recording and Replay functionality on Spectrum Instrumentation cards

For more information about what setups are available, please have a look at the user manual for your specific card.

Multi(card, *args, **kwargs)
28    def __init__(self, card, *args, **kwargs) -> None:
29        super().__init__(card, *args, **kwargs)
30        self.pre_trigger = None
31        self._segment_size = 0
32        self._num_segments = 0

Initialize the DataTransfer object with a card object and additional arguments

Parameters
  • card (Card): the card object that is used for the data transfer
  • *args (list): list of additional arguments
  • **kwargs (dict): dictionary of additional keyword arguments
def pre_trigger(self, num_samples: int = None) -> int:
329    def pre_trigger(self, num_samples : int = None) -> int:
330        """
331        Set the number of pre trigger samples (see register `SPC_PRETRIGGER` in the manual)
332        
333        Parameters
334        ----------
335        num_samples : int | pint.Quantity
336            the number of pre trigger samples
337        
338        Returns
339        -------
340        int
341            the number of pre trigger samples
342        """
343
344        if num_samples is not None:
345            num_samples = UnitConversion.convert(num_samples, units.Sa, int)
346            self.card.set_i(SPC_PRETRIGGER, num_samples)
347        self._pre_trigger = self.card.get_i(SPC_PRETRIGGER)
348        return self._pre_trigger

Set the number of pre trigger samples (see register SPC_PRETRIGGER in the manual)

Parameters
  • num_samples (int | pint.Quantity): the number of pre trigger samples
Returns
  • int: the number of pre trigger samples
def segment_samples(self, segment_size: int = None) -> None:
34    def segment_samples(self, segment_size : int = None) -> None:
35        """
36        Sets the memory size in samples per channel. The memory size setting must be set before transferring 
37        data to the card. (see register `SPC_MEMSIZE` in the manual)
38        
39        Parameters
40        ----------
41        segment_size : int | pint.Quantity
42            the size of a single segment in memory in Samples
43        """
44
45        if segment_size is not None:
46            segment_size = UnitConversion.convert(segment_size, units.S, int)
47            self.card.set_i(SPC_SEGMENTSIZE, segment_size)
48        segment_size = self.card.get_i(SPC_SEGMENTSIZE)
49        self._segment_size = segment_size

Sets the memory size in samples per channel. The memory size setting must be set before transferring data to the card. (see register SPC_MEMSIZE in the manual)

Parameters
  • segment_size (int | pint.Quantity): the size of a single segment in memory in Samples
def post_trigger(self, num_samples: int = None) -> int:
51    def post_trigger(self, num_samples : int = None) -> int:
52        """
53        Set the number of post trigger samples (see register `SPC_POSTTRIGGER` in the manual)
54        
55        Parameters
56        ----------
57        num_samples : int | pint.Quantity
58            the number of post trigger samples
59        
60        Returns
61        -------
62        int
63            the number of post trigger samples
64        """
65
66        post_trigger = super().post_trigger(num_samples)
67        self._pre_trigger = self._segment_size - post_trigger
68        return post_trigger

Set the number of post trigger samples (see register SPC_POSTTRIGGER in the manual)

Parameters
  • num_samples (int | pint.Quantity): the number of post trigger samples
Returns
  • int: the number of post trigger samples
def allocate_buffer(self, segment_samples: int, num_segments: int = None) -> None:
70    def allocate_buffer(self, segment_samples : int, num_segments : int = None) -> None:
71        """
72        Memory allocation for the buffer that is used for communicating with the card
73
74        Parameters
75        ----------
76        segment_samples : int | pint.Quantity
77            use the number of samples and get the number of active channels and bytes per samples directly from the card
78        num_segments : int = None
79            the number of segments that are used for the multiple recording mode
80        """
81        
82        segment_samples = UnitConversion.convert(segment_samples, units.S, int)
83        num_segments = UnitConversion.convert(num_segments, units.S, int)
84        self.segment_samples(segment_samples)
85        if num_segments is None:
86            self._num_segments = self._memory_size // segment_samples
87        else:
88            self._num_segments = num_segments
89        super().allocate_buffer(segment_samples * self._num_segments)
90        num_channels = self.card.active_channels()
91        if self.bits_per_sample > 1 and not self._12bit_mode:
92            self.buffer = self.buffer.reshape((self._num_segments, segment_samples, num_channels), order='C') # index definition: [segment, sample, channel] !

Memory allocation for the buffer that is used for communicating with the card

Parameters
  • segment_samples (int | pint.Quantity): use the number of samples and get the number of active channels and bytes per samples directly from the card
  • num_segments (int = None): the number of segments that are used for the multiple recording mode
def time_data( self, total_num_samples: int = None) -> numpy.ndarray[typing.Any, numpy.dtype[+_ScalarType_co]]:
 94    def time_data(self, total_num_samples : int = None) -> npt.NDArray:
 95        """
 96        Get the time array for the data buffer
 97        
 98        Returns
 99        -------
100        numpy array
101            the time array
102        """
103
104        sample_rate = self._sample_rate()
105        if total_num_samples is None:
106            total_num_samples = self._buffer_samples // self._num_segments
107        total_num_samples = UnitConversion.convert(total_num_samples, units.Sa, int)
108        return (np.arange(total_num_samples) - self._pre_trigger) / sample_rate

Get the time array for the data buffer

Returns
  • numpy array: the time array
def unpack_12bit_buffer(self) -> numpy.ndarray[typing.Any, numpy.dtype[numpy.int64]]:
110    def unpack_12bit_buffer(self) -> npt.NDArray[np.int_]:
111        """
112        Unpacks the 12-bit packed data to 16-bit data
113
114        Returns
115        -------
116        npt.NDArray[np.int_]
117            the unpacked data
118        """
119        buffer_12bit = super().unpack_12bit_buffer()
120        return buffer_12bit.reshape((self._num_segments, self.num_channels, self._segment_size), order='C')

Unpacks the 12-bit packed data to 16-bit data

Returns
  • npt.NDArray[np.int_]: the unpacked data
class TimeStamp(spcm.DataTransfer):
 12class TimeStamp(DataTransfer):
 13    """a class to control Spectrum Instrumentation cards with the timestamp functionality
 14
 15    For more information about what setups are available, please have a look at the user manual
 16    for your specific card
 17
 18    Parameters
 19    ----------
 20    ts_mode : int
 21    transfer_mode : int
 22    bits_per_ts : int = 0
 23    bytes_per_ts : int = 16
 24    """
 25    ts_mode : int
 26    transfer_mode : int
 27
 28    bits_per_ts : int = 0
 29    bytes_per_ts : int = 16
 30    
 31    _notify_timestamps : int = 0
 32    _to_transfer_timestamps : int = 0
 33
 34    def __init__(self, card, *args, **kwargs) -> None:
 35        """
 36        Initialize the TimeStamp object with a card object
 37
 38        Parameters
 39        ----------
 40        card : Card
 41            a card object that is used to control the card
 42        """
 43        
 44        super().__init__(card, *args, **kwargs)
 45        self.buffer_type = SPCM_BUF_TIMESTAMP
 46        self.bits_per_ts = self.bytes_per_ts * 8
 47    
 48    def cmd(self, *args) -> None:
 49        """
 50        Execute spcm timestamp commands (see register 'SPC_TIMESTAMP_CMD' in chapter `Timestamp` in the manual)
 51    
 52        Parameters
 53        ----------
 54        *args : int
 55            The different timestamp command flags to be executed.
 56        """
 57
 58        cmd = 0
 59        for arg in args:
 60            cmd |= arg
 61        self.card.set_i(SPC_TIMESTAMP_CMD, cmd)
 62    
 63    def reset(self) -> None:
 64        """Reset the timestamp counter (see command 'SPC_TS_RESET' in chapter `Timestamp` in the manual)"""
 65        self.cmd(SPC_TS_RESET)
 66    
 67    def mode(self, mode : int, *args : list[int]) -> None:
 68        """
 69        Set the mode of the timestamp counter (see register 'SPC_TIMESTAMP_CMD' in chapter `Timestamp` in the manual)
 70
 71        Parameters
 72        ----------
 73        mode : int
 74            The mode of the timestamp counter
 75        *args : list[int]
 76            List of additional commands send with setting the mode
 77        """
 78        self.ts_mode = mode
 79        self.cmd(self.ts_mode, *args)
 80
 81    def notify_timestamps(self, notify_timestamps : int) -> None:
 82        """
 83        Set the number of timestamps to notify the user about
 84        
 85        Parameters
 86        ----------
 87        notify_timestamps : int
 88            the number of timestamps to notify the user about
 89        """
 90        self._notify_timestamps = notify_timestamps
 91    
 92    def allocate_buffer(self, num_timestamps : int) -> None:
 93        """
 94        Allocate the buffer for the timestamp data transfer
 95        
 96        Parameters
 97        ----------
 98        num_timestamps : int
 99            The number of timestamps to be allocated
100        """
101
102        self.buffer_size = num_timestamps * self.bytes_per_ts
103
104        dwMask = self._buffer_alignment - 1
105
106        sample_type = np.int64
107        item_size = sample_type(0).itemsize
108        # allocate a buffer (numpy array) for DMA transfer: a little bigger one to have room for address alignment
109        databuffer_unaligned = np.empty(((self._buffer_alignment + self.buffer_size) // item_size, ), dtype = sample_type)   # half byte count at int16 sample (// = integer division)
110        # two numpy-arrays may share the same memory: skip the begin up to the alignment boundary (ArrayVariable[SKIP_VALUE:])
111        # Address of data-memory from numpy-array: ArrayVariable.__array_interface__['data'][0]
112        start_pos_samples = ((self._buffer_alignment - (databuffer_unaligned.__array_interface__['data'][0] & dwMask)) // item_size)
113        self.buffer = databuffer_unaligned[start_pos_samples:start_pos_samples + (self.buffer_size // item_size)]   # byte address but int16 sample: therefore / 2
114        self.buffer = self.buffer.reshape((num_timestamps, 2), order='C') # array items per timestamp, because the maximum item size is 8 bytes = 64 bits
115    
116    def start_buffer_transfer(self, *args, direction=SPCM_DIR_CARDTOPC, notify_timestamps=0, transfer_offset=0, transfer_length=None) -> None:
117        """
118        Start the transfer of the timestamp data to the card
119        
120        Parameters
121        ----------
122        *args : list
123            list of additonal arguments that are added as flags to the start dma command
124        """
125        
126        notify_size = 0
127        if notify_timestamps: 
128            self._notify_timestamps = notify_timestamps
129        if self._notify_timestamps: 
130            notify_size = self._notify_timestamps * self.bytes_per_ts
131
132        if transfer_offset:
133            transfer_offset_bytes = transfer_offset * self.bytes_per_ts
134        else:
135            transfer_offset_bytes = 0
136
137        if transfer_length is not None: 
138            transfer_length_bytes = transfer_length * self.bytes_per_ts
139        else:
140            transfer_length_bytes = self.buffer_size
141
142
143        # we define the buffer for transfer and start the DMA transfer
144        self.card._print("Starting the Timestamp transfer and waiting until data is in board memory")
145        self._c_buffer = self.buffer.ctypes.data_as(c_void_p)
146        spcm_dwDefTransfer_i64(self.card._handle, self.buffer_type, direction, notify_size, self._c_buffer, transfer_offset_bytes, transfer_length_bytes)
147        cmd = 0
148        for arg in args:
149            cmd |= arg
150        self.card.cmd(cmd)
151        self.card._print("... timestamp data transfer started")
152    
153    def avail_card_len(self, num_timestamps : int) -> None:
154        """
155        Set the amount of timestamps that is available for reading of the timestamp buffer (see register 'SPC_TS_AVAIL_CARD_LEN' in chapter `Timestamp` in the manual)
156
157        Parameters
158        ----------
159        num_timestamps : int
160            the amount of timestamps that is available for reading
161        """
162        card_len = num_timestamps * self.bytes_per_ts
163        self.card.set_i(SPC_TS_AVAIL_CARD_LEN, card_len)
164    
165    def avail_user_pos(self) -> int:
166        """
167        Get the current position of the pointer in the timestamp buffer (see register 'SPC_TS_AVAIL_USER_POS' in chapter `Timestamp` in the manual)
168
169        Returns
170        -------
171        int
172            pointer position in timestamps
173        """
174        return self.card.get_i(SPC_TS_AVAIL_USER_POS) // self.bytes_per_ts
175    
176    def avail_user_len(self) -> int:
177        """
178        Get the current length of the data in timestamps in the timestamp buffer (see register 'SPC_TS_AVAIL_USER_LEN' in chapter `Timestamp` in the manual)
179
180        Returns
181        -------
182        int
183            data length available in number of timestamps
184        """
185        return self.card.get_i(SPC_TS_AVAIL_USER_LEN) // self.bytes_per_ts
186    
187
188    # Iterator methods
189    _max_polling = 64
190
191    def to_transfer_timestamps(self, timestamps: int) -> None:
192        """
193        This method sets the number of timestamps to transfer
194
195        Parameters
196        ----------
197        timestamps : int
198            the number of timestamps to transfer
199        """
200        self._to_transfer_timestamps = timestamps
201
202    def poll(self) -> npt.ArrayLike:
203        """
204        This method is called when polling for timestamps
205
206        Returns
207        -------
208        npt.ArrayLike
209            the next data block
210        """
211        while True:
212            user_len = self.avail_user_len()
213            if user_len >= 1:
214                user_pos = self.avail_user_pos()
215                self.avail_card_len(user_len)
216                return self.buffer[user_pos:user_pos+user_len, :]

a class to control Spectrum Instrumentation cards with the timestamp functionality

For more information about what setups are available, please have a look at the user manual for your specific card

Parameters
  • ts_mode (int):

  • transfer_mode (int):

  • bits_per_ts (int = 0):

  • bytes_per_ts (int = 16):

TimeStamp(card, *args, **kwargs)
34    def __init__(self, card, *args, **kwargs) -> None:
35        """
36        Initialize the TimeStamp object with a card object
37
38        Parameters
39        ----------
40        card : Card
41            a card object that is used to control the card
42        """
43        
44        super().__init__(card, *args, **kwargs)
45        self.buffer_type = SPCM_BUF_TIMESTAMP
46        self.bits_per_ts = self.bytes_per_ts * 8

Initialize the TimeStamp object with a card object

Parameters
  • card (Card): a card object that is used to control the card
ts_mode: int
transfer_mode: int
bits_per_ts: int = 0
bytes_per_ts: int = 16
buffer_type
def cmd(self, *args) -> None:
48    def cmd(self, *args) -> None:
49        """
50        Execute spcm timestamp commands (see register 'SPC_TIMESTAMP_CMD' in chapter `Timestamp` in the manual)
51    
52        Parameters
53        ----------
54        *args : int
55            The different timestamp command flags to be executed.
56        """
57
58        cmd = 0
59        for arg in args:
60            cmd |= arg
61        self.card.set_i(SPC_TIMESTAMP_CMD, cmd)

Execute spcm timestamp commands (see register 'SPC_TIMESTAMP_CMD' in chapter Timestamp in the manual)

Parameters
  • *args (int): The different timestamp command flags to be executed.
def reset(self) -> None:
63    def reset(self) -> None:
64        """Reset the timestamp counter (see command 'SPC_TS_RESET' in chapter `Timestamp` in the manual)"""
65        self.cmd(SPC_TS_RESET)

Reset the timestamp counter (see command 'SPC_TS_RESET' in chapter Timestamp in the manual)

def mode(self, mode: int, *args: list[int]) -> None:
67    def mode(self, mode : int, *args : list[int]) -> None:
68        """
69        Set the mode of the timestamp counter (see register 'SPC_TIMESTAMP_CMD' in chapter `Timestamp` in the manual)
70
71        Parameters
72        ----------
73        mode : int
74            The mode of the timestamp counter
75        *args : list[int]
76            List of additional commands send with setting the mode
77        """
78        self.ts_mode = mode
79        self.cmd(self.ts_mode, *args)

Set the mode of the timestamp counter (see register 'SPC_TIMESTAMP_CMD' in chapter Timestamp in the manual)

Parameters
  • mode (int): The mode of the timestamp counter
  • *args (list[int]): List of additional commands send with setting the mode
def notify_timestamps(self, notify_timestamps: int) -> None:
81    def notify_timestamps(self, notify_timestamps : int) -> None:
82        """
83        Set the number of timestamps to notify the user about
84        
85        Parameters
86        ----------
87        notify_timestamps : int
88            the number of timestamps to notify the user about
89        """
90        self._notify_timestamps = notify_timestamps

Set the number of timestamps to notify the user about

Parameters
  • notify_timestamps (int): the number of timestamps to notify the user about
def allocate_buffer(self, num_timestamps: int) -> None:
 92    def allocate_buffer(self, num_timestamps : int) -> None:
 93        """
 94        Allocate the buffer for the timestamp data transfer
 95        
 96        Parameters
 97        ----------
 98        num_timestamps : int
 99            The number of timestamps to be allocated
100        """
101
102        self.buffer_size = num_timestamps * self.bytes_per_ts
103
104        dwMask = self._buffer_alignment - 1
105
106        sample_type = np.int64
107        item_size = sample_type(0).itemsize
108        # allocate a buffer (numpy array) for DMA transfer: a little bigger one to have room for address alignment
109        databuffer_unaligned = np.empty(((self._buffer_alignment + self.buffer_size) // item_size, ), dtype = sample_type)   # half byte count at int16 sample (// = integer division)
110        # two numpy-arrays may share the same memory: skip the begin up to the alignment boundary (ArrayVariable[SKIP_VALUE:])
111        # Address of data-memory from numpy-array: ArrayVariable.__array_interface__['data'][0]
112        start_pos_samples = ((self._buffer_alignment - (databuffer_unaligned.__array_interface__['data'][0] & dwMask)) // item_size)
113        self.buffer = databuffer_unaligned[start_pos_samples:start_pos_samples + (self.buffer_size // item_size)]   # byte address but int16 sample: therefore / 2
114        self.buffer = self.buffer.reshape((num_timestamps, 2), order='C') # array items per timestamp, because the maximum item size is 8 bytes = 64 bits

Allocate the buffer for the timestamp data transfer

Parameters
  • num_timestamps (int): The number of timestamps to be allocated
def start_buffer_transfer( self, *args, direction=1, notify_timestamps=0, transfer_offset=0, transfer_length=None) -> None:
116    def start_buffer_transfer(self, *args, direction=SPCM_DIR_CARDTOPC, notify_timestamps=0, transfer_offset=0, transfer_length=None) -> None:
117        """
118        Start the transfer of the timestamp data to the card
119        
120        Parameters
121        ----------
122        *args : list
123            list of additonal arguments that are added as flags to the start dma command
124        """
125        
126        notify_size = 0
127        if notify_timestamps: 
128            self._notify_timestamps = notify_timestamps
129        if self._notify_timestamps: 
130            notify_size = self._notify_timestamps * self.bytes_per_ts
131
132        if transfer_offset:
133            transfer_offset_bytes = transfer_offset * self.bytes_per_ts
134        else:
135            transfer_offset_bytes = 0
136
137        if transfer_length is not None: 
138            transfer_length_bytes = transfer_length * self.bytes_per_ts
139        else:
140            transfer_length_bytes = self.buffer_size
141
142
143        # we define the buffer for transfer and start the DMA transfer
144        self.card._print("Starting the Timestamp transfer and waiting until data is in board memory")
145        self._c_buffer = self.buffer.ctypes.data_as(c_void_p)
146        spcm_dwDefTransfer_i64(self.card._handle, self.buffer_type, direction, notify_size, self._c_buffer, transfer_offset_bytes, transfer_length_bytes)
147        cmd = 0
148        for arg in args:
149            cmd |= arg
150        self.card.cmd(cmd)
151        self.card._print("... timestamp data transfer started")

Start the transfer of the timestamp data to the card

Parameters
  • *args (list): list of additonal arguments that are added as flags to the start dma command
def avail_card_len(self, num_timestamps: int) -> None:
153    def avail_card_len(self, num_timestamps : int) -> None:
154        """
155        Set the amount of timestamps that is available for reading of the timestamp buffer (see register 'SPC_TS_AVAIL_CARD_LEN' in chapter `Timestamp` in the manual)
156
157        Parameters
158        ----------
159        num_timestamps : int
160            the amount of timestamps that is available for reading
161        """
162        card_len = num_timestamps * self.bytes_per_ts
163        self.card.set_i(SPC_TS_AVAIL_CARD_LEN, card_len)

Set the amount of timestamps that is available for reading of the timestamp buffer (see register 'SPC_TS_AVAIL_CARD_LEN' in chapter Timestamp in the manual)

Parameters
  • num_timestamps (int): the amount of timestamps that is available for reading
def avail_user_pos(self) -> int:
165    def avail_user_pos(self) -> int:
166        """
167        Get the current position of the pointer in the timestamp buffer (see register 'SPC_TS_AVAIL_USER_POS' in chapter `Timestamp` in the manual)
168
169        Returns
170        -------
171        int
172            pointer position in timestamps
173        """
174        return self.card.get_i(SPC_TS_AVAIL_USER_POS) // self.bytes_per_ts

Get the current position of the pointer in the timestamp buffer (see register 'SPC_TS_AVAIL_USER_POS' in chapter Timestamp in the manual)

Returns
  • int: pointer position in timestamps
def avail_user_len(self) -> int:
176    def avail_user_len(self) -> int:
177        """
178        Get the current length of the data in timestamps in the timestamp buffer (see register 'SPC_TS_AVAIL_USER_LEN' in chapter `Timestamp` in the manual)
179
180        Returns
181        -------
182        int
183            data length available in number of timestamps
184        """
185        return self.card.get_i(SPC_TS_AVAIL_USER_LEN) // self.bytes_per_ts

Get the current length of the data in timestamps in the timestamp buffer (see register 'SPC_TS_AVAIL_USER_LEN' in chapter Timestamp in the manual)

Returns
  • int: data length available in number of timestamps
def to_transfer_timestamps(self, timestamps: int) -> None:
191    def to_transfer_timestamps(self, timestamps: int) -> None:
192        """
193        This method sets the number of timestamps to transfer
194
195        Parameters
196        ----------
197        timestamps : int
198            the number of timestamps to transfer
199        """
200        self._to_transfer_timestamps = timestamps

This method sets the number of timestamps to transfer

Parameters
  • timestamps (int): the number of timestamps to transfer
def poll( self) -> Union[numpy._typing._array_like._SupportsArray[numpy.dtype[Any]], numpy._typing._nested_sequence._NestedSequence[numpy._typing._array_like._SupportsArray[numpy.dtype[Any]]], bool, int, float, complex, str, bytes, numpy._typing._nested_sequence._NestedSequence[bool | int | float | complex | str | bytes]]:
202    def poll(self) -> npt.ArrayLike:
203        """
204        This method is called when polling for timestamps
205
206        Returns
207        -------
208        npt.ArrayLike
209            the next data block
210        """
211        while True:
212            user_len = self.avail_user_len()
213            if user_len >= 1:
214                user_pos = self.avail_user_pos()
215                self.avail_card_len(user_len)
216                return self.buffer[user_pos:user_pos+user_len, :]

This method is called when polling for timestamps

Returns
  • npt.ArrayLike: the next data block
class Sequence(spcm.DataTransfer):
 11class Sequence(DataTransfer):
 12    """
 13    a high-level class to control the sequence mode on Spectrum Instrumentation cards
 14
 15    For more information about what setups are available, please have a look at the user manual
 16    for your specific card.
 17
 18    """
 19
 20    def __init__(self, card, *args, **kwargs) -> None:
 21        super().__init__(card, *args, **kwargs)
 22
 23    def max_segments(self, max_segments : int = 0) -> int:
 24        """
 25        Set the maximum number of segments that can be used in the sequence mode (see register 'SPC_SEQMODE_MAXSEGMENTS' in chapter `Sequence Mode` in the manual)
 26
 27        Parameters
 28        ----------
 29        max_segments : int
 30            The maximum number of segments that can be used in the sequence mode
 31
 32        Returns
 33        -------
 34        max_segments : int
 35            The actual maximum number of segments that can be used in the sequence mode
 36        """
 37        if max_segments: 
 38            self.card.set_i(SPC_SEQMODE_MAXSEGMENTS, max_segments)
 39        return self.card.get_i(SPC_SEQMODE_MAXSEGMENTS)
 40    
 41    def write_segment(self, segment : int = None) -> int:
 42        """
 43        Defines the current segment to be addressed by the user. Must be programmed prior to changing any segment parameters. (see register 'SPC_SEQMODE_WRITESEGMENT' in chapter `Sequence Mode` in the manual)
 44
 45        Parameters
 46        ----------
 47        segment : int
 48            The segment to be addresses
 49
 50        Returns
 51        -------
 52        segment : int
 53            The segment to be addresses
 54        """
 55
 56        if segment is not None:
 57            self.card.set_i(SPC_SEQMODE_WRITESEGMENT, segment)
 58        return self.card.get_i(SPC_SEQMODE_WRITESEGMENT)
 59    
 60    def segment_size(self, segment_size : int = None, return_unit = None) -> int:
 61        """
 62        Defines the number of valid/to be replayed samples for the current selected memory segment in samples per channel. (see register 'SPC_SEQMODE_SEGMENTSIZE' in chapter `Sequence Mode` in the manual)
 63
 64        Parameters
 65        ----------
 66        segment_size : int | pint.Quantity
 67            The size of the segment in samples
 68
 69        Returns
 70        -------
 71        segment_size : int
 72            The size of the segment in samples
 73        """
 74
 75        if segment_size is not None:
 76            segment_size = UnitConversion.convert(segment_size, units.Sa, int)
 77            self.card.set_i(SPC_SEQMODE_SEGMENTSIZE, segment_size)
 78        return_value = self.card.get_i(SPC_SEQMODE_SEGMENTSIZE)
 79        if return_unit is not None: return UnitConversion.to_unit(return_value, return_unit)
 80        return return_value
 81    
 82    def step_memory(self, step_index : int, next_step_index : int = None, segment_index : int = None, loops : int = None, flags : int = None) -> tuple[int, int, int, int]:
 83        """
 84        Defines the step memory for the current selected memory segment. (see register 'SPC_SEQMODE_STEPMEM0' in chapter `Sequence Mode` in the manual)
 85
 86        Parameters
 87        ----------
 88        step_index : int
 89            The index of the current step
 90        next_step_index : int
 91            The index of the next step in the sequence
 92        segment_index : int
 93            The index of the segment associated to the step
 94        loops : int
 95            The number of times the segment is looped 
 96        flags : int
 97            The flags for the step
 98
 99        Returns
100        -------
101        next_step_index : int
102            The index of the next step in the sequence
103        segment_index : int
104            The index of the segment associated to the step
105        loops : int
106            The number of times the segment is looped 
107        flags : int
108            The flags for the step
109
110        """
111        qwSequenceEntry = 0
112
113        # setup register value
114        if next_step_index is not None and segment_index is not None and loops is not None and flags is not None:
115            qwSequenceEntry = (flags & ~SPCSEQ_LOOPMASK) | (loops & SPCSEQ_LOOPMASK)
116            qwSequenceEntry <<= 32
117            qwSequenceEntry |= ((next_step_index << 16) & SPCSEQ_NEXTSTEPMASK) | (int(segment_index) & SPCSEQ_SEGMENTMASK)
118            self.card.set_i(SPC_SEQMODE_STEPMEM0 + step_index, qwSequenceEntry)
119        
120        qwSequenceEntry = self.card.get_i(SPC_SEQMODE_STEPMEM0 + step_index)
121        return (qwSequenceEntry & SPCSEQ_NEXTSTEPMASK) >> 16, qwSequenceEntry & SPCSEQ_SEGMENTMASK, (qwSequenceEntry >> 32) & SPCSEQ_LOOPMASK, (qwSequenceEntry >> 32) & ~SPCSEQ_LOOPMASK
122
123    
124    def start_step(self, start_step_index : int = None) -> int:
125        """
126        Defines which of all defined steps in the sequence memory will be used first directly after the card start. (see register 'SPC_SEQMODE_STARTSTEP' in chapter `Sequence Mode` in the manual)
127
128        Parameters
129        ----------
130        start_step_index : int
131            The index of the start step
132
133        Returns
134        -------
135        start_step_index : int
136            The index of the start step
137        """
138
139        if start_step_index is not None:
140            self.card.set_i(SPC_SEQMODE_STARTSTEP, start_step_index)
141        return self.card.get_i(SPC_SEQMODE_STARTSTEP)
142    
143    def status(self) -> int:
144        """
145        Reads the status of the sequence mode. (see register 'SPC_SEQMODE_STATUS' in chapter `Sequence Mode` in the manual)
146
147        Returns
148        -------
149        status : int
150            The status of the sequence mode
151
152        """
153        return self.card.get_i(SPC_SEQMODE_STATUS)

a high-level class to control the sequence mode on Spectrum Instrumentation cards

For more information about what setups are available, please have a look at the user manual for your specific card.

Sequence(card, *args, **kwargs)
20    def __init__(self, card, *args, **kwargs) -> None:
21        super().__init__(card, *args, **kwargs)

Initialize the DataTransfer object with a card object and additional arguments

Parameters
  • card (Card): the card object that is used for the data transfer
  • *args (list): list of additional arguments
  • **kwargs (dict): dictionary of additional keyword arguments
def max_segments(self, max_segments: int = 0) -> int:
23    def max_segments(self, max_segments : int = 0) -> int:
24        """
25        Set the maximum number of segments that can be used in the sequence mode (see register 'SPC_SEQMODE_MAXSEGMENTS' in chapter `Sequence Mode` in the manual)
26
27        Parameters
28        ----------
29        max_segments : int
30            The maximum number of segments that can be used in the sequence mode
31
32        Returns
33        -------
34        max_segments : int
35            The actual maximum number of segments that can be used in the sequence mode
36        """
37        if max_segments: 
38            self.card.set_i(SPC_SEQMODE_MAXSEGMENTS, max_segments)
39        return self.card.get_i(SPC_SEQMODE_MAXSEGMENTS)

Set the maximum number of segments that can be used in the sequence mode (see register 'SPC_SEQMODE_MAXSEGMENTS' in chapter Sequence Mode in the manual)

Parameters
  • max_segments (int): The maximum number of segments that can be used in the sequence mode
Returns
  • max_segments (int): The actual maximum number of segments that can be used in the sequence mode
def write_segment(self, segment: int = None) -> int:
41    def write_segment(self, segment : int = None) -> int:
42        """
43        Defines the current segment to be addressed by the user. Must be programmed prior to changing any segment parameters. (see register 'SPC_SEQMODE_WRITESEGMENT' in chapter `Sequence Mode` in the manual)
44
45        Parameters
46        ----------
47        segment : int
48            The segment to be addresses
49
50        Returns
51        -------
52        segment : int
53            The segment to be addresses
54        """
55
56        if segment is not None:
57            self.card.set_i(SPC_SEQMODE_WRITESEGMENT, segment)
58        return self.card.get_i(SPC_SEQMODE_WRITESEGMENT)

Defines the current segment to be addressed by the user. Must be programmed prior to changing any segment parameters. (see register 'SPC_SEQMODE_WRITESEGMENT' in chapter Sequence Mode in the manual)

Parameters
  • segment (int): The segment to be addresses
Returns
  • segment (int): The segment to be addresses
def segment_size(self, segment_size: int = None, return_unit=None) -> int:
60    def segment_size(self, segment_size : int = None, return_unit = None) -> int:
61        """
62        Defines the number of valid/to be replayed samples for the current selected memory segment in samples per channel. (see register 'SPC_SEQMODE_SEGMENTSIZE' in chapter `Sequence Mode` in the manual)
63
64        Parameters
65        ----------
66        segment_size : int | pint.Quantity
67            The size of the segment in samples
68
69        Returns
70        -------
71        segment_size : int
72            The size of the segment in samples
73        """
74
75        if segment_size is not None:
76            segment_size = UnitConversion.convert(segment_size, units.Sa, int)
77            self.card.set_i(SPC_SEQMODE_SEGMENTSIZE, segment_size)
78        return_value = self.card.get_i(SPC_SEQMODE_SEGMENTSIZE)
79        if return_unit is not None: return UnitConversion.to_unit(return_value, return_unit)
80        return return_value

Defines the number of valid/to be replayed samples for the current selected memory segment in samples per channel. (see register 'SPC_SEQMODE_SEGMENTSIZE' in chapter Sequence Mode in the manual)

Parameters
  • segment_size (int | pint.Quantity): The size of the segment in samples
Returns
  • segment_size (int): The size of the segment in samples
def step_memory( self, step_index: int, next_step_index: int = None, segment_index: int = None, loops: int = None, flags: int = None) -> tuple[int, int, int, int]:
 82    def step_memory(self, step_index : int, next_step_index : int = None, segment_index : int = None, loops : int = None, flags : int = None) -> tuple[int, int, int, int]:
 83        """
 84        Defines the step memory for the current selected memory segment. (see register 'SPC_SEQMODE_STEPMEM0' in chapter `Sequence Mode` in the manual)
 85
 86        Parameters
 87        ----------
 88        step_index : int
 89            The index of the current step
 90        next_step_index : int
 91            The index of the next step in the sequence
 92        segment_index : int
 93            The index of the segment associated to the step
 94        loops : int
 95            The number of times the segment is looped 
 96        flags : int
 97            The flags for the step
 98
 99        Returns
100        -------
101        next_step_index : int
102            The index of the next step in the sequence
103        segment_index : int
104            The index of the segment associated to the step
105        loops : int
106            The number of times the segment is looped 
107        flags : int
108            The flags for the step
109
110        """
111        qwSequenceEntry = 0
112
113        # setup register value
114        if next_step_index is not None and segment_index is not None and loops is not None and flags is not None:
115            qwSequenceEntry = (flags & ~SPCSEQ_LOOPMASK) | (loops & SPCSEQ_LOOPMASK)
116            qwSequenceEntry <<= 32
117            qwSequenceEntry |= ((next_step_index << 16) & SPCSEQ_NEXTSTEPMASK) | (int(segment_index) & SPCSEQ_SEGMENTMASK)
118            self.card.set_i(SPC_SEQMODE_STEPMEM0 + step_index, qwSequenceEntry)
119        
120        qwSequenceEntry = self.card.get_i(SPC_SEQMODE_STEPMEM0 + step_index)
121        return (qwSequenceEntry & SPCSEQ_NEXTSTEPMASK) >> 16, qwSequenceEntry & SPCSEQ_SEGMENTMASK, (qwSequenceEntry >> 32) & SPCSEQ_LOOPMASK, (qwSequenceEntry >> 32) & ~SPCSEQ_LOOPMASK

Defines the step memory for the current selected memory segment. (see register 'SPC_SEQMODE_STEPMEM0' in chapter Sequence Mode in the manual)

Parameters
  • step_index (int): The index of the current step
  • next_step_index (int): The index of the next step in the sequence
  • segment_index (int): The index of the segment associated to the step
  • loops (int): The number of times the segment is looped
  • flags (int): The flags for the step
Returns
  • next_step_index (int): The index of the next step in the sequence
  • segment_index (int): The index of the segment associated to the step
  • loops (int): The number of times the segment is looped
  • flags (int): The flags for the step
def start_step(self, start_step_index: int = None) -> int:
124    def start_step(self, start_step_index : int = None) -> int:
125        """
126        Defines which of all defined steps in the sequence memory will be used first directly after the card start. (see register 'SPC_SEQMODE_STARTSTEP' in chapter `Sequence Mode` in the manual)
127
128        Parameters
129        ----------
130        start_step_index : int
131            The index of the start step
132
133        Returns
134        -------
135        start_step_index : int
136            The index of the start step
137        """
138
139        if start_step_index is not None:
140            self.card.set_i(SPC_SEQMODE_STARTSTEP, start_step_index)
141        return self.card.get_i(SPC_SEQMODE_STARTSTEP)

Defines which of all defined steps in the sequence memory will be used first directly after the card start. (see register 'SPC_SEQMODE_STARTSTEP' in chapter Sequence Mode in the manual)

Parameters
  • start_step_index (int): The index of the start step
Returns
  • start_step_index (int): The index of the start step
def status(self) -> int:
143    def status(self) -> int:
144        """
145        Reads the status of the sequence mode. (see register 'SPC_SEQMODE_STATUS' in chapter `Sequence Mode` in the manual)
146
147        Returns
148        -------
149        status : int
150            The status of the sequence mode
151
152        """
153        return self.card.get_i(SPC_SEQMODE_STATUS)

Reads the status of the sequence mode. (see register 'SPC_SEQMODE_STATUS' in chapter Sequence Mode in the manual)

Returns
  • status (int): The status of the sequence mode
class BlockAverage(spcm.Multi):
11class BlockAverage(Multi):
12    """a high-level class to control Block Average functionality on Spectrum Instrumentation cards
13
14    For more information about what setups are available, please have a look at the user manual
15    for your specific card.
16
17    """
18
19    def __init__(self, card, *args, **kwargs) -> None:
20        super().__init__(card, *args, **kwargs)
21    
22    def averages(self, num_averages : int = None) -> int:
23        """Sets the number of averages for the block averaging functionality (see hardware reference manual register 'SPC_AVERAGES')
24
25        Parameters
26        ----------
27        num_averages : int
28            the number of averages for the boxcar functionality
29
30        Returns
31        -------
32        int
33            the number of averages for the block averaging functionality
34        """
35        if num_averages is not None:
36            self.card.set_i(SPC_AVERAGES, num_averages)
37        return self.card.get_i(SPC_AVERAGES)
38    
39    def bits_per_sample(self) -> int:
40        """
41        Get the number of bits per sample
42
43        Returns
44        -------
45        int
46            number of bits per sample
47        """
48        return super().bits_per_sample * 2
49    
50    def bytes_per_sample(self) -> int:
51        """
52        Get the number of bytes per sample
53
54        Returns
55        -------
56        int
57            number of bytes per sample
58        """
59        return super().bytes_per_sample * 2
60
61    def numpy_type(self) -> npt.NDArray[np.int_]:
62        """
63        Get the type of numpy data from number of bytes
64
65        Returns
66        -------
67        numpy data type
68            the type of data that is used by the card
69        """
70        if self.bytes_per_sample == 2:
71            return np.int16
72        return np.int32

a high-level class to control Block Average functionality on Spectrum Instrumentation cards

For more information about what setups are available, please have a look at the user manual for your specific card.

BlockAverage(card, *args, **kwargs)
19    def __init__(self, card, *args, **kwargs) -> None:
20        super().__init__(card, *args, **kwargs)

Initialize the DataTransfer object with a card object and additional arguments

Parameters
  • card (Card): the card object that is used for the data transfer
  • *args (list): list of additional arguments
  • **kwargs (dict): dictionary of additional keyword arguments
def averages(self, num_averages: int = None) -> int:
22    def averages(self, num_averages : int = None) -> int:
23        """Sets the number of averages for the block averaging functionality (see hardware reference manual register 'SPC_AVERAGES')
24
25        Parameters
26        ----------
27        num_averages : int
28            the number of averages for the boxcar functionality
29
30        Returns
31        -------
32        int
33            the number of averages for the block averaging functionality
34        """
35        if num_averages is not None:
36            self.card.set_i(SPC_AVERAGES, num_averages)
37        return self.card.get_i(SPC_AVERAGES)

Sets the number of averages for the block averaging functionality (see hardware reference manual register 'SPC_AVERAGES')

Parameters
  • num_averages (int): the number of averages for the boxcar functionality
Returns
  • int: the number of averages for the block averaging functionality
def bits_per_sample(self) -> int:
39    def bits_per_sample(self) -> int:
40        """
41        Get the number of bits per sample
42
43        Returns
44        -------
45        int
46            number of bits per sample
47        """
48        return super().bits_per_sample * 2

Get the number of bits per sample

Returns
  • int: number of bits per sample
def bytes_per_sample(self) -> int:
50    def bytes_per_sample(self) -> int:
51        """
52        Get the number of bytes per sample
53
54        Returns
55        -------
56        int
57            number of bytes per sample
58        """
59        return super().bytes_per_sample * 2

Get the number of bytes per sample

Returns
  • int: number of bytes per sample
def numpy_type(self) -> numpy.ndarray[typing.Any, numpy.dtype[numpy.int64]]:
61    def numpy_type(self) -> npt.NDArray[np.int_]:
62        """
63        Get the type of numpy data from number of bytes
64
65        Returns
66        -------
67        numpy data type
68            the type of data that is used by the card
69        """
70        if self.bytes_per_sample == 2:
71            return np.int16
72        return np.int32

Get the type of numpy data from number of bytes

Returns
  • numpy data type: the type of data that is used by the card
class Boxcar(spcm.Multi):
11class Boxcar(Multi):
12    """a high-level class to control Boxcar functionality on Spectrum Instrumentation cards
13
14    For more information about what setups are available, please have a look at the user manual
15    for your specific card.
16
17    """
18
19    def __init__(self, card, *args, **kwargs) -> None:
20        super().__init__(card, *args, **kwargs)
21    
22    def box_averages(self, num_averages : int = None) -> int:
23        """Sets the number of averages for the boxcar functionality (see hardware reference manual register 'SPC_BOX_AVERAGES')
24
25        Parameters
26        ----------
27        num_averages : int
28            the number of averages for the boxcar functionality
29
30        Returns
31        -------
32        int
33            the number of averages for the boxcar functionality
34        """
35        if num_averages is not None:
36            self.card.set_i(SPC_BOX_AVERAGES, num_averages)
37        return self.card.get_i(SPC_BOX_AVERAGES)
38    
39    def bits_per_sample(self) -> int:
40        """
41        Get the number of bits per sample
42
43        Returns
44        -------
45        int
46            number of bits per sample
47        """
48        return 32
49    
50    def bytes_per_sample(self) -> int:
51        """
52        Get the number of bytes per sample
53
54        Returns
55        -------
56        int
57            number of bytes per sample
58        """
59        return 4
60
61    def numpy_type(self) -> npt.NDArray[np.int_]:
62        """
63        Get the type of numpy data from number of bytes
64
65        Returns
66        -------
67        numpy data type
68            the type of data that is used by the card
69        """
70        return np.int32

a high-level class to control Boxcar functionality on Spectrum Instrumentation cards

For more information about what setups are available, please have a look at the user manual for your specific card.

Boxcar(card, *args, **kwargs)
19    def __init__(self, card, *args, **kwargs) -> None:
20        super().__init__(card, *args, **kwargs)

Initialize the DataTransfer object with a card object and additional arguments

Parameters
  • card (Card): the card object that is used for the data transfer
  • *args (list): list of additional arguments
  • **kwargs (dict): dictionary of additional keyword arguments
def box_averages(self, num_averages: int = None) -> int:
22    def box_averages(self, num_averages : int = None) -> int:
23        """Sets the number of averages for the boxcar functionality (see hardware reference manual register 'SPC_BOX_AVERAGES')
24
25        Parameters
26        ----------
27        num_averages : int
28            the number of averages for the boxcar functionality
29
30        Returns
31        -------
32        int
33            the number of averages for the boxcar functionality
34        """
35        if num_averages is not None:
36            self.card.set_i(SPC_BOX_AVERAGES, num_averages)
37        return self.card.get_i(SPC_BOX_AVERAGES)

Sets the number of averages for the boxcar functionality (see hardware reference manual register 'SPC_BOX_AVERAGES')

Parameters
  • num_averages (int): the number of averages for the boxcar functionality
Returns
  • int: the number of averages for the boxcar functionality
def bits_per_sample(self) -> int:
39    def bits_per_sample(self) -> int:
40        """
41        Get the number of bits per sample
42
43        Returns
44        -------
45        int
46            number of bits per sample
47        """
48        return 32

Get the number of bits per sample

Returns
  • int: number of bits per sample
def bytes_per_sample(self) -> int:
50    def bytes_per_sample(self) -> int:
51        """
52        Get the number of bytes per sample
53
54        Returns
55        -------
56        int
57            number of bytes per sample
58        """
59        return 4

Get the number of bytes per sample

Returns
  • int: number of bytes per sample
def numpy_type(self) -> numpy.ndarray[typing.Any, numpy.dtype[numpy.int64]]:
61    def numpy_type(self) -> npt.NDArray[np.int_]:
62        """
63        Get the type of numpy data from number of bytes
64
65        Returns
66        -------
67        numpy data type
68            the type of data that is used by the card
69        """
70        return np.int32

Get the type of numpy data from number of bytes

Returns
  • numpy data type: the type of data that is used by the card
class SpcmException(builtins.Exception):
100class SpcmException(Exception):
101    """a container class for handling driver level errors
102
103    Examples
104    ----------
105    ```python
106    raise SpcmException(handle=card.handle())
107    raise SpcmException(register=0, value=0, text="Some weird error")
108    ```
109    
110    Parameters
111    ---------
112    error : SpcmError
113        the error that induced the raising of the exception
114    
115    """
116    error = None
117
118    def __init__(self, error = None, register = None, value = None, text = None) -> None:
119        """
120        Constructs exception object and an associated error object, either by getting 
121        the last error from the card specified by the handle or using the information 
122        coming from the parameters register, value and text
123
124        Parameters
125        ----------
126        handle : drv_handle (optional)
127            a card handle to obtain the last error
128        register, value and text : int, int, str (optional)
129            parameters to define an error that is not raised by a driver error
130        """
131        if error: self.error = error
132        if register or value or text:
133            self.error = SpcmError(register=register, value=value, text=text)
134        
135    
136    def __str__(self) -> str:
137        """
138        Returns a human-readable text of the last error connected to the exception
139    
140        Class Parameters
141        ----------
142        self.error
143    
144        Returns
145        -------
146        str
147            the human-readable text as return by the error
148        """
149        
150        return str(self.error)

a container class for handling driver level errors

Examples
raise SpcmException(handle=card.handle())
raise SpcmException(register=0, value=0, text="Some weird error")
Parameters
  • error (SpcmError): the error that induced the raising of the exception
SpcmException(error=None, register=None, value=None, text=None)
118    def __init__(self, error = None, register = None, value = None, text = None) -> None:
119        """
120        Constructs exception object and an associated error object, either by getting 
121        the last error from the card specified by the handle or using the information 
122        coming from the parameters register, value and text
123
124        Parameters
125        ----------
126        handle : drv_handle (optional)
127            a card handle to obtain the last error
128        register, value and text : int, int, str (optional)
129            parameters to define an error that is not raised by a driver error
130        """
131        if error: self.error = error
132        if register or value or text:
133            self.error = SpcmError(register=register, value=value, text=text)

Constructs exception object and an associated error object, either by getting the last error from the card specified by the handle or using the information coming from the parameters register, value and text

Parameters
  • handle (drv_handle (optional)): a card handle to obtain the last error
  • register, value and text (int, int, str (optional)): parameters to define an error that is not raised by a driver error
error = None
class SpcmTimeout(builtins.Exception):
152class SpcmTimeout(Exception):
153    """a container class for handling specific timeout exceptions"""
154    pass

a container class for handling specific timeout exceptions

class SpcmError:
10class SpcmError():
11    """a container class for handling driver level errors
12        
13    Examples
14    ----------
15    ```python
16    error = SpcmError(handle=card.handle())
17    error = SpcmError(register=0, value=0, text="Some weird error")
18    ```
19
20    Parameters
21    ---------
22    register : int
23        the register address that triggered the error
24    
25    value : int
26        the value that was written to the register address
27    
28    text : str
29        the human-readable text associated with the error
30    
31    """
32
33    register : int = 0
34    value : int = 0
35    text : str = ""
36    _handle = None
37    
38    def __init__(self, handle = None, register = None, value = None, text = None) -> None:
39        """
40        Constructs an error object, either by getting the last error from the card specified by the handle
41        or using the information coming from the parameters register, value and text
42
43        Parameters
44        ----------
45        handle : pyspcm.drv_handle (optional)
46            a card handle to obtain the last error
47        register, value and text : int, int, str (optional)
48            parameters to define an error that is not raised by a driver error
49        """
50        if handle:
51            self._handle = handle
52            self.get_info()
53        if register: self.register = register
54        if value: self.value = value
55        if text: self.text = text
56
57    def get_info(self) -> int:
58        """
59        Gets the last error registered by the card and puts it in the object
60    
61        Class Parameters
62        ----------
63        self.register
64        self.value
65        self.text
66    
67        Returns
68        -------
69        int
70            Error number of the spcm_dwGetErrorInfo_i32 class
71        """
72
73        register = uint32(0)
74        value = int32(0)
75        text = create_string_buffer(ERRORTEXTLEN)
76        dwErr = spcm_dwGetErrorInfo_i32(self._handle, byref(register), byref(value), byref(text))
77        self.register = register.value
78        self.value = value.value
79        self.text = text.value.decode('utf-8')
80        return dwErr
81    
82    def __str__(self) -> str:
83        """
84        Returns a human-readable text of the last error
85    
86        Class Parameters
87        ----------
88        self.register
89        self.value
90        self.text
91    
92        Returns
93        -------
94        str
95            the human-readable text as saved in self.text.
96        """
97        
98        return str(self.text)

a container class for handling driver level errors

Examples
error = SpcmError(handle=card.handle())
error = SpcmError(register=0, value=0, text="Some weird error")
Parameters
  • register (int): the register address that triggered the error
  • value (int): the value that was written to the register address
  • text (str): the human-readable text associated with the error
SpcmError(handle=None, register=None, value=None, text=None)
38    def __init__(self, handle = None, register = None, value = None, text = None) -> None:
39        """
40        Constructs an error object, either by getting the last error from the card specified by the handle
41        or using the information coming from the parameters register, value and text
42
43        Parameters
44        ----------
45        handle : pyspcm.drv_handle (optional)
46            a card handle to obtain the last error
47        register, value and text : int, int, str (optional)
48            parameters to define an error that is not raised by a driver error
49        """
50        if handle:
51            self._handle = handle
52            self.get_info()
53        if register: self.register = register
54        if value: self.value = value
55        if text: self.text = text

Constructs an error object, either by getting the last error from the card specified by the handle or using the information coming from the parameters register, value and text

Parameters
  • handle (pyspcm.drv_handle (optional)): a card handle to obtain the last error
  • register, value and text (int, int, str (optional)): parameters to define an error that is not raised by a driver error
register: int = 0
value: int = 0
text: str = ''
def get_info(self) -> int:
57    def get_info(self) -> int:
58        """
59        Gets the last error registered by the card and puts it in the object
60    
61        Class Parameters
62        ----------
63        self.register
64        self.value
65        self.text
66    
67        Returns
68        -------
69        int
70            Error number of the spcm_dwGetErrorInfo_i32 class
71        """
72
73        register = uint32(0)
74        value = int32(0)
75        text = create_string_buffer(ERRORTEXTLEN)
76        dwErr = spcm_dwGetErrorInfo_i32(self._handle, byref(register), byref(value), byref(text))
77        self.register = register.value
78        self.value = value.value
79        self.text = text.value.decode('utf-8')
80        return dwErr

Gets the last error registered by the card and puts it in the object

Class Parameters

self.register self.value self.text

Returns
  • int: Error number of the spcm_dwGetErrorInfo_i32 class
class SCAPPTransfer(spcm.DataTransfer):
 46class SCAPPTransfer(DataTransfer):
 47    """
 48    Class for data transfer between the card and the host using the SCAPP API.
 49
 50    Parameters
 51    ----------
 52    direction : Direction = Direction.Acquisition
 53        Direction of the data transfer.
 54    """
 55
 56    direction : Direction = None
 57
 58    def __init__(self, card : Card, direction : Direction = Direction.Acquisition):
 59        if not _cuda_support:
 60            raise ImportError("CUDA support is not available. Please install the cupy and cuda-python packages.")
 61        super().__init__(card)
 62        self.direction = direction
 63        self.iterator_index = 0
 64
 65    def allocate_buffer(self, num_samples : int) -> None:
 66        """
 67        Memory allocation for the buffer that is used for communicating with the card
 68
 69        Parameters
 70        ----------
 71        num_samples : int | pint.Quantity = None
 72            use the number of samples an get the number of active channels and bytes per samples directly from the card
 73        """
 74        
 75        self.buffer_samples = UnitConversion.convert(num_samples, units.Sa, int)
 76        # Allocate RDMA buffer
 77        self.buffer = cp.empty((self.num_channels, self.buffer_samples), dtype = self.numpy_type(), order='F')
 78        flag = 1
 79        checkCudaErrors(cuda.cuPointerSetAttribute(flag, cuda.CUpointer_attribute.CU_POINTER_ATTRIBUTE_SYNC_MEMOPS, self.buffer.data.ptr))
 80    
 81    def start_buffer_transfer(self, *args, notify_samples = None, transfer_length = None) -> None:
 82        """
 83        Setup an RDMA transfer.
 84
 85        Parameters
 86        ----------
 87        args : int
 88            Additional commands that are send to the card.
 89        notify_samples : int
 90            Size of the part of the buffer that is used for notifications.
 91        transfer_length : int
 92            Total length of the transfer buffer.
 93        """
 94
 95        self.notify_samples(notify_samples)
 96        self.buffer_samples = transfer_length
 97
 98        # Define transfer CUDA buffers
 99        if self.direction == Direction.Acquisition:
100            direction = SPCM_DIR_CARDTOGPU
101        else:
102            direction = SPCM_DIR_GPUTOCARD
103        self.card._check_error(spcm_dwDefTransfer_i64(self.card._handle, SPCM_BUF_DATA, direction, self.notify_size, c_void_p(self.buffer.data.ptr), 0, self.buffer_size))
104
105        # Execute additional commands if available
106        if args:
107            cmd = 0
108            for arg in args:
109                cmd |= arg
110            self.card.cmd(cmd)
111            self.card._print("... CUDA data transfer started")
112    
113    _auto_avail_card_len = True
114    def auto_avail_card_len(self, value : bool = None) -> bool:
115        """
116        Enable or disable the automatic sending of the number of samples that the card can now use for sample data transfer again
117
118        Parameters
119        ----------
120        value : bool = None
121            True to enable, False to disable and None to get the current status
122
123        Returns
124        -------
125        bool
126            the current status
127        """
128        if value is not None:
129            self._auto_avail_card_len = value
130        return self._auto_avail_card_len
131
132    def __next__(self) -> tuple:
133        """
134        This method is called when the next element is requested from the iterator
135
136        Returns
137        -------
138        npt.ArrayLike
139            the next data block
140        
141        Raises
142        ------
143        StopIteration
144        """
145        timeout_counter = 0
146
147        if self.iterator_index != 0 and self._auto_avail_card_len:
148            self.avail_card_len(self._notify_samples)
149
150        while True:
151            try:
152                self.wait_dma()
153            except SpcmTimeout:
154                self.card._print("... Timeout ({})".format(timeout_counter), end='\r')
155                timeout_counter += 1
156                if timeout_counter > self._max_timeout:
157                    self.iterator_index = 0
158                    raise StopIteration
159            else:
160                break
161        
162        self.iterator_index += 1
163
164        self._current_samples += self._notify_samples
165        if self._to_transfer_samples != 0 and self._to_transfer_samples < self._current_samples:
166            self.iterator_index = 0
167            raise StopIteration
168
169        user_pos = self.avail_user_pos()
170        fill_size = self.fill_size_promille()
171        
172        self.card._print("Fill size: {}%  Pos:{:08x} Total:{:.2f} MiS / {:.2f} MiS".format(fill_size/10, user_pos, self._current_samples / MEBI(1), self._to_transfer_samples / MEBI(1)), end='\r', verbose=self._verbose)
173        
174        return self.buffer[:, user_pos:user_pos+self._notify_samples]

Class for data transfer between the card and the host using the SCAPP API.

Parameters
  • direction (Direction = Direction.Acquisition): Direction of the data transfer.
SCAPPTransfer( card: Card, direction: spcm_core.pyspcm.Direction = <Direction.Acquisition: 1>)
58    def __init__(self, card : Card, direction : Direction = Direction.Acquisition):
59        if not _cuda_support:
60            raise ImportError("CUDA support is not available. Please install the cupy and cuda-python packages.")
61        super().__init__(card)
62        self.direction = direction
63        self.iterator_index = 0

Initialize the DataTransfer object with a card object and additional arguments

Parameters
  • card (Card): the card object that is used for the data transfer
  • *args (list): list of additional arguments
  • **kwargs (dict): dictionary of additional keyword arguments
direction: spcm_core.pyspcm.Direction = None
iterator_index = 0
def allocate_buffer(self, num_samples: int) -> None:
65    def allocate_buffer(self, num_samples : int) -> None:
66        """
67        Memory allocation for the buffer that is used for communicating with the card
68
69        Parameters
70        ----------
71        num_samples : int | pint.Quantity = None
72            use the number of samples an get the number of active channels and bytes per samples directly from the card
73        """
74        
75        self.buffer_samples = UnitConversion.convert(num_samples, units.Sa, int)
76        # Allocate RDMA buffer
77        self.buffer = cp.empty((self.num_channels, self.buffer_samples), dtype = self.numpy_type(), order='F')
78        flag = 1
79        checkCudaErrors(cuda.cuPointerSetAttribute(flag, cuda.CUpointer_attribute.CU_POINTER_ATTRIBUTE_SYNC_MEMOPS, self.buffer.data.ptr))

Memory allocation for the buffer that is used for communicating with the card

Parameters
  • num_samples (int | pint.Quantity = None): use the number of samples an get the number of active channels and bytes per samples directly from the card
def start_buffer_transfer(self, *args, notify_samples=None, transfer_length=None) -> None:
 81    def start_buffer_transfer(self, *args, notify_samples = None, transfer_length = None) -> None:
 82        """
 83        Setup an RDMA transfer.
 84
 85        Parameters
 86        ----------
 87        args : int
 88            Additional commands that are send to the card.
 89        notify_samples : int
 90            Size of the part of the buffer that is used for notifications.
 91        transfer_length : int
 92            Total length of the transfer buffer.
 93        """
 94
 95        self.notify_samples(notify_samples)
 96        self.buffer_samples = transfer_length
 97
 98        # Define transfer CUDA buffers
 99        if self.direction == Direction.Acquisition:
100            direction = SPCM_DIR_CARDTOGPU
101        else:
102            direction = SPCM_DIR_GPUTOCARD
103        self.card._check_error(spcm_dwDefTransfer_i64(self.card._handle, SPCM_BUF_DATA, direction, self.notify_size, c_void_p(self.buffer.data.ptr), 0, self.buffer_size))
104
105        # Execute additional commands if available
106        if args:
107            cmd = 0
108            for arg in args:
109                cmd |= arg
110            self.card.cmd(cmd)
111            self.card._print("... CUDA data transfer started")

Setup an RDMA transfer.

Parameters
  • args (int): Additional commands that are send to the card.
  • notify_samples (int): Size of the part of the buffer that is used for notifications.
  • transfer_length (int): Total length of the transfer buffer.
def auto_avail_card_len(self, value: bool = None) -> bool:
114    def auto_avail_card_len(self, value : bool = None) -> bool:
115        """
116        Enable or disable the automatic sending of the number of samples that the card can now use for sample data transfer again
117
118        Parameters
119        ----------
120        value : bool = None
121            True to enable, False to disable and None to get the current status
122
123        Returns
124        -------
125        bool
126            the current status
127        """
128        if value is not None:
129            self._auto_avail_card_len = value
130        return self._auto_avail_card_len

Enable or disable the automatic sending of the number of samples that the card can now use for sample data transfer again

Parameters
  • value (bool = None): True to enable, False to disable and None to get the current status
Returns
  • bool: the current status