@@ -61,6 +61,20 @@ def _channels_check(self, channels: int | list[int] | str) -> int | list[int]:
6161 assert not isinstance (channels , str )
6262 return channels
6363
64+ @staticmethod
65+ def _data_format_check (data_format : str ) -> str :
66+ """Check the data format string and return in SCPI style."""
67+ data_formats = {
68+ "ascii" : "ASCii" ,
69+ "byte" : "BYTE" ,
70+ "rbyte" : "RBYTe" ,
71+ "word" : "WORD"
72+ }
73+ if data_format .lower () not in data_formats :
74+ raise QMI_InstrumentException (f"Unexpected data format, got { data_format } " )
75+
76+ return data_formats [data_format .lower ()]
77+
6478 @staticmethod
6579 def _data_type_check (data_type : str ) -> str :
6680 """Check the data type string and return in SCPI style."""
@@ -70,6 +84,7 @@ def _data_type_check(data_type: str) -> str:
7084 data_type = "ASCii"
7185 else :
7286 raise QMI_InstrumentException (f"Unexpected data type, got { data_type } " )
87+
7388 return data_type
7489
7590 def _channel_value_setter (
@@ -280,17 +295,13 @@ def get_max_waveform(self, channels: int | list[int] | str) -> np.ndarray:
280295
281296 @rpc_method
282297 def set_channel_position (self , channels : int | list [int ] | str , position : float | list [float ]) -> None :
283- """
284- Changes the position of the specified channels in volts.
285- """
298+ """Changes the position of the specified channels in volts."""
286299 checked_chs = self ._channels_check (channels )
287300 self ._channel_value_setter (checked_chs , position , "POSition" )
288301
289302 @rpc_method
290303 def get_channel_position (self , channels : int | list [int ] | str ) -> np .ndarray :
291- """
292- Returns the current position for the specified channels in volts.
293- """
304+ """Returns the current position for the specified channels in volts."""
294305 checked_chs = self ._channels_check (channels )
295306 position = self ._channel_value_getter (checked_chs , "POSition" , 11 )
296307
@@ -314,6 +325,21 @@ def set_trigger_level(self, voltage: float) -> None:
314325 """
315326 self ._scpi_protocol .write (f":TRIGger:ATRigger:SIMPle:LEVel { voltage } V" )
316327
328+ @rpc_method
329+ def set_average (self , average : int ) -> None :
330+ """Sets the mode to average and set the average count.
331+
332+ Parameters:
333+ average: Has to be between 2 and 1024 in 2**n steps.
334+ """
335+ self ._scpi_protocol .write (":ACQUIRE:MODE AVERAGE" )
336+ self ._scpi_protocol .write (f":ACQUIRE:AVERAGE:COUNT { average } " )
337+
338+ @rpc_method
339+ def set_normal (self ) -> None :
340+ """Sets acquisition mode to normal."""
341+ self ._scpi_protocol .write (":ACQUIRE:MODE NORMAL" )
342+
317343 @rpc_method
318344 def set_number_data_points (self , length : int ) -> None :
319345 """Sets the scope acquire length.
@@ -325,49 +351,141 @@ def set_number_data_points(self, length: int) -> None:
325351
326352 @rpc_method
327353 def get_number_data_points (self ) -> int :
354+ """Gets the number of data points that will be saved."""
355+ return int (self ._scpi_protocol .ask (":WAVeform:LENGth?" )[10 :])
356+
357+ @rpc_method
358+ def set_data_format (self , data_format : str ) -> None :
359+ """Set the waveform data format to specific type.
360+
361+ Parameters:
362+ data_format: What format to send the data in. Possible options:
328363 """
329- Gets the number of data points that will be saved.
330- """
331- return int (self ._scpi_protocol .ask (":WAVeform:LENGth?" )[10 :]) # TODO: range 10:-1 needs to be checked with HW
364+ data_format = self ._data_format_check (data_format )
365+ self ._scpi_protocol .write (":WAVeform:FORMat {data_format}" )
332366
333367 @rpc_method
334- def set_average (self , average : int ) -> None :
335- """Sets the mode to average and set the average count.
368+ def set_waveform_trace_channel (self , channel : int ) -> None :
369+ """Set the waveform (channel) number to obtain the waveform trace data from.
370+
371+ Parameters:
372+ channel: Waveform (channel) number.
373+ """
374+ assert channel in range (1 , self .CHANNELS + 1 ), f"Invalid waveform channel number { channel } "
375+ self ._scpi_protocol .write (f":WAVeform:TRACE { channel } " )
336376
337- Parameters:
338- average: Has to be between 2 and 1024 in 2**n steps.
377+ @rpc_method
378+ def get_waveform_trace_data (self , data_format : str ) -> np .ndarray :
379+ """Get waveform (channel) trace data from single waveform.
380+
381+ Returns:
382+ trace_data: The trace data formatted according to the given data format.
339383 """
340- self ._scpi_protocol .write (":ACQUIRE:MODE AVERAGE" )
341- self ._scpi_protocol .write (f":ACQUIRE:AVERAGE:COUNT { average } " )
384+ data_format = self ._data_format_check (data_format )
385+ # Get raw trace data
386+ raw_data = self ._scpi_protocol .ask (":WAVeform:SEND?" )
387+ # Manipulate and return
388+ if data_format == "ASCii" :
389+ return np .array ([float (x ) for x in raw_data .split (',' )])
390+
391+ '''<Block Data>
392+ <Block data> is any 8-bit data. It is only used in
393+ response messages on the DLM4000. The syntax is as
394+ follows:
395+ Form Example
396+ #N<N-digit decimal number><data byte sequence>#800000010ABCDEFGHIJ
397+ • #N
398+ Indicates that the data is <block data>. “N” indicates
399+ the number of succeeding data bytes (digits) in
400+ ASCII code.
401+ • <N-digit decimal number>
402+ Indicates the number of bytes of data (example:
403+ 00000010 = 10 bytes).
404+ • <Data byte sequence>
405+ Expresses the actual data (example: ABCDEFGHIJ).
406+ • Data is comprised of 8-bit values (0 to 255). This
407+ means that the ASCII code “0AH,” which stands for
408+ “NL,” can also be included in the data. Hence, care
409+ must be taken when programming the controller'''
410+ range = self .get_waveform_data_range () # TODO
411+ offset = self .get_waveform_data_offset () # TODO
412+ position = 0.0
413+ if data_format == "WORD" :
414+ division = 3200.0
415+
416+ elif data_format == "BYTE" :
417+ division = 12.5
418+
419+ elif data_format == "RBYTe" :
420+ division = 25.0
421+ position = self .get_waveform_position () # TODO
422+
423+ data_points = ...
424+ for dp in data_points :
425+ value = range * (dp - position ) / division + offset
342426
343427 @rpc_method
344- def set_normal (self ) -> None :
345- """Sets acquisition mode to normal."""
346- self ._scpi_protocol .write (":ACQUIRE:MODE NORMAL" )
428+ def get_waveforms_trace_data (self , channels : list [int ], data_format : str = "ascii" ) -> np .ndarray :
429+ """Returns the on-screen traces from specified waveforms (channels) in the form of a 2D numpy array.
430+ Data is streamed via ethernet in the given format.
431+
432+ Parameters:
433+ channels: A list of the waveform numbers (waveform channels) to return data from.
434+ data_format: What format to send the data in. Possible formats are:
435+ 'ascii' | 'byte' | 'rbyte' | 'word'. Default is 'ascii'
436+
437+ Returns:
438+ traces: An array where each row is a channel trace.
439+ Channel order corresponds to 'channels' parameter.
440+ """
441+ # Expected length of each trace
442+ num_points = self .get_number_data_points ()
443+
444+ # Initialize data array
445+ traces = np .zeros ((len (channels ), num_points ))
446+
447+ # Stop to acquire data
448+ self .stop ()
449+
450+ # Set data format
451+ self .set_data_format (data_format )
347452
453+ # Get trace data from each defined channel
454+ for i , waveform in enumerate (channels ):
455+ # Set which channel to get data from
456+ self .set_waveform_trace_channel (waveform )
457+ # Transfer data from the stopped acquisition
458+ resp = self .get_waveform_trace_data (data_format )
459+ traces [i ] = np .array ([float (x ) for x in resp .split (',' )])
460+
461+ # Start continuous acquisition again
462+ self .start ()
463+
464+ return traces
465+
348466 @rpc_method
349467 def save_file (self , name : str , data_type : str = "binary" , waiting_time : float = 12.0 ) -> None :
350468 """Saves in the internal memory the file with a name "nameXXX.csv".
351469 Here XXX labels the files that have the same name with numbers from 000 to 999.
352470
353471 Parameters:
354472 name: The preposition of the file name.
355- data_type: Specify with type: 'binary' for raw data and 'ascii' for csv data.
473+ data_type: Specify data type: 'binary' for raw data and 'ascii' for csv data.
356474 waiting_time: Time to wait for saving the file. In seconds.
357475 """
358476 data_type = self ._data_type_check (data_type )
359477
360478 # Stop in order to acquire the data
361- self ._scpi_protocol . write ( ":STOP" )
479+ self .stop ( )
362480 # Parameters of the saved file
363- self ._scpi_protocol . write ( ":WAV:FORM BYTE " )
481+ self .set_data_format ( "byte " )
364482 _ = self .get_number_data_points ()
365483 self ._scpi_protocol .write (f":FILE:SAVE:NAME { name } " )
366484 self ._scpi_protocol .write (f":FILE:SAVE:{ data_type } :EXECute" )
367485 # waits until the save is completed
368486 time .sleep (waiting_time )
369487 # Starts again, notice that at least a time of 10*time_division is needed to get a full spectrum after starting
370- self ._scpi_protocol . write ( ":START" )
488+ self .start ( )
371489
372490 @rpc_method
373491 def find_file_name (self , name : str , select_files : str = "last" ) -> list [str ]:
@@ -428,23 +546,23 @@ def copy_file(self, name: str, destination: str, select_files: str = "last") ->
428546 shutil .copy (f"{ self ._directory_oscilloscope } { f } " , destination )
429547
430548 @rpc_method
431- def delete_file (self , name : str , select_files : str = "last" , data_type : str = "binary" ) -> None :
549+ def delete_file (self , name : str , select_files : str = "last" , data_format : str = "binary" ) -> None :
432550 """Deletes files in the oscilloscope with names 'nameXXX.csv'.
433551 It can be chosen whether to delete the last file created with that name (higher XXX) with select_files = 'last'
434552 or to delete all of them with select_files = 'all'.
435553
436554 Parameters:
437555 name: Preposition of file names to find.
438556 select_files: Optional parameter to select only the "last" file (default) or "all" the files.
439- data_type : Specify with type: 'binary' for raw data and 'ascii' for csv data file.
557+ data_format : Specify with type: 'binary' for raw data and 'ascii' for csv data file.
440558 """
441- data_type = self ._data_type_check ( data_type )
559+ data_format = self ._data_format_check ( data_format )
442560
443561 # Stops in order to delete the data
444562 self .stop ()
445563 file_names = self .find_file_name (name , select_files )
446564 for f in file_names :
447- self ._scpi_protocol .write (f':FILE:DELete:{ data_type } :EXECute "{ f [:- 4 ]} "' )
565+ self ._scpi_protocol .write (f':FILE:DELete:{ data_format } :EXECute "{ f [:- 4 ]} "' )
448566
449567 # Starts again, notice that at least a time of 10*time_division is needed to get a full spectrum after starting
450568 self .start ()
0 commit comments