<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Basic spectroscopy measurements
This notebook is indended as a starting point into spectroscopic (frequency domain) measurements with qkit. We focus on measurements with vector network analyzers (VNAs) in the GHz regime.
The notebook briefly covers the qkit startup, initialization of measurement devices and the use of a sample object. For a detailed view on these topic you will find more information in their respective example notebooks. 
After creating of an object of the spectrum class and parsing the device information to the object we can start measuring. The spectrum class offers frequency domain VNA measurements with up to two additional external sweep parameters. The data is stored in a hdf5 file and can be view and further analyzed.
Step1:  Creating a spectrum object. The init takes<br>
* vna
Step2:  The general VNA parameters (probing frequencies, power, etc.) can be either set at the device or use the qkit instrument command. Using sample object attributes is possible as well.
Step4:  Next we want to record the VNA trace. In the spectrum object the parameter comment can be used for all non-digital information. Any digitally available instrument settings are saved automatically.
Step6:  For resonator measurements it is also possible to fit the probed resonance live while measureing. For the fits to converge the VNA parameters need to adjusted properly.
Step8:  A sweep parameter can be added to the spectum object and attributed to a sweep axis. In the example below we measure a resonator agains a applied current (ie for creating a magnetic field bias). The current is swept from 0 to 1A in 1mA steps. After the measurement the current is ramped down again. For one value of the x-parameter all VNA frequencies are probed before the next value of x is set.
Step10:  In the next example the VNA power is changed in addition to the current. Depending on the applied power, the number of averages is changed.
<ASSISTANT_TASK:>
Python Code:
# start qkit and import the needed modules. we here assume an already configured qkit measurement environment
import qkit
qkit.start()
from qkit.measure.spectroscopy import spectroscopy
import qkit.measure.samples_class as sc
import numpy as np
# initialize instruments; as an example we here work with a Keysight VNA, a Yokogawa current source, 
# and an Anritsu MW source
vna = qkit.instruments.create('vna', 'Keysight_VNA_E5071C', address='TCPIP::XXX.XXX.XXX.XXX')
yoko = qkit.instruments.create('yoko', 'Yokogawa_GS820', address='TCPIP::XXX.XXX.XXX.XXX')
mw_src = qkit.instruments.create('mw_src', 'Anritsu_MG37022', address='TCPIP::XXX.XXX.XXX.XXX')
# create/load sample object; (optional), for more information see the example notebook on the sample class.
sample_filepath = r'\some\path\sample_1.sample'
smpl = sc.Sample(sample_filepath)
Explanation: Basic spectroscopy measurements
This notebook is indended as a starting point into spectroscopic (frequency domain) measurements with qkit. We focus on measurements with vector network analyzers (VNAs) in the GHz regime.
The notebook briefly covers the qkit startup, initialization of measurement devices and the use of a sample object. For a detailed view on these topic you will find more information in their respective example notebooks. 
After creating of an object of the spectrum class and parsing the device information to the object we can start measuring. The spectrum class offers frequency domain VNA measurements with up to two additional external sweep parameters. The data is stored in a hdf5 file and can be view and further analyzed.
End of explanation
s = spectroscopy.spectrum(vna=vna, sample = smpl)
Explanation: Creating a spectrum object. The init takes<br>
* vna: instrument object (mandatory)<br>
* exp_name: string (optional) for a brief name of the experiment<br>
* sample: sample object (optional)
End of explanation
vna.set_centerfreq(smpl.fr)
vna.set_span(200e6)
vna.set_averages(10)
vna.set_Average(True)
Explanation: The general VNA parameters (probing frequencies, power, etc.) can be either set at the device or use the qkit instrument command. Using sample object attributes is possible as well.
End of explanation
comment = 
    * -30dB attenuator @ VNA
    
s.comment = comment
s.measure_1D()
Explanation: Next we want to record the VNA trace. In the spectrum object the parameter comment can be used for all non-digital information. Any digitally available instrument settings are saved automatically.
End of explanation
comment = 
    * measure resonance in reflection
    * -30dB attenuator @ VNA
    
s.set_resonator_fit(fit_function='circle_fit_reflection')
s.comment = comment
s.measure_1D()
Explanation: For resonator measurements it is also possible to fit the probed resonance live while measureing. For the fits to converge the VNA parameters need to adjusted properly.
End of explanation
comment = 
    * resonator vs applied current
    * -30dB step attenuator @ VNA
    
s.comment = comment
s.set_resonator_fit(fit_resonator=False)
# x_func gets called for every value of i. Here it would be not necessary to define a function for only the ramp fct,
# it will be useful in the next example.
def x_func(i):
    return yoko.ramp_current(i, 1e-3)
s.set_x_parameters(x_vec = np.arange(0, 1, 1e-3),
                  x_coordname = 'current',
                  x_set_obj = x_func,
                  x_unit = 'A')
s.measure_2D()
yoko.ramp_current(0, 1e-3)
Explanation: A sweep parameter can be added to the spectum object and attributed to a sweep axis. In the example below we measure a resonator agains a applied current (ie for creating a magnetic field bias). The current is swept from 0 to 1A in 1mA steps. After the measurement the current is ramped down again. For one value of the x-parameter all VNA frequencies are probed before the next value of x is set.
End of explanation
# the x/y-loops are interleaved, y changes "faster" than x: 
#     1) each VNA freq at y0 and x0
#     2) each VNA freq at y1 and x0
#     ...
#     3) each VNA freq at yN and x0
#     4) each VNA freq at y0 and x1
#     ...
#     5) each VNA freq at yN and xM
comment = 
    * resonator vs applied current at different power levels
    * -30dB step attenuator @ VNA
    
s.comment = comment
s.set_resonator_fit(fit_resonator=False)
def x_func(i):
    return yoko.ramp_current(i, 1e-3)
# Here the called function features more commends, i.e. change the number of averages at different powers to shorten the
# measurement time.
def y_func(i):
    if i < -25: vna.set_averages(10)
    else: vna.set_averages(5)
    return vna.set_power(i)
s.set_x_parameters(x_vec = np.arange(0, 1.001, 1e-3),
                  x_coordname = 'current',
                  x_set_obj = x_func,
                  x_unit = 'A')
s.set_y_parameters(y_vec = np.arange(-35, 11, 5),
                  y_coordname = 'power',
                  y_set_obj = y_func,
                  y_unit = 'dBm')
s.measure_3D()
yoko.ramp_current(0, 1e-3)
Explanation: In the next example the VNA power is changed in addition to the current. Depending on the applied power, the number of averages is changed.
End of explanation
<END_TASK>