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
andspcerr.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:
- Digitizers
- M2p
- M4i / M4x
- M5i
- digitizerNetbox
- Arbitrary Waveform Generators (AWGs)
- M2p
- M4i / M4x
- generatorNetbox
- Digital Waveform Acquisition/Pattern Generation
- M2p
- M4i / M4x
- Hybrid
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)
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
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
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
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
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
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
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
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
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.
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
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
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)
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)
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
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.
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
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
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
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
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.
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.
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
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
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)
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
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.
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
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
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
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
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.
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
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
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
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
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
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
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)
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)
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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)
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)
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
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
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
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
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
602 def write_setup(self) -> None: 603 """Write the setup to the card""" 604 self.card.write_setup()
Write the setup to the card
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.
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
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
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
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
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
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
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
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
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
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:
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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")
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
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
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?):
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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)
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)
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)
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
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
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
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
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
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
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
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
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
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
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
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)
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
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).
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
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
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
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
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
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
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
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
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
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
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.
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
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
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
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 bufferbuffer_size
(int): defines the size of the current buffer shared between the PC and the cardbuffer_type
(int): defines the type of data in the buffer that is used for the transfernum_channels
(int): defines the number of channels that are used for the transferbytes_per_sample
(int): defines the number of bytes per samplebits_per_sample
(int): defines the number of bits per sample
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
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]
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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)
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)
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
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
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
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
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
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.
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
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
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
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
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)
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
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
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
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)
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)
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
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
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):
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
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)
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)
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
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
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
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
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
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
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
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
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
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
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
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
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!):
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!):
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!):
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
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
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
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
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!):
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!):
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!):
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
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
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!):
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!):
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!):
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.
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.
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
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
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
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
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!):
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!):
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!):
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
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
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
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!):
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!):
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!):
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
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)
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)
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)
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)
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)
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
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)
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
An enumeration.
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.
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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.
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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)
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
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.
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
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
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
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
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
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
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
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):
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
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.
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)
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
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
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
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
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
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
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
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
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
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.
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
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
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
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
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
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
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
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.
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
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
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
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
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
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.
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
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
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
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
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
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
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
152class SpcmTimeout(Exception): 153 """a container class for handling specific timeout exceptions""" 154 pass
a container class for handling specific timeout exceptions
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
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
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
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.
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
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
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.
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