qreadout.py 10.3 KB
Newer Older
Oscar Gargiulo's avatar
Oscar Gargiulo committed
1

Oscar Gargiulo's avatar
update1    
Oscar Gargiulo committed
2
import numpy as np
Oscar Gargiulo's avatar
update3    
Oscar Gargiulo committed
3
import time
Oscar Gargiulo's avatar
Oscar Gargiulo committed
4
5
from .functions import load_HVI,digits_to_Vp

Oscar Gargiulo's avatar
update3    
Oscar Gargiulo committed
6

Oscar Gargiulo's avatar
Oscar Gargiulo committed
7

Oscar Gargiulo's avatar
Oscar Gargiulo committed
8
class Readout(object):
Oscar Gargiulo's avatar
Oscar Gargiulo committed
9
    def __init__(self,Cryostat_ID=None,pars_dict=None):
Oscar Gargiulo's avatar
Oscar Gargiulo committed
10
11
12
13
14
15
        """
        This class is used to perform a system readout:
        Args:
            - Cryostat_ID: None (def) or string, it specify the Cryostat so 
            that it can be used to read the base temp and insert it in the 
            measurement
Oscar Gargiulo's avatar
Oscar Gargiulo committed
16
            - pars_dict: a python dictionary with all the parameters for the 
Oscar Gargiulo's avatar
Oscar Gargiulo committed
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
            readout
        
        the dictionary used to perform a readout is the following:
            {'Readout generator': None,
             'LO generator': None,
             'Digitizer': None,
             'HVI_path': '',
             'Averages': 1,
             'Repetitions period': 1000,
             'Readout frequency':10,
             'Downmixer power':8,
             'Readout_pulse': {'Length':1000,'Delay':0}}
        
        the user must connect the instruments and use the dedicated functions
        to set the previous values.
        
        """
        if Cryostat_ID is None:
            print('No Cryostat_ID specified, no temperature will be measured')
            Cryostat_ID=''
37
38
39
40
        if type(Cryostat_ID)!=str:
            print('Cryostat_ID must be a string')
            raise Exception(TypeError)
        
Oscar Gargiulo's avatar
Oscar Gargiulo committed
41
            
Oscar Gargiulo's avatar
Oscar Gargiulo committed
42
43
        if pars_dict is None:
            self.pars_dict = {'Cryostat_ID':Cryostat_ID}
Oscar Gargiulo's avatar
bugfix1    
Oscar Gargiulo committed
44
        else:
Oscar Gargiulo's avatar
Oscar Gargiulo committed
45
            if type(pars_dict)!=dict:
Oscar Gargiulo's avatar
bugfix1    
Oscar Gargiulo committed
46
47
48
                print('exp_dict must be a python dictionary')
                raise Exception(TypeError)
            
Oscar Gargiulo's avatar
Oscar Gargiulo committed
49
50
            self.pars_dict = pars_dict
            self.pars_dict.update({'Cryostat_ID':Cryostat_ID})
Oscar Gargiulo's avatar
bugfix1    
Oscar Gargiulo committed
51
            
Oscar Gargiulo's avatar
Oscar Gargiulo committed
52
        self.pars_dict.update({'Readout generator': None,
Oscar Gargiulo's avatar
Oscar Gargiulo committed
53
54
55
56
57
58
59
60
                             'LO generator': None,
                             'Digitizer': None,
                             'Dig channel': 1,
                             'HVI': None,
                             'Averages': 1,
                             'Repetitions period': 1000,
                             'DM frequency':10,
                             'Downmixer power':8,
Oscar Gargiulo's avatar
Oscar Gargiulo committed
61
                             #'Acquisition delay fix':320,
Oscar Gargiulo's avatar
Oscar Gargiulo committed
62
63
64
65
                             })
        self._rogen = None
        self._logen = None
        self._dig = None
Oscar Gargiulo's avatar
Oscar Gargiulo committed
66
        self.cryo_id = Cryostat_ID
Oscar Gargiulo's avatar
Oscar Gargiulo committed
67
##----------------------------------------------------------------------------- Set functions ----
Oscar Gargiulo's avatar
Oscar Gargiulo committed
68
69
    
    
Oscar Gargiulo's avatar
Oscar Gargiulo committed
70
##-------------------------------------------------------------------------------------------    
Oscar Gargiulo's avatar
Oscar Gargiulo committed
71
    
Oscar Gargiulo's avatar
Oscar Gargiulo committed
72
73
74
75
76
    def set_HVI(self,path,dig,awg):
        """Function used to set the HVI, requires the path and initialized
        digitizer and awg classes"""
        
        self._HVI = load_HVI(path,dig,awg)
