add some docs

This commit is contained in:
Robert Jördens 2022-09-04 19:21:49 +00:00
parent fa3678f8a3
commit 876f26ee30
1 changed files with 105 additions and 1 deletions

View File

@ -1279,6 +1279,110 @@ class PhaserOscillator:
class Miqro:
"""
Miqro pulse generator.
Notes
-----
* The `_mu` suffix in method names refers to parameters and data in "machine units",
i.e. integers. Conversion methods and wrappers to convert from SI units (Hz frequency,
full scale amplitude, turns phase, seconds time) are provided.
* The annotation that some operation is "expensive" does not mean it is impossible, just
that it may take a significant amount of time and resources to execute such that
it may be impractical when used often or during fast pulse sequences.
They are intended for use in calibration and initialization.
Functionality
-------------
A Miqro instance represents one RF output.
The output is generated by with the following data flow:
### Oscillators
* There are n_osc = 16 oscillators with oscillator IDs 0..n_osc-1.
* Each oscillator outputs one tone at any given time
I/Q (quadrature, a.k.a. complex) 2x16 bit signed data
at tau = 4 ns sample intervals, 250 MS/s, Nyquist 125 MHz, bandwidth 200 MHz
(from f = -100..+100 MHz, taking into account the interpolation anti-aliasing
filters in subsequent interpolators),
32 bit frequency (f) resolution (~ 1/16 Hz),
16 bit unsigned amplitude (a) resolution
16 bit phase (p) resolution
* The output phase p' of each oscillator at time t (boot/reset/initialization of the
device at t=0) is then p' = f*t + p (mod 1 turn) where f and p are the
(currently active) profile frequency and phase.
The terms "phase coherent" and "phase tracking" are defined to refer to this
choice of oscillator output phase p'.
Note that the phase p is not accumulated (on top of previous
phases, previous profiles, or oscillator history). It is "absolute" in the
sense that frequency f and phase p fully determine oscillator
output phase p' at time t. This is unlike typical DDS behavior.
* Frequency, phase and amplitude of each oscillator are configurable by selecting one of
n_profile = 32 profiles 0..n_profile-1. This selection is fast and can be done for
each pulse.
* Note: one profile per oscillator (usually profile index 0) should be reserved
for the NOP (no operation, identity) profile, usually with zero
amplitude.
* Data for each profile for each oscillator can be configured
individually. Storing profile data should be considered "expensive".
### Summation
* The oscillator outputs are added together (wrapping addition).
* The user must ensure that the sum of oscillators outputs does
not exceed the (16 bit signed) data range. In general that means that the sum of the
amplitudes must not exceed the range.
### Shaper
* The summed output stream is then multiplied with a the complex-valued output of a
triggerable shaper.
* Triggering the shaper corresponds to passing a pulse from all
oscillators to the RF output.
* Any previously staged profiles and phase offsets become active simultaneously
(on the same output sample) when triggering the shaper.
* The shaper reads (replays) window samples from a memory of size n_window = 1 << 10 starting
and stopping at memory locations specified.
* Each window memory segment starts with a header determining segment
length and interpolation parameters.
* The window samples are interpolated by a factor (rate change) r where log2(r) = 0..n_cic=12
selectable when triggering.
* The interpolation order is constant, linear, quadratic, or cubic. This
corresponds to interpolation modes from rectangular window (1st order CIC)
or zero order hold) and to Parzen window (4th order CIC, cubic spline),
selectable when triggering.
* This results in support for pulse lengths of between tau and a bit more than
(1 << 12 + 10) tau ~ 17 ms.
* Windows can be configured to be head-less and/or tail-less, meaning, they
do not feed zero-amplitude samples into the shaper before and after
each window. This is used to implement pulses with arbitrary length or
CW output.
* The window memory can be segmented by choosing different start indices
to support different windows selectable when triggering.
### DAC
* This section of the data flow is analogous to the `base` Phaser mode.
* The DAC receives the 250 MS/s I/Q data stream and interpolates it to 1 GS/s I/Q
(with a bandwidth 200 MHz).
* It then applies a (expensive to change) frequency offset of
f1 = -400 MHz..400 MHz.
* Then the DAC converts the data stream to 2 analog outputs I and Q.
* The signals go through two anti-aliasing filters with 340 MHz 3dB bandwidth.
### IQ Mixer and PLL (Upconverter variant)
* The analog I and Q signals after the filter are upconverted in a single-sideband IQ
mixer with a f2 = 0.3 GHz..4.8 GHz LO (the "carrier").
* The output goes through a digitally switchable attenuator (0..31.5 dB attenuation) and
is available at an SMA output with a typical max signal level of 0 to -10 dBm (TBC).
### Overall properties
* The resulting phase of that signal is
(f + f1 + f2)*t + p + s(t - t0) + p1 + p2 (mod 1 turn)
where p1 and p2 are constant but arbitrary and undetermined phase offsets of the
two (common) upconversion stages, s(t - t0) is the phase of the interpolated
shaper output, and t0 is the trigger time (fiducial of the shaper).
Unsurprisingly the frequency is the derivative of the phase.
* The minimum time to change profiles and phase offsets is ~128 ns (estimate, TBC).
This is the minimum practical pulse interval.
"""
def __init__(self, channel):
self.channel = channel
self.base_addr = (self.channel.phaser.channel_base + 1 +
@ -1314,7 +1418,7 @@ class Miqro:
(asf & 0xffff) | (pow << 16))
@kernel
def set_profile(oscillator, profile, frequency, amplitude, phase=0.):
def set_profile(self, oscillator, profile, frequency, amplitude, phase=0.):
# frequency is interpreted in the Nyquist sense, i.e. aliased
ftw = int32(round(frequency*((1 << 30)/(62.5*MHz))))
asf = int32(round(amplitude*0xffff))