forked from sinara-hw/datasheets
4410-4412: get example code from file
Updates #24. Added example files for DDS and SUServo respectively.
This commit is contained in:
parent
9f6056f615
commit
8404fed3da
144
4410-4412.tex
144
4410-4412.tex
@ -4,8 +4,6 @@
|
||||
\usepackage{minted}
|
||||
\usepackage{tcolorbox}
|
||||
\usepackage{etoolbox}
|
||||
\BeforeBeginEnvironment{minted}{\begin{tcolorbox}[colback=white]}%
|
||||
\AfterEndEnvironment{minted}{\end{tcolorbox}}%
|
||||
|
||||
\usepackage[justification=centering]{caption}
|
||||
|
||||
@ -69,6 +67,11 @@ RF switches (1ns temporal resolution) on each channel provides 70 dB isolation.
|
||||
\newcommand*{\MyLabel}[3][2cm]{\parbox{#1}{\centering #2 \\ #3}}
|
||||
\newcommand*{\MymyLabel}[3][4cm]{\parbox{#1}{\centering #2 \\ #3}}
|
||||
\newcommand{\repeatfootnote}[1]{\textsuperscript{\ref{#1}}}
|
||||
\newcommand{\inputcolorboxminted}[2]{%
|
||||
\begin{tcolorbox}[colback=white]
|
||||
\inputminted[#1, gobble=4]{python}{#2}
|
||||
\end{tcolorbox}
|
||||
}
|
||||
|
||||
\begin{figure}[h]
|
||||
\centering
|
||||
@ -688,37 +691,13 @@ Generate a 10MHz sinusoid from RF0 with full scale amplitude, attenuated by 6 dB
|
||||
Both the CPLD and the DDS channels should be initialized.
|
||||
By default, AD9910 single-tone profiles are programmed to profile 7.
|
||||
|
||||
\begin{minted}{python}
|
||||
@kernel
|
||||
def run(self):
|
||||
self.core.reset()
|
||||
self.cpld.init()
|
||||
self.dds0.init()
|
||||
self.dds0.cfg_sw(True)
|
||||
self.dds0.set_att(6.*dB)
|
||||
self.dds0.set(10*MHz)
|
||||
\end{minted}
|
||||
\inputcolorboxminted{firstline=11,lastline=18}{examples/dds.py}
|
||||
|
||||
If the synchronization feature of AD9910 was enabled, RF signal across different channels of the same Urukul can be synchronized.
|
||||
For example, phase-coherent RF signal can be produced on both channel 0 and channel 1 after configuring an appropriate phase mode.
|
||||
\begin{minted}{python}
|
||||
@kernel
|
||||
def run(self):
|
||||
self.core.reset()
|
||||
self.cpld.init()
|
||||
|
||||
self.dds0.init()
|
||||
self.dds0.cfg_sw(True)
|
||||
self.dds0.set_phase_mode(PHASE_MODE_TRACKING)
|
||||
self.dds0.set_att(6.*dB)
|
||||
self.dds1.init()
|
||||
self.dds1.cfg_sw(True)
|
||||
self.dds1.set_phase_mode(PHASE_MODE_TRACKING)
|
||||
self.dds1.set_att(6.*dB)
|
||||
\inputcolorboxminted{firstline=28,lastline=43}{examples/dds.py}
|
||||
|
||||
self.dds0.set(frequency=10*MHz, phase=0.0)
|
||||
self.dds1.set(frequency=10*MHz, phase=0.25) # 0.25 turns phase offset
|
||||
\end{minted}
|
||||
Note that the phase difference between the 2 channels might not be exactly 0.25 turns, but it is a constant.
|
||||
It can be negated by adjusting the \texttt{phase} parameter.
|
||||
|
||||
@ -727,49 +706,7 @@ It can be negated by adjusting the \texttt{phase} parameter.
|
||||
This examples demonstrates that the RF signal can be modulated by amplitude using the RAM modulation feature of AD9910.
|
||||
By default, RAM profiles are programmed to profile 0.
|
||||
|
||||
\begin{minted}{python}
|
||||
from artiq.coredevice.ad9910 import RAM_MODE_CONT_RAMPUP
|
||||
|
||||
def prepare(self):
|
||||
self.amp = [0.0, 0.0, 0.0, 0.7, 0.0, 0.7, 0.7] # Reversed Order
|
||||
self.asf_ram = [0] * len(self.amp)
|
||||
|
||||
@kernel
|
||||
def init_dds(self, dds):
|
||||
self.core.break_realtime()
|
||||
dds.init()
|
||||
dds.set_att(6.*dB)
|
||||
dds.cfg_sw(True)
|
||||
|
||||
@kernel
|
||||
def configure_ram_mode(self, dds):
|
||||
self.core.break_realtime()
|
||||
dds.set_cfr1(ram_enable=0)
|
||||
self.cpld.io_update.pulse_mu(8)
|
||||
self.cpld.set_profile(0) # Enable the corresponding RAM profile
|
||||
# Profile 0 is the default
|
||||
dds.set_profile_ram(start=0, end=len(self.asf_ram)-1,
|
||||
step=250, profile=0, mode=RAM_MODE_CONT_RAMPUP)
|
||||
self.cpld.io_update.pulse_mu(8)
|
||||
dds.amplitude_to_ram(self.amp, self.asf_ram)
|
||||
dds.write_ram(self.asf_ram)
|
||||
|
||||
self.core.break_realtime()
|
||||
dds.set(frequency=5*MHz, ram_destination=RAM_DEST_ASF)
|
||||
# Pass osk_enable=1 to set_cfr1() if it is not an amplitude RAM
|
||||
dds.set_cfr1(ram_enable=1, ram_destination=RAM_DEST_ASF)
|
||||
|
||||
self.cpld.io_update.pulse_mu(8)
|
||||
|
||||
@kernel
|
||||
def run(self):
|
||||
self.core.reset()
|
||||
self.core.break_realtime()
|
||||
self.cpld.init()
|
||||
|
||||
self.init_dds(self.dds0)
|
||||
self.configure_ram_mode(self.dds0)
|
||||
\end{minted}
|
||||
\inputcolorboxminted{firstline=53,lastline=91}{examples/dds.py}
|
||||
|
||||
The generated RF output of the above example consists of the following features in sequence:
|
||||
\begin{enumerate}
|
||||
@ -815,12 +752,8 @@ Urukul was operated with a 50$\Omega$ termination to produce the waveform.
|
||||
|
||||
\subsection{Simple Amplitude Ramp (AD9910 Only)}
|
||||
An amplitude ramp of an RF signal can be generated by modifying the \texttt{self.amp} array in the previous example.
|
||||
\begin{minted}{python}
|
||||
def prepare(self):
|
||||
# Reversed Order
|
||||
self.amp = [1.0, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.0]
|
||||
self.asf_ram = [0] * len(self.amp)
|
||||
\end{minted}
|
||||
|
||||
\inputcolorboxminted{firstline=95,lastline=98}{examples/dds.py}
|
||||
|
||||
The generated RF output has an incrementing amplitude scale factor (ASF), increasing by 0.1 at every microsecond.
|
||||
Once the ASF reaches 1.0, it drops back to 0.0 at the next microsecond.
|
||||
@ -871,25 +804,13 @@ Urukul was operated with a 50$\Omega$ termination to produce the waveform.
|
||||
Multiple RAM channels can also be synchronized.
|
||||
Similar to the 10 MHz single-tone RF signals, specify \texttt{phase} when calling \texttt{dds.set()} in \texttt{configure\char`_ram\char`_mode}.
|
||||
For example, set phase to 0 for the channels (\texttt{phase=0.0}).
|
||||
\begin{minted}{python}
|
||||
dds.set(frequency=5*MHz, phase=0.0, ram_destination=RAM_DEST_ASF)
|
||||
\end{minted}
|
||||
|
||||
\inputcolorboxminted{firstline=116,lastline=116}{examples/dds.py}
|
||||
|
||||
Then, replace the \texttt{run()} function with the following.
|
||||
\begin{minted}{python}
|
||||
@kernel
|
||||
def run(self):
|
||||
self.core.reset()
|
||||
self.core.break_realtime()
|
||||
self.cpld.init()
|
||||
|
||||
self.init_dds(self.dds0)
|
||||
self.init_dds(self.dds1)
|
||||
self.dds0.set_phase_mode(PHASE_MODE_TRACKING)
|
||||
self.dds1.set_phase_mode(PHASE_MODE_TRACKING)
|
||||
\inputcolorboxminted{firstline=122,lastline=134}{examples/dds.py}
|
||||
|
||||
self.configure_ram_mode(self.dds0)
|
||||
self.configure_ram_mode(self.dds1)
|
||||
\end{minted}
|
||||
Two phase-coherent RF signal with the same waveform as the previous figure (from either RAM examples) should be generated.
|
||||
|
||||
\subsection{Voltage-controlled DDS Amplitude (SU-Servo Only)}
|
||||
@ -899,41 +820,17 @@ Amplitude of the DDS output can be controlled by the ADC input of the Sampler th
|
||||
In the following example, the amplitude of DDS is proportional to the ADC input from Sampler.
|
||||
First, initialize the RTIO, SU-Servo and its channel.
|
||||
Note that the programmable gain of the Sampler is $10^0=1$, the input range is [-10V, 10V].
|
||||
\begin{minted}{python}
|
||||
@kernel
|
||||
def run(self):
|
||||
self.core.reset()
|
||||
self.core.break_realtime()
|
||||
self.suservo.init()
|
||||
self.suservo.set_pgia_mu(0, 0) # unity gain
|
||||
self.suservo.cplds[0].set_att(0, 15.)
|
||||
self.channel.set_y(profile=0, y=0.) # Clear integrator
|
||||
\end{minted}
|
||||
|
||||
\inputcolorboxminted{firstline=14,lastline=21}{examples/suservo.py}
|
||||
|
||||
Next, setup the PI control as an IIR filter. It has -1 proportional gain $k_p$ and no integrator gain $k_i$.
|
||||
|
||||
\begin{minted}{python}
|
||||
self.channel.set_iir(
|
||||
profile=0,
|
||||
adc=0, # take data from Sampler channel 0
|
||||
kp=-1., # -1 P gain
|
||||
ki=0./s, # no integrator gain
|
||||
g=0., # no integrator gain limit
|
||||
delay=0. # no IIR update delay after enabling
|
||||
)
|
||||
\end{minted}
|
||||
\inputcolorboxminted{firstline=22,lastline=29}{examples/suservo.py}
|
||||
|
||||
Then, configure the DDS frequency to 10 MHz with 3V input offset.
|
||||
When input voltage $\geq$ offset voltage, the DDS output amplitude is 0.
|
||||
|
||||
\begin{minted}{python}
|
||||
self.channel.set_dds(
|
||||
profile=0,
|
||||
offset=-.3, # 3V with above PGIA settings
|
||||
# Note the inverted sign
|
||||
frequency=10*MHz,
|
||||
phase=0.)
|
||||
\end{minted}
|
||||
\inputcolorboxminted{firstline=30,lastline=34}{examples/suservo.py}
|
||||
|
||||
SU-Servo encodes the ADC voltage in a linear scale [-1, 1].
|
||||
Therefore, 3V is converted to 0.3.
|
||||
@ -941,10 +838,7 @@ Note that the ASF of all DDS channels are capped at 1.0, the amplitude clips whe
|
||||
|
||||
Finally, enable the SU-Servo channel with the IIR filter programmed beforehand.
|
||||
|
||||
\begin{minted}{python}
|
||||
self.channel.set(en_out=1, en_iir=1, profile=0)
|
||||
self.suservo.set_config(enable=1)
|
||||
\end{minted}
|
||||
\inputcolorboxminted{firstline=36,lastline=37}{examples/suservo.py}
|
||||
|
||||
A 10 MHz DDS signal is generated from the example above, with amplitude controllable by ADC.
|
||||
The RMS voltage of the DDS channel against the ADC voltage is plotted.
|
||||
|
134
examples/dds.py
Normal file
134
examples/dds.py
Normal file
@ -0,0 +1,134 @@
|
||||
from artiq.experiment import *
|
||||
from artiq.coredevice.ad9910 import *
|
||||
|
||||
|
||||
class Sinusoid(EnvExperiment):
|
||||
def build(self):
|
||||
self.setattr_device("core")
|
||||
self.cpld = self.get_device("urukul0_cpld")
|
||||
self.dds0 = self.get_device("urukul0_ch0")
|
||||
|
||||
@kernel
|
||||
def run(self):
|
||||
self.core.reset()
|
||||
self.cpld.init()
|
||||
self.dds0.init()
|
||||
self.dds0.cfg_sw(True)
|
||||
self.dds0.set_att(6.*dB)
|
||||
self.dds0.set(10*MHz)
|
||||
|
||||
|
||||
class SynchronizedSinusoid(EnvExperiment):
|
||||
def build(self):
|
||||
self.setattr_device("core")
|
||||
self.cpld = self.get_device("urukul0_cpld")
|
||||
self.dds0 = self.get_device("urukul0_ch0")
|
||||
self.dds1 = self.get_device("urukul0_ch1")
|
||||
|
||||
@kernel
|
||||
def run(self):
|
||||
self.core.reset()
|
||||
self.cpld.init()
|
||||
|
||||
self.dds0.init()
|
||||
self.dds0.cfg_sw(True)
|
||||
self.dds0.set_phase_mode(PHASE_MODE_TRACKING)
|
||||
self.dds0.set_att(6.*dB)
|
||||
self.dds1.init()
|
||||
self.dds1.cfg_sw(True)
|
||||
self.dds1.set_phase_mode(PHASE_MODE_TRACKING)
|
||||
self.dds1.set_att(6.*dB)
|
||||
|
||||
self.dds0.set(frequency=10*MHz, phase=0.0)
|
||||
self.dds1.set(frequency=10*MHz, phase=0.25) # 0.25 turns phase offset
|
||||
|
||||
|
||||
class PulseRAM(EnvExperiment):
|
||||
def build(self):
|
||||
self.setattr_device("core")
|
||||
self.cpld = self.get_device("urukul0_cpld")
|
||||
self.dds0 = self.get_device("urukul0_ch0")
|
||||
self.dds1 = self.get_device("urukul0_ch1")
|
||||
|
||||
def prepare(self):
|
||||
self.amp = [0.0, 0.0, 0.0, 0.7, 0.0, 0.7, 0.7] # Reversed Order
|
||||
self.asf_ram = [0] * len(self.amp)
|
||||
|
||||
@kernel
|
||||
def init_dds(self, dds):
|
||||
self.core.break_realtime()
|
||||
dds.init()
|
||||
dds.set_att(6.*dB)
|
||||
dds.cfg_sw(True)
|
||||
|
||||
@kernel
|
||||
def configure_ram_mode(self, dds):
|
||||
self.core.break_realtime()
|
||||
dds.set_cfr1(ram_enable=0)
|
||||
self.cpld.io_update.pulse_mu(8)
|
||||
self.cpld.set_profile(0) # Enable the corresponding RAM profile
|
||||
# Profile 0 is the default
|
||||
dds.set_profile_ram(start=0, end=len(self.asf_ram)-1,
|
||||
step=250, profile=0, mode=RAM_MODE_CONT_RAMPUP)
|
||||
self.cpld.io_update.pulse_mu(8)
|
||||
dds.amplitude_to_ram(self.amp, self.asf_ram)
|
||||
dds.write_ram(self.asf_ram)
|
||||
|
||||
self.core.break_realtime()
|
||||
dds.set(frequency=5*MHz, ram_destination=RAM_DEST_ASF)
|
||||
# Pass osk_enable=1 to set_cfr1() if it is not an amplitude RAM
|
||||
dds.set_cfr1(ram_enable=1, ram_destination=RAM_DEST_ASF)
|
||||
|
||||
self.cpld.io_update.pulse_mu(8)
|
||||
|
||||
@kernel
|
||||
def run(self):
|
||||
self.core.reset()
|
||||
self.core.break_realtime()
|
||||
self.cpld.init()
|
||||
|
||||
self.init_dds(self.dds0)
|
||||
self.configure_ram_mode(self.dds0)
|
||||
|
||||
|
||||
class AmpRAM(PulseRAM):
|
||||
def prepare(self):
|
||||
# Reversed Order
|
||||
self.amp = [1.0, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.0]
|
||||
self.asf_ram = [0] * len(self.amp)
|
||||
|
||||
|
||||
class SynchronizedPulseRAM(PulseRAM):
|
||||
@kernel
|
||||
def configure_ram_mode(self, dds):
|
||||
self.core.break_realtime()
|
||||
dds.set_cfr1(ram_enable=0)
|
||||
self.cpld.io_update.pulse_mu(8)
|
||||
self.cpld.set_profile(0) # Enable the corresponding RAM profile
|
||||
# Profile 0 is the default
|
||||
dds.set_profile_ram(start=0, end=len(self.asf_ram)-1,
|
||||
step=250, profile=0, mode=RAM_MODE_CONT_RAMPUP)
|
||||
self.cpld.io_update.pulse_mu(8)
|
||||
dds.amplitude_to_ram(self.amp, self.asf_ram)
|
||||
dds.write_ram(self.asf_ram)
|
||||
|
||||
self.core.break_realtime()
|
||||
dds.set(frequency=5*MHz, phase=0.0, ram_destination=RAM_DEST_ASF)
|
||||
# Pass osk_enable=1 to set_cfr1() if it is not an amplitude RAM
|
||||
dds.set_cfr1(ram_enable=1, ram_destination=RAM_DEST_ASF)
|
||||
|
||||
self.cpld.io_update.pulse_mu(8)
|
||||
|
||||
@kernel
|
||||
def run(self):
|
||||
self.core.reset()
|
||||
self.core.break_realtime()
|
||||
self.cpld.init()
|
||||
|
||||
self.init_dds(self.dds0)
|
||||
self.init_dds(self.dds1)
|
||||
self.dds0.set_phase_mode(PHASE_MODE_TRACKING)
|
||||
self.dds1.set_phase_mode(PHASE_MODE_TRACKING)
|
||||
|
||||
self.configure_ram_mode(self.dds0)
|
||||
self.configure_ram_mode(self.dds1)
|
37
examples/suservo.py
Normal file
37
examples/suservo.py
Normal file
@ -0,0 +1,37 @@
|
||||
from artiq.experiment import *
|
||||
from scipy import signal
|
||||
import numpy
|
||||
|
||||
|
||||
class SUServoExample(EnvExperiment):
|
||||
def build(self):
|
||||
self.setattr_device("core")
|
||||
self.suservo = self.get_device("suservo0")
|
||||
self.suschannels = [
|
||||
self.get_device("suservo0_ch0")
|
||||
]
|
||||
|
||||
@kernel
|
||||
def run(self):
|
||||
self.core.reset()
|
||||
self.core.break_realtime()
|
||||
self.suservo.init()
|
||||
self.suservo.set_pgia_mu(0, 0) # unity gain
|
||||
self.suservo.cplds[0].set_att(0, 15.)
|
||||
self.suschannels[0].set_y(profile=0, y=0.) # Clear integrator
|
||||
self.suschannels[0].set_iir(
|
||||
profile=0,
|
||||
adc=0, # take data from Sampler channel 0
|
||||
kp=-1., # -1 P gain
|
||||
ki=0./s, # no integrator gain
|
||||
g=0., # no integrator gain limit
|
||||
delay=0. # no IIR update delay after enabling
|
||||
)
|
||||
self.suschannels[0].set_dds(
|
||||
profile=0,
|
||||
offset=-.3, # 3 V with above PGIA settings
|
||||
frequency=10*MHz,
|
||||
phase=0.)
|
||||
# enable RF, IIR updates and set profile
|
||||
self.suschannels[0].set(en_out=1, en_iir=1, profile=0)
|
||||
self.suservo.set_config(enable=1)
|
Loading…
Reference in New Issue
Block a user