Oscar Gargiulo's avatar
Oscar Gargiulo committed
77
        self.pars_dict.update({'HVI': path})
Oscar Gargiulo's avatar
Oscar Gargiulo committed
78
        self._dig = dig
Oscar Gargiulo's avatar
Oscar Gargiulo committed
79
        self.pars_dict.update({'Digitizer':{'Slot':dig._slot,'Chassis': dig._chassis}})
Oscar Gargiulo's avatar
Oscar Gargiulo committed
80
    
Oscar Gargiulo's avatar
Oscar Gargiulo committed
81
82
83
84
    def dig_ch(self,chn=None):
        """Function used to get(def)/set which channel of the digitizer will be 
        used for readout"""
        if chn is None:
Oscar Gargiulo's avatar
Oscar Gargiulo committed
85
            return self.pars_dict['Dig channel']
Oscar Gargiulo's avatar
Oscar Gargiulo committed
86
        else:
Oscar Gargiulo's avatar
Oscar Gargiulo committed
87
            
Oscar Gargiulo's avatar
Oscar Gargiulo committed
88
            self.pars_dict.update({'Dig channel': int(chn)})
Oscar Gargiulo's avatar
Oscar Gargiulo committed
89
    
Oscar Gargiulo's avatar
Oscar Gargiulo committed
90
91
92
93
94
95
96
97
98
99
100
    def rogen(self,generator=None):
        """Function used to get(def)/set which generator will be used for the
        readout pulse, it must be a Instruments.SG class object"""
        if generator is None:
            if self._rogen is None:
                print('No generator assigned')
            else:
                print(self._rogen.id)
                self._rogen.get_parameters()
        else:
            self._rogen = generator
Oscar Gargiulo's avatar
Oscar Gargiulo committed
101
            self.pars_dict.update({'Readout generator': generator.id})
Oscar Gargiulo's avatar
Oscar Gargiulo committed
102
    
Oscar Gargiulo's avatar
Oscar Gargiulo committed
103
104
105
106
107
108
109
110
111
112
113
    def logen(self,generator=None):
        """Function used to get(def)/set which generator will be used for 
        downmixing, it must be a Instruments.SG class object"""
        if generator is None:
            if self._logen is None:
                print('No generator assigned')
            else:
                print(self._logen.id)
                self._logen.get_parameters()
        else:
            self._logen = generator
Oscar Gargiulo's avatar
Oscar Gargiulo committed
114
            self.pars_dict.update({'LO generator': generator.id})
Oscar Gargiulo's avatar
update3    
Oscar Gargiulo committed
115
    
Oscar Gargiulo's avatar
Oscar Gargiulo committed
116
117
118
    def averages(self,averages=None):
        """Function used to get(def)/set the averages, minimum is 1"""
        if averages is None:
Oscar Gargiulo's avatar
Oscar Gargiulo committed
119
            return self.pars_dict['Averages']
Oscar Gargiulo's avatar
Oscar Gargiulo committed
120
121
122
        else:
            averages = int(averages)
            if averages<0:
Oscar Gargiulo's avatar
Oscar Gargiulo committed
123
124
125
126
127
128
129
130
131
132
133
134
135
136
                raise self._ROEXC('ValueError','averages must be an integer larger than 1\n')
            self.pars_dict.update({'Averages': averages})
    """
    def acq_delay(self,delay=None):
        '''Function used to get(def)/set the acquisition delay fix, 320 def.
        It must be a multiple of 10ns'''
        if delay is None:
            return self.pars_dict['Acquisition delay fix']
        else:
            delay = int(delay)
            if delay%10 != 0:
                raise self._ROEXC('ValueError','delay must be a multiple of 10.')
            self.pars_dict.update({'Acquisition delay fix': delay})
    """
Oscar Gargiulo's avatar
Oscar Gargiulo committed
137
138
139
140
    def repetitions_period(self,rep_per=None):
        """Function used to get(def)/set the wait time between measurements,
        in microseconds"""
        if rep_per is None:
Oscar Gargiulo's avatar
Oscar Gargiulo committed
141
            return self.pars_dict['Repetitions period']
Oscar Gargiulo's avatar
Oscar Gargiulo committed
142
        else:
Oscar Gargiulo's avatar
Oscar Gargiulo committed
143
            self.pars_dict.update({'Repetitions period': rep_per})
Oscar Gargiulo's avatar
update1    
Oscar Gargiulo committed
144
    
Oscar Gargiulo's avatar
Oscar Gargiulo committed
145
146
147
148
    def DM_frequency(self,dm_freq=None):
        """Function used to get(def)/set the down mixed frequency, in MHz. def
        is 10 MHz"""
        if dm_freq is None:
