Motivation
- Recent studies of quantum memory (JJL Morton, UCL) reveal that echo suppression technique using frequency chirped pulse can increase phase coherence and offer memory protection.
- However, chirp pulses for this technique presumably require an accurate phase value on the time center of pulse.
-This limits to synthesize directly an I-Q envelop relevance to frequency chirp. Also such type of pulse is a signature example of synthesizing general waveforms.
STFT-pulsegen surely can achieve and reproduce this if proper Fourier coefficients are ready!
- Low noise interpolation features in Fourier transform nature of STFT-pulsegen and we also confirmed synthesizing via Fourier transform in STFT pulsegen has a better smoothness over than user I/O with time domain stream array (I tried to bypass FFT of STFT module, but failed to deal with smooth interpolation).
- We also confirmed that the Fourier transform in STFT module is accurate enough to process in I-Q plane.
Before giving turorials and how-to, I show here my result of the chirp pulse we are making in STFT-pulsegen.

Mathematics
The Fourier coefficients of Linear chirp pulse are shown as
.
This coefficients are the result of mere Fourier transform of a chirp waveform : exp(j (Gamma/T) t2) for a time duration from -T/2 to T/2.
The value F is the chirp amplitude defined by (the maximum chirp frequency: Gamma/2pi) * (time duration : T), so that this is in natural unit. The used F to make this result as in the figure is 400. Increasing F consumes the more number of non-zero Fourier coefficient.
The maximum dimension of FFT coefficients is 1024. Someone can argue whether the dimension is rich enough. But I saw 512 or less number even produces well but in sacrificing smoothness. I doesn't feel any uncomfortable with 1024 dimension, and FFT calculations with this dimension limit make fast outcomes.
The STFT-pulsegen author (Norman Krackow) considered much about overflow in an internal stage in FFT calculations with a 18bit fixed point resolution. However, I felt that this coefficients transformed from a bounded time trace: exp(j (Gamma/T) t2) are very robust and sustainable to overflow.
Artiq Code
from artiq.experiment import *
import numpy
def spec(k, F):
from scipy.special import erf
from math import pi as m_pi
from math import sqrt as msqrt
a = -((-1) ** 0.75) * 0.5 /msqrt(F) * numpy.exp(1.0j *m_pi * (k))
b = (0.5 + 0.5j) * msqrt(0.5 * m_pi / F)
return a* numpy.exp(1.0j * m_pi * k**2 / F ) * (erf(b * (F + 2.0*k)) + erf(b * (F - 2.0*k)))
class Phaser(EnvExperiment):
def build(self):
self.areal, self.aimag = self.get_coeffs_R(400.01)
self.real = [self.areal[i] for i in range(1024)]
self.imag = [self.aimag[i] for i in range(1024)]
self.setattr_device("core")
self.setattr_device("phaser0")
self.setattr_device("ttl12")
def get_coeffs_R (self, Ff):
_k = numpy.zeros(1024, dtype=numpy.int32)
_k[:] = numpy.arange(-512,512)[:]
_k=numpy.roll(_k,512)
_a_m = spec(_k, Ff)
mabs = numpy.sum(numpy.abs(_a_m))
_a_m = _a_m / mabs * (2**19)
return numpy.round(_a_m.real).astype(int), numpy.round(_a_m.imag).astype(int)
@rpc(flags={"async"})
def p(self, *p):
print([hex(_ & 0xffffffff) for _ in p])
def run(self):
self.do()
@kernel
def do(self):
self.core.reset()
self.core.break_realtime()
for i in range(1):
self.inner()
@kernel
def inner(self):
f = self.phaser0
ttl0 = self.ttl12
## Initialization
f.init(debug=True)
delay(.1 * ms)
for ch in range(2):
f.channel[ch].set_att(0 * dB)
f.channel[ch].set_duc_frequency_mu(0)
f.channel[ch].set_duc_phase(.0)
f.channel[ch].set_duc_cfg(select=0, clr=1)
delay(.1 * ms)
for osc in range(5):
ftw = 0.0
asf = 0.0
f.channel[ch].oscillator[osc].set_frequency_mu(0)
delay(.1 * ms)
f.channel[ch].oscillator[osc].set_amplitude_phase(asf, phase=.0, clr=1)
delay(.1 * ms)
f.duc_stb()
delay(.1 * ms)
####################
f.set_stft_enable_flag(1)
f.pulsegen.set_pulsesettings(disable_window=0, gated_output=1)
delay(.1 * ms)
#############CLS###########
for i in range(4): # branches + shaper
f.pulsegen.clear_full_coef(i)
delay(.1 * ms)
## STFT clear
for i in range(3):
f.pulsegen.clear_full_coef(i)
delay(.1 * ms)
f.pulsegen.set_duc_frequency(i, 0 * MHz)
f.pulsegen.set_duc_cfg(i, clr=1)
delay(.1 * ms)
f.duc_stb()
############################
f.pulsegen.set_nr_repeats(1)
delay(.1 * ms)
for i in range(1): # branches
f.pulsegen.clear_full_coef(i)
delay(.1 * ms)
f.pulsegen.set_interpolation_rate(i, 4) # + i*4)
delay(.1 * ms)
f.pulsegen.send_full_coef(i, self.real, self.imag)
delay(.1 * ms)
f.pulsegen.set_shiftmask(i, 0xff)
delay(.1 * ms)
f.pulsegen.start_fft(i)
delay(.1 * ms)
if i <= 2: # if branch
f.pulsegen.set_duc_frequency(i, 0.00 * MHz) # To see I-Q evenvlope
#f.pulsegen.set_duc_frequency(i, (200) * MHz) # for AOM
delay(.1 * ms)
f.pulsegen.set_duc_cfg(i, clr=0, clr_once=1)
delay(.1 * ms)
f.duc_stb()
delay(.1 * ms)
real = [0 for i in range(1024)]
imag = [0 for _ in range(1024)]
real[-1] = -16000
real[0] = 32000
real[1] = -16000
f.pulsegen.clear_full_coef(3)
delay(.1 * ms)
f.pulsegen.set_interpolation_rate(3, 4)
delay(.1 * ms)
f.pulsegen.send_full_coef(3, real, imag)
delay(.1 * ms)
f.pulsegen.set_shiftmask(3, 0xff)
delay(.1 * ms)
f.pulsegen.start_fft(3)
delay(.1 * ms)
f.pulsegen.get_frame_timestamp()
t = now_mu()+(int64(f.pulsegen.tframe) * 20000)
loopdelay = 200000 * f.pulsegen.tframe
for i in range(10000):
at_mu(t)
t = t + loopdelay
ttl0.pulse(1 * us)
f.pulsegen.trigger()
delay(100*us)
f.pulsegen.trigger()
print("done")
self.core.break_realtime()
self.core.wait_until_mu(now_mu())
The coefficients are calculated by Scipy special function; which returns numpy.complex and is needed to convert to integers. Take a close look on how the code normalize them in a fixed point resolution.
The original pulsegen firmware lets STFT duc branches be freely released while for pulse shape to be fixed at trigger signal. We slightly modified the STFT Migen code to release STFT duc branch synchronously (this does not mean phase initialization of duc - DUC (carrier) is continuously running) with pulse shape (this was just a simple copy & paste work). I will show in a later chance how I did.
Thank you very much.