.. |MLA (TM)| unicode:: MLA U+2122 .. index: Drive Constructor .. _drive-constructor-label: Drive Constructor ================= .. image:: ../Icons/drive_constructor.png :width: 20mm :align: left The ``Drive Constructor`` is initially selected in the ``Advanced`` pull-down menu. The Python scripting interface allows you configure the Multifrequency Lockin Analyzer |MLA (TM)|. You can use the Drive Constructor to create a frequency comb of drive tones, and to select the frequencies at which you want to perform lockin measurement. Before attempting to use the Drive Constructor, you should read the section on :ref:`intermodulation-measurement-label`. Configuring the |MLA (TM)| ++++++++++++++++++++++++++ The Python scripting interface has several pre-defined variables and and built-in functions to help in the construction of drive frequency combs. Using the interface requires knowledge of the Python programming language. A pull-down menu lists the name of various Python scripts, stored in **IMP Sessions and Settings/settings/drive_setups/user_drive_scripts.py**. These scripts are well commented to help you understand how they work. You create a new script by changing the name of an old script, and pressing ``Save``. Similarly, ``Delete`` will kill the currently selected script. .. |help_icon| image:: ../Icons/helpicon.png :width: 5mm The scripting interface has some useful functionality: * Change font size with ctrl+Plus / ctrl+Minus or ctrl+Mouse wheel. * Hit ctrl+Space for a list of reserved keywords * A quick help and link to this manual appears by clicking the IMP help icon |help_icon|. Pre-defined variables ''''''''''''''''''''' The following variables are defined in all scripts. For some of these variables you will want to change their values, or re-define them, but do not change their shape or data-type. Other variables should not be changed, but you will want to use their values. - **f0** [real, Hz] and **Q [real]** are the measured resonant frequency and quality factor stored in the :ref:`current-calibration-label`. If you have not run a calibration, these will be zero and your drive script may give an error if you do not set them to a reasonable value in your script. * **nfreq_out [integer]** and **nfreq_in [integer]** are the number of drive frequencies and response frequencies respectively. These numbers will depend on the |MLA (TM)| firmware that you are running. In the most common configurations, **nfreq_out** = **freq_in**, and in any case, **nfreq_out** < **nfreq_in**. Do not re-define these values. - **f_sample [samples per second]** is the sampling frequency of the |MLA (TM)|. Do not re-define this value. * **df** [real, Hz], **T** [real, sec], or **samples_per_pixel** [integer]. The measurement bandwidth is set by assigning a value to one (and only one) of these variables. **(df = 1/T = samples_per_pixel/f_sample)**. - **n [integer array]** or **f [real array, Hz]**. The frequencies are set by assigning values to either of these arrays **(f=n*df)**. The length of these arrays is **nfreq_in** and should not be changed. The first **nfreq_out** entries are the frequencies at which it is possible to drive. * **a [real array, arbitrary units]**. The drive amplitudes are set by assigning values to this array. The length of **a** is set to be **nfreq_out** and should not be changed. Amplitudes are set relative to each other, in arbitrary units. The maximum or peak amplitude is then set with the amplitude scaling factor (see below). - **p [real array, Radians]**. The drive phases are set by assigning values to this array. The length of **p** is set to **nfreq_out** and should not be changed. Phases are given in radians. * **asf [real]** is the amplitude scaling factor, a real number between zero and one. **asf = 1** gives maximum output voltage of the |MLA (TM)| at peak amplitude of the waveform. The default value that you get if you do not specify this variable is **asf = 0.1**. An error will appear in the debug window if **asf** is out of the range [0,1]. ( Advanced users only: **asf = -1** overrides the scaling of amplitudes, in which case the amplitude values must be given in ADU and care must be taken not to exceed the max output. ) - **out1_mask [Boolean array]** with **nfreq_out** elements. When the element is True (False) it will (will not) send the corresponding tone to port **OUT 1** of the |MLA (TM)|. The default value of this array is all True. * **out2_mask [Boolean array]** same as **out1_mask**, but for port **OUT 2** of the |MLA (TM)|. Note that it is possible to send the same signal to both output ports. The default value of this array is all False. - **input_multiplex [integer array]** with **nfreq_in** elements. Each element can take on the values 1,2,3 or 4. When an element is 1, the corresponding tone will be measured at port **IN 1**; when the element is 2, the corresponding tone will be measured at port **IN 2**... etc. Note that each tone can be measured at only one input port. The default value is all elements = 1. .. index: Tone index .. note:: The frequency array (**n** or **f**) does not have to be ordered in ascending frequency values. The frequency values can have any order, but there is a correspondence between the first **nfreq_out** elements of the frequency array and the elements of the amplitude array (**a**), phase array (**p**), and the output mask arrays. There is also a correspondence between all elements of the frequency array and input_multiplex array. We access each element of these arrays with the **tone index**. In summary: * |MLA (TM)| tones are specified by the indexed element of an array * n drive tones [0,1,2,...n] * m measurement tones [0,1,2,...n,...m] * first n elements correspond. n cantilever -> detector) is connected between the output and input ports of the |MLA (TM)| and it will not work properly when nonlinearity is present in the chain. If the drive voltage to the piezo shaker is not very high and the detector signal is not close to saturation, the AFM is very close to a linear chain. In this case, after pressing Configure Response, the actual cantilever motion have the frequency content given by the comb synthesized from your script. Setting the feedback ++++++++++++++++++++ ``Setup Feedback`` performs the necessary routines to make the free response at the tone index zero, correspond to 100% set-point value. By convention, the |MLA (TM)| calculates the feedback error signal from the amplitude of the response at the tone with index zero (frequency specified in the zeroth position of the **n** array or **f** array). You should make sure that your script puts the frequency at which you would like to run feedback in the zero position (see useful code block below). It is probably best to chose the frequency where you get the largest free response for feedback. More information on feedback can be found in the section on :ref:`intermodulation-measurement-label` (:ref:`feedback-label`) and in the section on :ref:`advanced-setup-label` (:ref:`feedback-setup-label`). .. _debug-label: Debugging a script ++++++++++++++++++ The ``Debug`` window allows you to test what is happening with your script. Error messages will appear in this window and you can print any variable here using the **dprint(var_name)** statement in your script. The script automatically prints relevant data for your comb in the Debug window. Useful Python code blocks +++++++++++++++++++++++++ Some useful blocks of code for making frequency combs are described below. These code blocks are used in the example scripts. You can copy and paste these in to your scripts. Tune **df** close to 500 Hz and set the frequencies to consecutive integer multiples of **df**, centered as close as possible to the resonant frequency **f0**:: df = tune_integer(500) f1 =f0 - (nfreq_out / 2) * df #start freq of comb n1 = round(f1/df) # integer closest f1 n = np.int_(arange(nfreq_out) + n1) # integer array of frequencies Tune **df** closer to 500 Hz and set the frequencies to consecutive integer multiples of **df**, centered as close as possible to the resonant frequency **f0**:: df = tune_round(500,f0) f1 =f0 - (nfreq_out / 2) * df #start freq of comb n1 = round(f1/df) # integer closest f1 n = np.int_(arange(nfreq_out) + n1) # integer array of frequencies Exchange the frequency with the index **mi**, with the feedback frequency at position **n[0]**:: #put frequency near resonance as first frequency for feedback mi=nfreq_out/2 #index of center of comb n[mi], n[0], a[mi], a[0], p[mi], p[0] = n[0], n[mi], a[0], a[mi], p[0], p[mi] Create an signal in the time domain, consisting of a rapidly oscillating component at the frequency **f_bar** and a slowly modulated amplitude **mod**. Fourier transform and pick out the **nfreq_out** components close to **f_bar** and set the amplitudes *a* and phases *p*:: N=2*int(2.*f_bar/df) #number of time samples, so f_max = 2*f_bar t = arange(0.,1,1./N)/df #construct signal x(t) in the time domain and FFT to get comb mod = 0.1*sin(2.*pi*arange(N)/N) # the modulation function x =(1.+ mod)*cos(2.*pi*f_bar*t) # x(t) x_hat = fft.rfft(x) # take components of comb, at frequencies given by n array a = abs(x_hat[n]) asf = 0.1 # adjust peak amplitude p = angle(x_hat[n]) Create an signal in the time domain, consisting of a rapidly oscillating component with a slowly modulated frequency **f_bar*mod(t)** . Fourier transform and pick out the **nfreq_out** components close to the center frequency **f_bar** and set the amplitudes *a* and phases *p*:: N=2*int(2.*n_bar) #number of time samples, so f_max = 2*f_bar t = (1./df)*arange(0.,1,1./N) #construct signal x(t) in the time domains x = cos(2. * pi * ( f_bar * t - delta_f /(4*pi*df)*sin(2*pi*df*t))) # FFT to get frequency comb x_hat = fft.rfft(x)/(N/2.) a = abs(x_hat[n]) asf = 0.1 # adjust peak amplitude p = angle(x_hat[n])