Oscar Gargiulo's avatar
Oscar Gargiulo committed
149
            return self.pars_dict['DM frequency']
Oscar Gargiulo's avatar
Oscar Gargiulo committed
150
        else:
Oscar Gargiulo's avatar
Oscar Gargiulo committed
151
            self.pars_dict.update({'DM frequency': dm_freq})
Oscar Gargiulo's avatar
update3    
Oscar Gargiulo committed
152

Oscar Gargiulo's avatar
Oscar Gargiulo committed
153
154
155
    def DM_power(self,dm_power=None):
        """Function used to get(def)/set the LO power in dBm, def is 8 dBm"""
        if dm_power is None:
Oscar Gargiulo's avatar
Oscar Gargiulo committed
156
            return self.pars_dict['Downmixer power']
Oscar Gargiulo's avatar
Oscar Gargiulo committed
157
        else:
Oscar Gargiulo's avatar
Oscar Gargiulo committed
158
            self.pars_dict.update({'Downmixer power': dm_power})
Oscar Gargiulo's avatar
Oscar Gargiulo committed
159
160

    def __set_LO(self):
Aleksei's avatar
Aleksei committed
161
162
        #freq = np.round(self._rogen.frequency()+self.DM_frequency()*1e-3,10)
        freq = np.round(self._rogen.frequency(),10)
Oscar Gargiulo's avatar
Oscar Gargiulo committed
163
164
165
166
167
168
169
170
171
172
173
174
175
176
        self._logen.frequency(freq)
        self._logen.power(self.DM_power())
        self._logen.output(1)
        
    def __set_dig(self,acq_length=None,acq_delay=None):
        Channel_parameters = self._dig.channel(self.dig_ch()).pars_dict
        
        Channel_parameters['Cycles']= self.averages()
        if acq_length is None:
            Channel_parameters['Points']= int(self._rogen.instr.pulse_width()*self._dig.SF())
        else:
            Channel_parameters['Points']= int(acq_length*self._dig.SF())
            
        if acq_delay is not None:
Oscar Gargiulo's avatar
Oscar Gargiulo committed
177
178
            Channel_parameters['Delay'] = acq_delay

Oscar Gargiulo's avatar
Oscar Gargiulo committed
179
180
        self._dig.channel(self.dig_ch()).pars_dict = Channel_parameters
        self._dig.set_channel(self.dig_ch())
Oscar Gargiulo's avatar
update3    
Oscar Gargiulo committed
181
        
Oscar Gargiulo's avatar
Oscar Gargiulo committed
182
183
        self._dig.register(4,int(self.repetitions_period()*100))
        self._dig.register(2,int(self.averages()))
Oscar Gargiulo's avatar
Oscar Gargiulo committed
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
        if acq_delay is None:
            self._dig.register(0,int(self._rogen.instr.pulse_delay()/10))
            
        else:
            self._dig.register(0,int(acq_delay/10))
   
    def generators_parameters_list(self):
        tmp={'DIG':self._dig.channel(self.dig_ch()).pars_dict}
        
        reg_list={'DIG-R0': self._dig.register(0),
                  'DIG-R1': self._dig.register(1),
                  'DIG-R2': self._dig.register(2),
                  'DIG-R3': self._dig.register(3),
                  'DIG-R4': self._dig.register(4)
                }
        
        tmp.update(reg_list)
        tmp.update({self._rogen.id:self._rogen.parameters_dictionary()})
        tmp.update({self._logen.id:self._logen.parameters_dictionary()})
        return tmp
Oscar Gargiulo's avatar
update3    
Oscar Gargiulo committed
204
        
Oscar Gargiulo's avatar
Oscar Gargiulo committed
205
206
207
#------------------------------------------------------------------------------- Custom exceptions
    class __BASEEXC(Exception):
        pass
Oscar Gargiulo's avatar
Oscar Gargiulo committed
208
    
Oscar Gargiulo's avatar
Oscar Gargiulo committed
209
210
211
212
213
    class _ROEXC(__BASEEXC):
        def __init__(self,Expression,Message):
            self.Expression = Expression
            self.Message = Message

Oscar Gargiulo's avatar
Oscar Gargiulo committed
214
#------------------------------------------------------------------------------- readout waves        
Oscar Gargiulo's avatar
Oscar Gargiulo committed
215
216
217
218
219
    def readout_waves(self,channel_list=None,acq_length=None,acq_delay=None):
        """Function used to perform a readout and get all the waves in the pc 
        memory. It is possible to specify multiples channels, acq_length and 
        acq_delay in ns for future implementation or debug"""    
        if self._HVI is None:
