Repository: microsoft/Qcodes

Issue:
RTO1024/RTO1000 scope driver issues
I have found several problems with the RTO scope driver. 

1. The when using the HD mode on an RTO1024 the bandwidht get's set to 1MHz and cannot be changed from there. 
This would not be problematic if this mode was not needed but it is also used to set the dataformat which uses the same parameter. 

Observe the following difference and not the (spurious) staircase due to digitization in the left figure. 

![image](https://user-images.githubusercontent.com/6142932/32517253-1e69d472-c406-11e7-80bd-bc421a5c9c39.png)

Both are the same trace measured. The one on the right is measured by a little hack in the driver. 
The second trace contains a scaling error on the y-axis but I'm sure that can be fixed quite easilty. 
![image](https://user-images.githubusercontent.com/6142932/32517319-4e4e5352-c406-11e7-8372-369cce667340.png)

I added a screenshot from the scope itself as reference. 
![image](https://user-images.githubusercontent.com/6142932/32517336-623ff820-c406-11e7-802a-d3c9c33c31d3.png)


2. Recent QCoDeS updates make this driver give a warning upon instantiation. IMO this should be fixed. 
![image](https://user-images.githubusercontent.com/6142932/32517377-914e9216-c406-11e7-9e1c-06bc77b27956.png)



Discussion:
When using this workaround it is important to also use the following change for the rescaling of the data. 

![image](https://user-images.githubusercontent.com/6142932/32518174-18f63c1c-c409-11e7-88da-76a9bcd86096.png)

Hi @AdriaanRol, thanks for catching this one. I don't have access to an instrument right now, but let's see if I can make sense of what is going on.

From how I understood the manual, one can not acquire in 16 bit unless the instrument is in high definition mode. Is you left figure really 16 bit with the high definition mode off? (nevermind the data _format_, are there really 2^16 unique values?) If so, then we might remove the `high_definition_state` paramater. If not, I think a real solution is to make the high definition mode not limit the bandwidth. Could you try using the `HDEFinition:BWIDth` command to see if you can set the high definition mode bandwidth?

I am sorry that I did not realise that the high definition mode comes with a bandwidth of its own.
I just figured out how to increase the bandwidth when using HD mode. 

I think this should also be implemented when addressing this issue. 

Tested using : 
![image](https://user-images.githubusercontent.com/6142932/33663237-71146864-da8f-11e7-928b-bedf1fb97af7.png)
Where rto is an instance of the RTO instrument driver. 

Below is an excerpt of the manual (page 1059) 
![image](https://user-images.githubusercontent.com/6142932/33663301-b0c697a2-da8f-11e7-9b3d-1d865803e4e2.png)


Yes, that's what I tried to hint at above. Glad that we agree 😄 

Do you want to make a PR adding this parameter? I can also do it, but it will not happen this week.
Or actually, an RTO 1044 just landed on my desk for an entirely different reason. Let me seize the opportunity to fix this.

Resolving patch:
diff --git a/qcodes/instrument_drivers/rohde_schwarz/RTO1000.py b/qcodes/instrument_drivers/rohde_schwarz/RTO1000.py
--- a/qcodes/instrument_drivers/rohde_schwarz/RTO1000.py
+++ b/qcodes/instrument_drivers/rohde_schwarz/RTO1000.py
@@ -1,8 +1,11 @@
 # All manual references are to R&S RTO Digital Oscilloscope User Manual
 # for firmware 3.65, 2017
 
-import numpy as np
+import logging
 import warnings
+import time
+
+import numpy as np
 from distutils.version import LooseVersion
 
 from qcodes import Instrument
@@ -11,6 +14,8 @@
 from qcodes.utils import validators as vals
 from qcodes.instrument.parameter import ArrayParameter
 
+log = logging.getLogger(__name__)
+
 
 class ScopeTrace(ArrayParameter):
 
@@ -37,18 +42,8 @@ def prepare_trace(self) -> None:
         """
         Prepare the scope for returning data, calculate the setpoints
         """
-
-        # we must have this nesting, since the parameter high_definition_state
-        # only exists if HD
-        if self.channel._parent.HD:
-            if self.channel._parent.high_definition_state() == 'ON':
-                dataformat = 'INT,16'
-            else:
-                dataformat = 'INT,8'
-        else:
-            dataformat = 'INT,8'
-
-        self.channel._parent.dataformat(dataformat)
+        # We always use 16 bit integers for the data format
+        self.channel._parent.dataformat('INT,16')
         # ensure little-endianess
         self.channel._parent.write('FORMat:BORder LSBFirst')
         # only export y-values
@@ -82,16 +77,28 @@ def prepare_trace(self) -> None:
         # we must ensure that all this took effect before proceeding
         self.channel._parent.ask('*OPC?')
 
-    def get(self):
+    def get_raw(self):
         """
         Returns a trace
         """
 
+        instr = self.channel._parent
+
         if not self._trace_ready:
             raise ValueError('Trace not ready! Please call '
                              'prepare_trace().')
 
-        vh = self.channel._parent.visa_handle
+        if instr.run_mode() == 'RUN Nx SINGLE':
+            N = instr.num_acquisitions()
+            M = instr.completed_acquisitions()
+            log.info('Acquiring {} traces.'.format(N))
+            while M < N:
+                log.info('Acquired {}:{} traces.'.format(M, N))
+                time.sleep(0.25)
+                M = instr.completed_acquisitions()
+
+        log.info('Acquisition completed. Polling trace from instrument.')
+        vh = instr.visa_handle
         vh.write('CHANnel{}:DATA?'.format(self.channum))
         raw_vals = vh.read_raw()
 
@@ -101,7 +108,7 @@ def get(self):
         # cut of the header and the trailing '\n'
         raw_vals = raw_vals[2+num_length:-1]
 
-        dataformat = self.channel._parent.dataformat.get_latest()
+        dataformat = instr.dataformat.get_latest()
 
         if dataformat == 'INT,8':
             int_vals = np.fromstring(raw_vals, dtype=np.int8, count=no_points)
@@ -114,13 +121,9 @@ def get(self):
 
         scale = self.channel.scale()
         no_divs = 10  # TODO: Is this ever NOT 10?
-        if self.channel._parent.HD:
-            if self.channel._parent.high_definition_state() == 'ON':
-                quant_levels = 253*256
-            else:
-                quant_levels = 253
-        else:
-            quant_levels = 253
+
+        # we always export as 16 bit integers
+        quant_levels = 253*256
         conv_factor = scale*no_divs/quant_levels
         output = conv_factor*int_vals + self.channel.offset()
 
@@ -287,7 +290,7 @@ def __init__(self, name: str, address: str,
                  model: str=None, timeout: float=5.,
                  HD: bool=True,
                  terminator: str='\n',
-                 **kwargs):
+                 **kwargs) -> None:
         """
         Args:
             name: name of the instrument
@@ -309,6 +312,10 @@ def __init__(self, name: str, address: str,
         # the user must provide the model manually
         firmware_version = self.get_idn()['firmware']
 
+        if LooseVersion(firmware_version) < LooseVersion('3'):
+            log.warning('Old firmware version detected. This driver may '
+                        'not be compatible. Please upgrade your firmware.')
+
         if LooseVersion(firmware_version) >= LooseVersion('3.65'):
             # strip just in case there is a newline character at the end
             self.model = self.ask('DIAGnostic:SERVice:WFAModel?').strip()
@@ -424,13 +431,28 @@ def __init__(self, name: str, address: str,
         #########################
         # Acquisition
 
+        # I couldn't find a way to query the run mode, so we manually keep
+        # track of it. It is very important when getting the trace to make
+        # sense of completed_acquisitions
+        self.add_parameter('run_mode',
+                           label='Run/acqusition mode of the scope',
+                           get_cmd=None,
+                           set_cmd=None)
+
+        self.run_mode('RUN CONT')
+
         self.add_parameter('num_acquisitions',
-                           label='Number of single acquisitions',
-                           get_cmd='ACQuire:COUNt ?',
+                           label='Number of single acquisitions to perform',
+                           get_cmd='ACQuire:COUNt?',
                            set_cmd='ACQuire:COUNt {}',
                            vals=vals.Ints(1, 16777215),
                            get_parser=int)
 
+        self.add_parameter('completed_acquisitions',
+                           label='Number of completed acquisitions',
+                           get_cmd='ACQuire:CURRent?',
+                           get_parser=int)
+
         self.add_parameter('sampling_rate',
                            label='Sample rate',
                            docstring='Number of averages for measuring '
@@ -468,13 +490,19 @@ def __init__(self, name: str, address: str,
                                get_cmd='HDEFinition:STAte?',
                                val_mapping={'ON': 1, 'OFF': 0})
 
+            self.add_parameter('high_definition_bandwidth',
+                               label='High definition mode bandwidth',
+                               set_cmd='HDEFinition:BWIDth {}',
+                               get_cmd='HDEFinition:BWIDth?',
+                               unit='Hz',
+                               get_parser=float,
+                               vals=vals.Numbers(1e4, 1e9))
+
         # Add the channels to the instrument
         for ch in range(1, self.num_chans+1):
             chan = ScopeChannel(self, 'channel{}'.format(ch), ch)
             self.add_submodule('ch{}'.format(ch), chan)
 
-        self.add_function('run_cont', call_cmd='RUN')
-        self.add_function('run_single', call_cmd='SINGle')
         self.add_function('stop', call_cmd='STOP')
         self.add_function('reset', call_cmd='*RST')
         self.add_function('opc', call_cmd='*OPC?')
@@ -484,6 +512,20 @@ def __init__(self, name: str, address: str,
 
         self.connect_message()
 
+    def run_cont(self) -> None:
+        """
+        Set the instrument in 'RUN CONT' mode
+        """
+        self.write('RUN')
+        self.run_mode.set('RUN CONT')
+
+    def run_single(self) -> None:
+        """
+        Set the instrument in 'RUN Nx SINGLE' mode
+        """
+        self.write('SINGLE')
+        self.run_mode.set('RUN Nx SINGLE')
+
     #########################
     # Specialised set/get functions
 


Test patch:
diff --git a/qcodes/tests/drivers/test_rto_1000.py b/qcodes/tests/drivers/test_rto_1000.py
new file mode 100644
--- /dev/null
+++ b/qcodes/tests/drivers/test_rto_1000.py
@@ -0,0 +1,24 @@
+import pytest
+
+from qcodes.instrument_drivers.rohde_schwarz.RTO1000 import RTO1000
+import qcodes.instrument.sims as sims
+visalib = sims.__file__.replace('__init__.py', 'RTO_1000.yaml@sim')
+
+
+@pytest.fixture(scope='function')
+def driver():
+    rto_sim = RTO1000('rto_sim',
+                      address='GPIB::1::65535::INSTR',
+                      visalib=visalib,
+                      model='RTO1044'
+                      )
+    yield rto_sim
+
+    rto_sim.close()
+
+
+def test_init(driver):
+
+    idn_dict = driver.IDN()
+
+    assert idn_dict['vendor'] == 'QCoDeS'