# Resonator spectroscopy Using {mod}`.lockin` mode, we do a simple single-frequency sweep. The example script for performing the resonator spectroscopy experiment is in the file [presto-measure/sweep.py][sweep.py]. Here, we first run the experiment and fit the resonator response, then we have a deeper look at the code used to perform the measurement. You can create a new experiment and run it on your Presto. Be sure to change the parameters of `Sweep` to match your experiment and change `presto_address` to match the IP address of your Presto. ```python from sweep import Sweep experiment = Sweep( freq_center=6e9, freq_span=5e6, df=100e3, num_averages=100, amp=0.1, output_port=1, input_port=1, ) presto_address = "192.168.88.65" # your Presto IP address save_filename = experiment.run(presto_address) ``` Or you can also load older data: ```python experiment = Sweep.load("data/sweep_20220331_163206.h5") ``` In either case, we analyze the data to get a nice plot: ```python experiment.analyze() ``` ```{image} images/sweep_light.svg :align: center :class: only-light ``` ```{image} images/sweep_dark.svg :align: center :class: only-dark ``` When the experiment is done, click and drag on the plot panel to select a region for the fit. This will fit the resonance with the [circle-fit method][circfit] and print the relevant fitted parameters. Note that the Python package [resonator_tools][restools] should be installed for this to work. ``` ---------------- fr = 6028146404.689569 Qi = 221104.1461204515 Qc = 12105.122298585347 Ql = 11476.785411046161 kappa = 497983.1063246707 f_min = 6028320000.0 ---------------- ``` ## Code explanation Here we look under the hood of the `Sweep` class and discuss the main parts of the code. See the full source at [presto-measure/sweep.py][sweep.py]. We start by creating the `lck` object, an instance of the {class}`.Lockin` class: this will connect to the Presto unit at the IP address `presto_address` and set up the reference clock (`ext_ref_clk`). We also tell the API we want to use digital up- and down-conversion, by setting both {class}`.AdcMode` and {class}`.DacMode` to the `Mixed` variant. If we didn't want to use the digital mixers, we'd use the default `Direct` variant instead. See the documentation for the {class}`.Lockin` class and [Advanced tile configuration](<#adv tile>) for more details. ```python with lockin.Lockin( address=presto_address, ext_ref_clk=ext_ref_clk, adc_mode=AdcMode.Mixed, dac_mode=DacMode.Mixed, ) as lck: ``` We tune the integration bandwidth `df`: this ensures that the chosen `df` is commensurate with the sampling frequency and if not, it will slightly change it. You can read more in the documentation for {meth}`~.Lockin.tune`. ```python _, self.df = lck.tune(0.0, self.df) lck.set_df(self.df) ``` We set up the digital IQ mixer for up- and down-conversion using {meth}`.Hardware.configure_mixer`. We start with the first frequency here, and we'll update the frequency in a loop later on to perform the sweep. ```python freq = self.freq_arr[0] lck.hardware.configure_mixer(freq, in_ports=self.input_port, out_ports=self.output_port) ``` Once the digital up- and down-conversion is set up, we proceed with the intermediate frequency (IF) configuration. We create an {class}`.OutputGroup` and an {class}`.InputGroup`, each with a single frequency. These groups map a number of the available output and input tones to output and input ports. We opt for using zero IF, so the spectroscopy tone will coincide with the local-oscillator frequency set in the mixer. In an external, *analog* frequency conversion scheme you typically don't want to use zero IF to avoid $1/f$ noise and isolate your signal from mixing artifacts like LO leakage and mixer imbalance. Presto, on the other hand, uses *digital* frequency conversion so these concerns are not applicable and using zero IF is a good way of simplifying the signal chain. ```python og = lck.add_output_group(self.output_port, nr_freq=1) og.set_frequencies(0.0) og.set_amplitudes(self.amp) og.set_phases(0.0, 0.0) ig = lck.add_input_group(self.input_port, nr_freq=1) ig.set_frequencies(0.0) ``` We complete the initial configuration by applying the settings to the hardware with {meth}`~.Lockin.apply_settings`. At this point, Presto will start outputting the spectroscopy tone. At very low output power, {meth}`~.Lockin.set_dither` can help to reduce nonlinearity by adding pseudorandom noise to the output. ```python lck.set_dither(self.dither, self.output_port) lck.apply_settings() ``` To perform the actual frequency sweep, we loop through the frequency array and update the mixer settings. After making sure the settings are applied, we obtain the measured data with {meth}`~.Lockin.get_pixels`. ```python for ii, freq in enumerate(self.freq_arr): lck.hardware.configure_mixer(freq, in_ports=self.input_port, out_ports=self.output_port) lck.apply_settings() _d = lck.get_pixels(self.num_skip + self.num_averages, quiet=True) data_i = _d[self.input_port][1][:, 0] # in-phase data data_q = _d[self.input_port][2][:, 0] # quadrature data data = data_i.real + 1j * data_q.real # using zero IF self.resp_arr[ii] = np.mean(data[-self.num_averages :]) ``` Finally, after the sweep is complete, we turn off the spectroscopy tone by setting the amplitude to zero. ```python og.set_amplitudes(0.0) lck.apply_settings() ``` [restools]: https://github.com/sebastianprobst/resonator_tools [circfit]: https://arxiv.org/abs/1410.3365 [sweep.py]: https://github.com/intermod-pro/presto-measure/blob/master/sweep.py