2016-11-22 18:57:34 +08:00
|
|
|
from numpy import int32, int64
|
2016-11-29 21:49:07 +08:00
|
|
|
from artiq.language.core import kernel, now_mu, portable, delay
|
2016-11-30 00:23:06 +08:00
|
|
|
from artiq.coredevice.rtio import rtio_output, rtio_output_wide
|
2016-11-20 23:39:53 +08:00
|
|
|
from artiq.language.types import TInt32, TInt64, TFloat, TList
|
2016-07-22 21:56:09 +08:00
|
|
|
|
|
|
|
|
2016-11-20 23:39:53 +08:00
|
|
|
class Spline:
|
|
|
|
kernel_invariants = {"channel", "core", "scale", "width",
|
|
|
|
"time_width", "time_scale"}
|
2016-07-22 21:56:09 +08:00
|
|
|
|
2016-11-20 23:39:53 +08:00
|
|
|
def __init__(self, width, time_width, channel, core_device, scale=1.):
|
|
|
|
self.core = core_device
|
|
|
|
self.channel = channel
|
|
|
|
self.width = width
|
2016-11-29 21:49:07 +08:00
|
|
|
self.scale = (1 << width) * scale
|
2016-11-20 23:39:53 +08:00
|
|
|
self.time_width = time_width
|
2016-11-29 21:49:07 +08:00
|
|
|
self.time_scale = (1 << time_width) * core_device.coarse_ref_period
|
2016-07-22 21:56:09 +08:00
|
|
|
|
2016-11-29 21:49:07 +08:00
|
|
|
@portable(flags={"fast-math"})
|
2016-11-20 23:39:53 +08:00
|
|
|
def to_mu(self, value: TFloat) -> TInt32:
|
2016-11-30 00:33:43 +08:00
|
|
|
return int32(round(value*self.scale))
|
2016-11-20 23:39:53 +08:00
|
|
|
|
2016-11-29 21:49:07 +08:00
|
|
|
@portable(flags={"fast-math"})
|
2016-11-20 23:39:53 +08:00
|
|
|
def from_mu(self, value: TInt32) -> TFloat:
|
|
|
|
return value/self.scale
|
|
|
|
|
2016-11-29 21:49:07 +08:00
|
|
|
@portable(flags={"fast-math"})
|
2016-11-30 00:33:43 +08:00
|
|
|
def to_mu64(self, value: TFloat) -> TInt64:
|
|
|
|
return int64(round(value*self.scale))
|
2016-07-22 21:56:09 +08:00
|
|
|
|
|
|
|
@kernel
|
2016-11-20 23:39:53 +08:00
|
|
|
def set_mu(self, value: TInt32):
|
|
|
|
"""Set spline value (machine units).
|
2016-07-22 21:56:09 +08:00
|
|
|
|
2016-11-20 23:39:53 +08:00
|
|
|
:param value: Spline value in integer machine units.
|
2016-07-22 21:56:09 +08:00
|
|
|
"""
|
2016-11-20 23:39:53 +08:00
|
|
|
rtio_output(now_mu(), self.channel, 0, value)
|
2016-07-22 21:56:09 +08:00
|
|
|
|
2016-11-20 23:39:53 +08:00
|
|
|
@kernel
|
|
|
|
def set(self, value: TFloat):
|
|
|
|
"""Set spline value.
|
2016-07-22 21:56:09 +08:00
|
|
|
|
2016-11-20 23:39:53 +08:00
|
|
|
:param value: Spline value relative to full-scale.
|
2016-07-22 21:56:09 +08:00
|
|
|
"""
|
2016-11-20 23:39:53 +08:00
|
|
|
rtio_output(now_mu(), self.channel, 0, self.to_mu(value))
|
2016-07-22 21:56:09 +08:00
|
|
|
|
|
|
|
@kernel
|
2016-11-20 23:39:53 +08:00
|
|
|
def set64(self, value: TFloat):
|
|
|
|
"""Set spline value.
|
2016-07-22 21:56:09 +08:00
|
|
|
|
2016-11-20 23:39:53 +08:00
|
|
|
:param value: Spline value relative to full-scale.
|
2016-07-22 21:56:09 +08:00
|
|
|
"""
|
2016-11-30 00:33:43 +08:00
|
|
|
v = self.to_mu64(value)
|
|
|
|
l = [int32(v), int32(v >> 32)]
|
|
|
|
rtio_output_wide(now_mu(), self.channel, 0, l)
|
2016-07-22 21:56:09 +08:00
|
|
|
|
2016-11-20 23:39:53 +08:00
|
|
|
@kernel
|
2016-11-30 00:23:06 +08:00
|
|
|
def set_coeff_mu(self, value):
|
2016-11-20 23:39:53 +08:00
|
|
|
"""Set spline raw values.
|
2016-07-22 21:56:09 +08:00
|
|
|
|
2016-11-20 23:39:53 +08:00
|
|
|
:param value: Spline packed raw values.
|
2016-07-22 21:56:09 +08:00
|
|
|
"""
|
2016-11-30 00:23:06 +08:00
|
|
|
rtio_output_wide(now_mu(), self.channel, 0, value)
|
2016-11-20 23:39:53 +08:00
|
|
|
|
2016-11-29 21:49:07 +08:00
|
|
|
@portable(flags={"fast-math"})
|
2016-11-30 00:20:02 +08:00
|
|
|
def pack_coeff_mu(self, coeff, packed):
|
2016-11-29 21:49:07 +08:00
|
|
|
pos = 0
|
2016-11-30 00:20:02 +08:00
|
|
|
for i in range(len(coeff)):
|
2016-11-29 21:49:07 +08:00
|
|
|
wi = self.width + i*self.time_width
|
|
|
|
ci = coeff[i]
|
2016-11-30 00:20:02 +08:00
|
|
|
while wi != 0:
|
2016-11-29 21:49:07 +08:00
|
|
|
j = pos//32
|
|
|
|
used = pos - 32*j
|
|
|
|
avail = 32 - used
|
|
|
|
if avail > wi:
|
|
|
|
avail = wi
|
2016-11-30 00:20:02 +08:00
|
|
|
packed[j] |= int32(ci & ((1 << avail) - 1)) << used
|
2016-11-29 21:49:07 +08:00
|
|
|
ci >>= avail
|
|
|
|
wi -= avail
|
|
|
|
pos += avail
|
|
|
|
|
|
|
|
@portable(flags={"fast-math"})
|
2016-11-30 00:20:02 +08:00
|
|
|
def coeff_to_mu(self, coeff, coeff64):
|
|
|
|
for i in range(len(coeff)):
|
2016-11-29 21:49:07 +08:00
|
|
|
vi = coeff[i] * self.scale
|
|
|
|
for j in range(i):
|
|
|
|
vi *= self.time_scale
|
2016-11-30 00:33:43 +08:00
|
|
|
ci = int64(round(vi))
|
2016-11-29 23:58:26 +08:00
|
|
|
coeff64[i] = ci
|
2016-11-29 21:49:07 +08:00
|
|
|
# artiq.wavesynth.coefficients.discrete_compensate:
|
|
|
|
if i == 2:
|
2016-11-30 03:51:29 +08:00
|
|
|
coeff64[1] += ci >> self.time_width + 1
|
2016-11-29 21:49:07 +08:00
|
|
|
elif i == 3:
|
2016-11-29 23:58:26 +08:00
|
|
|
coeff64[2] += ci >> self.time_width
|
2016-11-30 03:51:29 +08:00
|
|
|
coeff64[1] += ci // 6 >> 2*self.time_width
|
2016-07-22 21:56:09 +08:00
|
|
|
|
|
|
|
@kernel
|
2016-11-30 00:23:06 +08:00
|
|
|
def set_coeff(self, value):
|
2016-11-20 23:39:53 +08:00
|
|
|
"""Set spline coefficients.
|
2016-07-22 21:56:09 +08:00
|
|
|
|
2016-11-20 23:39:53 +08:00
|
|
|
:param value: List of floating point spline knot coefficients,
|
|
|
|
lowest order (constant) coefficient first.
|
2016-07-22 21:56:09 +08:00
|
|
|
"""
|
2016-11-30 00:20:02 +08:00
|
|
|
n = len(value)
|
|
|
|
width = n*self.width + (n - 1)*n//2*self.time_width
|
|
|
|
coeff64 = [int64(0)] * n
|
|
|
|
packed = [int32(0)] * ((width + 31)//32)
|
|
|
|
self.coeff_to_mu(value, coeff64)
|
|
|
|
self.pack_coeff_mu(coeff64, packed)
|
2016-11-30 00:23:06 +08:00
|
|
|
self.set_coeff_mu(packed)
|
2016-07-22 21:56:09 +08:00
|
|
|
|
2016-11-29 21:49:07 +08:00
|
|
|
@kernel(flags={"fast-math"})
|
2016-11-29 23:58:26 +08:00
|
|
|
def smooth(self, start: TFloat, stop: TFloat, duration: TFloat,
|
|
|
|
order: TInt32):
|
2016-11-29 21:49:07 +08:00
|
|
|
"""Initiate an interpolated value change.
|
|
|
|
|
|
|
|
The third order interpolation is constrained to have zero first
|
|
|
|
order derivative at both start and stop.
|
|
|
|
|
|
|
|
For zeroth order (step) interpolation, the step is at duration/2.
|
|
|
|
|
|
|
|
For first order and third order interpolation (linear and cubic)
|
|
|
|
the interpolator needs to be stopped (or fed a new spline knot)
|
|
|
|
explicitly at the stop time.
|
|
|
|
|
|
|
|
This method advances the timeline by `duration`.
|
|
|
|
|
|
|
|
:param start: Initial value of the change.
|
|
|
|
:param stop: Final value of the change.
|
|
|
|
:param duration: Duration of the interpolation.
|
|
|
|
:param order: Order of the interpolation. Only 0, 1,
|
|
|
|
and 3 are valid: step, linear, cubic.
|
|
|
|
"""
|
|
|
|
if order == 0:
|
|
|
|
delay(duration/2)
|
2016-11-30 00:23:06 +08:00
|
|
|
self.set_coeff([stop])
|
2016-11-29 21:49:07 +08:00
|
|
|
delay(duration/2)
|
|
|
|
elif order == 1:
|
2016-11-30 00:23:06 +08:00
|
|
|
self.set_coeff([start, (stop - start)/duration])
|
2016-11-29 21:49:07 +08:00
|
|
|
delay(duration)
|
|
|
|
elif order == 3:
|
|
|
|
v2 = 6*(stop - start)/(duration*duration)
|
2016-11-30 00:23:06 +08:00
|
|
|
self.set_coeff([start, 0., v2, -2*v2/duration])
|
2016-11-29 21:49:07 +08:00
|
|
|
delay(duration)
|
|
|
|
else:
|
|
|
|
raise ValueError("Invalid interpolation order. "
|
|
|
|
"Supported orders are: 0, 1, 3.")
|
|
|
|
|
2016-07-22 21:56:09 +08:00
|
|
|
|
2016-11-20 23:39:53 +08:00
|
|
|
class SAWG:
|
|
|
|
"""Smart arbitrary waveform generator channel.
|
|
|
|
The channel is parametrized as: ::
|
|
|
|
|
|
|
|
oscillators = exp(2j*pi*(frequency0*t + phase0))*(
|
|
|
|
amplitude1*exp(2j*pi*(frequency1*t + phase1)) +
|
|
|
|
amplitude2*exp(2j*pi*(frequency2*t + phase2))
|
|
|
|
|
|
|
|
output = (offset +
|
|
|
|
i_enable*Re(oscillators) +
|
|
|
|
q_enable*Im(buddy_oscillators))
|
|
|
|
|
|
|
|
Where:
|
|
|
|
* offset, amplitude1, amplitude1: in units of full scale
|
|
|
|
* phase0, phase1, phase2: in units of turns
|
|
|
|
* frequency0, frequency1, frequency2: in units of Hz
|
|
|
|
|
|
|
|
:param channel_base: RTIO channel number of the first channel (amplitude).
|
|
|
|
Frequency and Phase are then assumed to be successive channels.
|
|
|
|
"""
|
|
|
|
kernel_invariants = {"channel_base", "core",
|
|
|
|
"amplitude1", "frequency1", "phase1",
|
2016-11-21 20:16:44 +08:00
|
|
|
"amplitude2", "frequency2", "phase2",
|
2016-11-20 23:39:53 +08:00
|
|
|
"frequency0", "phase0", "offset"}
|
|
|
|
|
|
|
|
def __init__(self, dmgr, channel_base, parallelism, core_device="core"):
|
|
|
|
self.core = dmgr.get(core_device)
|
|
|
|
self.channel_base = channel_base
|
|
|
|
width = 16
|
|
|
|
time_width = 16
|
|
|
|
cordic_gain = 1.646760258057163 # Cordic(width=16, guard=None).gain
|
|
|
|
# cfg: channel_base
|
|
|
|
self.offset = Spline(width, time_width, channel_base + 1,
|
2016-11-29 21:49:07 +08:00
|
|
|
self.core, 1/2)
|
2016-11-20 23:39:53 +08:00
|
|
|
self.amplitude1 = Spline(width, time_width, channel_base + 2,
|
2016-11-29 21:49:07 +08:00
|
|
|
self.core, 1/(2*cordic_gain**2))
|
2016-11-20 23:39:53 +08:00
|
|
|
self.frequency1 = Spline(3*width, time_width, channel_base + 3,
|
2016-11-29 21:49:07 +08:00
|
|
|
self.core, 1/self.core.coarse_ref_period)
|
2016-11-20 23:39:53 +08:00
|
|
|
self.phase1 = Spline(width, time_width, channel_base + 4,
|
|
|
|
self.core, 1.)
|
|
|
|
self.amplitude2 = Spline(width, time_width, channel_base + 5,
|
2016-11-29 21:49:07 +08:00
|
|
|
self.core, 1/(2*cordic_gain**2))
|
2016-11-20 23:39:53 +08:00
|
|
|
self.frequency2 = Spline(3*width, time_width, channel_base + 6,
|
2016-11-29 21:49:07 +08:00
|
|
|
self.core, 1/self.core.coarse_ref_period)
|
2016-11-20 23:39:53 +08:00
|
|
|
self.phase2 = Spline(width, time_width, channel_base + 7,
|
|
|
|
self.core, 1.)
|
|
|
|
self.frequency0 = Spline(2*width, time_width, channel_base + 8,
|
|
|
|
self.core,
|
2016-11-29 21:49:07 +08:00
|
|
|
self.core.coarse_ref_period/parallelism)
|
2016-11-20 23:39:53 +08:00
|
|
|
self.phase0 = Spline(width, time_width, channel_base + 9,
|
|
|
|
self.core, 1.)
|