Oscar Gargiulo's avatar
Oscar Gargiulo committed
220
            print('Load HVI first')
Oscar Gargiulo's avatar
update3    
Oscar Gargiulo committed
221
222
            return
        
Oscar Gargiulo's avatar
Oscar Gargiulo committed
223
        self.__set_LO()
Oscar Gargiulo's avatar
Oscar Gargiulo committed
224
225
226
        if acq_delay is not None:
            Channel_parameters = self._dig.channel(self.dig_ch()).pars_dict
            old_delay = Channel_parameters['Delay']
Oscar Gargiulo's avatar
Oscar Gargiulo committed
227
228
229
230
        self.__set_dig(acq_length,acq_delay)
        
        self._HVI.start()
        
Oscar Gargiulo's avatar
update3    
Oscar Gargiulo committed
231
        
Oscar Gargiulo's avatar
Oscar Gargiulo committed
232
        
Oscar Gargiulo's avatar
update3    
Oscar Gargiulo committed
233
234
        try:
            start_time = time.time()
Oscar Gargiulo's avatar
Oscar Gargiulo committed
235
            while(self._dig.register(3)==0):
Oscar Gargiulo's avatar
update3    
Oscar Gargiulo committed
236
237
                time.sleep(0.001)
                if time.time()-start_time > 20:
Oscar Gargiulo's avatar
Oscar Gargiulo committed
238
                    raise self._ROEXC('TIMEOUT','Exceeded 20 sec wait time in the acquisition loop')
Oscar Gargiulo's avatar
update3    
Oscar Gargiulo committed
239
240
241
242
243
        except KeyboardInterrupt:
            print('Interrupted')
            raise KeyboardInterrupt
        
        
Oscar Gargiulo's avatar
Oscar Gargiulo committed
244
245
        if channel_list is None:
            channel_list = [self.dig_ch(),]
Oscar Gargiulo's avatar
update3    
Oscar Gargiulo committed
246
247
        
        data = []
Oscar Gargiulo's avatar
Oscar Gargiulo committed
248
249
        for c in channel_list:
            data.append(self._dig.get_wave(c))
Oscar Gargiulo's avatar
update3    
Oscar Gargiulo committed
250
        
Oscar Gargiulo's avatar
Oscar Gargiulo committed
251
        self._dig.register(3,1) #stops the HVI
Oscar Gargiulo's avatar
update3    
Oscar Gargiulo committed
252
        
Oscar Gargiulo's avatar
Oscar Gargiulo committed
253
254
255
256
        if acq_delay is not None:
            Channel_parameters = self._dig.channel(self.dig_ch()).pars_dict
            Channel_parameters['Delay'] = old_delay
        
Oscar Gargiulo's avatar
Oscar Gargiulo committed
257
        if len(channel_list)==1:
Oscar Gargiulo's avatar
update3    
Oscar Gargiulo committed
258
259
260
261
            return data[0]
        else:
            return data
                
Oscar Gargiulo's avatar
Oscar Gargiulo committed
262
263
264
    def readout(self,volt_conversion=True):
        """Function used to average the acquisitions and get the amplitude,
        if volt_conversion is True (def), the amplitude will be in Volts"""
Oscar Gargiulo's avatar
update3    
Oscar Gargiulo committed
265
            
Oscar Gargiulo's avatar
Oscar Gargiulo committed
266
        data = self.readout_waves()
Oscar Gargiulo's avatar
update3    
Oscar Gargiulo committed
267
268
269
270
271
272
        
        tot = np.average(data,0)
        fa = np.fft.fft(tot)/len(tot)
        
        
        
Oscar Gargiulo's avatar
Oscar Gargiulo committed
273
        index = np.int(np.round(self._rogen.instr.pulse_width()*self.DM_frequency()/1e3,12))
Oscar Gargiulo's avatar
update3    
Oscar Gargiulo committed
274
        
Oscar Gargiulo's avatar
bugfix1    
Oscar Gargiulo committed
275
        tmp = np.abs(fa[index])*2
Oscar Gargiulo's avatar
update3    
Oscar Gargiulo committed
276
277
278
        
        
        if volt_conversion is True:
Oscar Gargiulo's avatar
Oscar Gargiulo committed
279
            tmp = digits_to_Vp(tmp,self._dig.amplitude(self.dig_ch()))
Oscar Gargiulo's avatar
bugfix1    
Oscar Gargiulo committed
280
    
Oscar Gargiulo's avatar
Oscar Gargiulo committed
281
        return tmp