The tune-up chain¶
Before a measurement can run unattended, the spectrometer has to be
brought to a working point: enough microwave power, the magnet on the resonance
line, an integration window over the echo, the receiver phase zeroed, and the
pulse amplitudes calibrated. epr_auto does this as an ordered chain of tuning
steps, each one measuring a quantity, judging it, and storing the result for
the steps that follow. This page walks through that chain in the order
the shipped protocols/tune_up.yaml runs it and explains the physics behind
each rule. For the parameter tables see the step reference; for how
retries and on_fail wrap each step see Writing protocols.
The canonical order is:
Each stage depends on the ones before it. Power is set first because every
later measurement needs pulses that rotate the spins; the field search needs
those pulses to produce an echo; the echo window and phase are measured on that
echo; and the fine amplitude calibration is meaningful only once the window and
phase are fixed. A temperature prologue (temp.set / temp.wait) is optional
and, when present, runs before everything else so the whole tune-up happens at
the measurement temperature.
Preliminary tuning¶
Use protocols/preliminary_tuning.yaml to establish a working point before fine tuning. Set the sample name, synthesizer scan bounds, field center and field span for your experiment. All transmit pulses in this workflow use the AWG SINE channel. The first two steps use built-in sequences and do not accept a preset parameter; echo steps use .phase_awg files.
RV (rotary-vane attenuator) controls the microwave excitation power at the sample. VA (video attenuation) controls the level of the received electrical signal sent to the ADC. It is set using two bridge controls: VA1 (Video Attenuation 1, video1_db: 0–30 dB in 2 dB steps) and VA2 (Video Attenuation 2, video2_db: 0–31.5 dB in 0.5 dB steps). Increasing either video attenuation reduces the measured signal amplitude; an additional 6 dB approximately halves it.
ringing check -> [resonator scan] -> echo field search -> repetition-rate scan -> amplitude/field optimization -> video check -> fine-tuning handoff
Ringing check¶
tune.ringing_check sets a nonresonant field (field: 100 G by default), homes the rotary vane (RV) to 60 dB, then checks 60, 40, 20, 10, 5 and 0 dB. Choose a field away from the sample resonance so the ladder measures receiver ringing without a sample echo. Each move finishes before acquisition; the measured signal must pass before the next attenuation is commanded. The built-in sequence uses +x,+x on both DETECTION and its single SINE pulse, so phase cycling cannot cancel the ringing. Acquisition defaults are listed in the step reference.
pulse_length sets the ladder’s SINE pulse length and defaults to 102.4 ns. Later steps must use the same IF and stay within the tested DAC amplitudes. The ladder does not limit their pulse lengths.
For a same-day rerun on the same setup, done: true declares that the ladder has already passed at the same IF. The hardware-free preflight still runs and the limits are recorded, but this step does not touch the RV, field or receiver and takes no trace. The default is done: false, which runs the ladder.
The limit is 100 mV on the maximum unsmoothed magnitude sqrt(I² + Q²) after protection. Missing or invalid data also fail the check. A failure stops the protocol and attempts a settled return to 60 dB; retries, on_fail: skip and a continuing series cannot override this safety stop. Operator cancellation during the ringing ladder also returns RV to 60 dB. Stopping at a checkpoint or in another step leaves RV unchanged.
The check starts after receiver protection ends, using timing derived from the pulse sequence and the configured acquisition alignment. It includes the initial ringing amplitude. Rotary-vane movements are mechanical: the runner waits for calibrated movement and settling before acquiring data.
Optional resonator scan¶
tune.resonator runs the built-in AWG SINE resonator procedure at RV = 10 dB. It measures reflected amplitude with the diode and oscilloscope. Frequency selection uses a common early-ringing time window, subtracts the pre-pulse baseline, and handles either diode polarity. Weak, competing, boundary or clipped peaks and centers that move excessively between nearby windows are rejected. The typical precision_mhz is 5. Set window: 4 ns for resonator selection; this is the recommended width from hardware runs, while the parameter default remains 2 ns. Stability is checked with comparison windows shifted by 1 ns and 2 ns.
When region is omitted, the ringing peak is searched only after the nominal pulse end. This avoids selecting the reflected-pulse plateau, which can exceed the trailing-edge ringing at off-resonance frequencies. An explicit region is searched in full.
Both built-in steps have if_mhz: 50 by default. Their IF values must match each other and the later echo preset's DETECTION IF. The scan axis is the bridge synthesizer frequency; the observation frequency is synthesizer minus IF. The selected synthesizer value is applied directly, without adding IF again. Omit this step to retain the existing synthesizer frequency.
Echo search and optimization¶
With the default adjust_video: true, tune.find_echo first homes RV and opens it through 60, 40, 20, 15, 10, 5 and 0 dB, ending at attenuation_db. A target between these values is the final point. The initial pulses run at center, normally near g = 2. The phasing tool's live preview reads the current 1D time trace from the ADC buffers during RV movement, so VA can be increased while the vane is opening. The next RV move starts after settling and a valid trace below 200 mV.
Above 200 mV, the runner adds video attenuation and remeasures. It uses the maximum unsmoothed I/Q magnitude after receiver protection. The increase is 20*log10(measured_mV/200) dB, rounded upward to available settings: VA1 first (2 dB steps, up to 30 dB), then VA2 (0.5 dB steps, up to 31.5 dB). Invalid readout or a signal still too strong at maximum attenuation stops the protocol and attempts RV home.
Set adjust_video: false in tune.find_echo to keep the current video attenuation and move directly to the requested RV. tune.maximize_echo and tune.video_attenuation inherit this choice unless explicitly overridden. The final video step skips both measurement and bridge access when disabled.
tune.find_echo scans the requested field range at the chosen attenuation_db (default 10 dB, allowed 0–60 dB), scores the full recorded magnitude, and confirms a resolved echo and its integration window. pulse_length sets a common length for both microwave pulses; when omitted, it uses the shortest active microwave pulse in the preset. No echo means stop and return to 60 dB. frequency_shift_mhz is a signed integer offset from the resonator-selected frequency, default 0. For a two-frequency experiment, -50 puts the echo 50 MHz below the resonator center while keeping the AWG IF unchanged. Without a resonator scan, the reference is the bridge frequency at the first echo search; repeated searches do not accumulate the offset.
tune.find_echo accepts a relative frequency_shift_mhz, not an absolute frequency. To choose an absolute synthesizer frequency instead of running the resonator scan, place a bridge.set step with frequency_mhz before the echo search. Leave frequency_shift_mhz: 0 to use that frequency unchanged.
tune.maximize_echo holds RV attenuation and pulse length fixed at the values chosen for tune.find_echo, unless you explicitly override them. It scans the π/2 amplitude a with the π amplitude at 2a: first across amplitude_range (default 5–50 %), then on a finer grid around the best point. Both pulses have the same pulse_length, so their rotation angles differ through amplitude. Pulse roles come from pulse_map: {P2: pi2, P3: pi} or are inferred from the preset. The selected amplitudes are followed by a narrower field sweep and an echo confirmation.
Each coarse or fine amplitude scan keeps the FPGA initialized across its points. The fine scan reuses values already measured in the coarse scan at the same video attenuation. Each stage saves the integrated curve and full I/Q trace matrices; the amplitude-trial records identify the matrix files and the zero-based row for each measured pair. Echo selection still uses the integrated magnitude within the echo window.
Field and amplitude scans check received traces once data for all receiver phases are available. Above 200 mV, the scan stops and saves its partial data, VA is increased, and the field and pulse settings of the reported point are remeasured. The field search then restarts; amplitude optimization repeats its coarse, fine and field comparisons, discarding scores from the previous VA setting. The optimized final pulse pair is checked again.
rep_rate optionally sets the repetition rate in Hz for tune.find_echo, from 0.1 to 10000 Hz. Without it, the preset rate is used. tune.maximize_echo inherits the selected rate and accepts an override. Both steps accept rep_rate: auto to use an earlier accepted tune.rep_rate result in the same session. A missing or temperature-invalidated result, or a recommendation outside the preliminary range, fails the step. All four handoff presets carry the resulting rate; the ringing check remains at 500 Hz.
The preliminary example finds an echo first, runs tune.rep_rate on its selected pulse settings, then runs tune.maximize_echo with rep_rate: auto. Keep the same preset for those steps so the scan uses the selected preliminary pulse lengths and amplitudes. It also inherits the selected field and echo window. The example uses mode: quantitative; sensitivity mode permits partial saturation and should not supply a quantitative relaxation experiment without a new rate check.
RV attenuation and pulse length are operator choices; this step searches neither. A maximum at the upper amplitude bound stops with reduce attenuation; a maximum at the lower bound stops with increase attenuation. Change attenuation_db before rerunning. Ringing is checked in the initial ladder unless an earlier pass is declared with done: true. Later pulses must retain the tested IF and stay within the tested DAC amplitudes; their lengths are not limited by the ladder pulse.
Final video attenuation¶
tune.video_attenuation checks the final target preset after pulse tuning. It preserves pulse lengths and field and zeroes sweep increments for the check. Its single limit_mv defaults to 200 mV and can be lowered. After correcting any excess, it reduces attenuation one step at a time, VA2 first and then VA1, only when the predicted signal remains within the limit. Each change is remeasured. A corrective increase ends the opening attempt. The result records the final VA1/VA2 settings and measured peak.
- tune.video_attenuation:
preset: tuned/echo_cal.phase_awg
# adjust_video: false # skip this check and leave VA unchanged
Fine-tuning handoff and bridge control¶
tune.save_presets publishes fine_tuning.yaml and four preset copies to publish_dir, which defaults to tuned/ beside the preliminary protocol. It also saves an archive in the preliminary run directory under handoff_NNN/; input presets are preserved.
| Preset | Purpose |
|---|---|
echo.phase_awg |
Preliminary pulse pair for the first echo-window and receiver-phase measurements. |
calibration.phase_awg |
Swept Rabi pulse at calibration_length, detected with the preliminary pulse pair. |
field.phase_awg |
Echo-detected field sweep with both microwave pulses at calibration_length. |
echo_cal.phase_awg |
Echo preset with both microwave pulses at calibration_length, for the second tuning pass and later experiments. |
calibration_length defaults to the preliminary pulse length and is snapped to the AWG grid. It sets the Rabi pulse length in calibration.phase_awg and both echo-pulse lengths in field.phase_awg and echo_cal.phase_awg, so it also sets the pulse length used by a later experiment based on those presets. The initial amplitudes in the field and calibrated-echo presets are placeholders until fine calibration replaces them.
To change the exported pulse length, set calibration_length and rerun the preliminary protocol. Editing the Rabi pulse length in tuned/calibration.phase_awg alone leaves the exported field and echo-pulse lengths unchanged. Fine calibration then transfers the measured amplitudes to those lengths using the ratio of the calibration pulse length to the target pulse length.
The generated protocol restores RV, the selected synthesizer frequency and known VA settings with bridge.set, measures the echo window and receiver phase on echo.phase_awg, and runs amplitude calibration. It then calls tune.apply_calibration for both field.phase_awg and echo_cal.phase_awg before the EDFS. The field sweep covers the original tune.find_echo span, recentered on the tuned field, with 200 points by default; field_span and field_points in tune.save_presets override these settings. It does not use the narrower maximization span.
After the EDFS, the protocol repeats the echo-window and phase measurements on echo_cal.phase_awg, then repeats amplitude calibration. A final tune.apply_calibration writes the resulting pulse amplitudes, zero-order phase, echo window and field into echo_cal.phase_awg. The handoff then runs tune.video_attenuation on this final sequence, inheriting the preliminary choice to enable or skip VA adjustment. A later experiment can use this file with window: preset and apply_cal: none, without calibration results from the earlier session. The preset does not store RV, synthesizer frequency or VA; retain the final settings or restore them with bridge.set. Its video1_db and video2_db values must be on the 2 dB and 0.5 dB hardware grids respectively.
Open the MW bridge control window before a live run and allow its vane homing to finish. It may stay open: manual bridge commands are blocked while the runner owns the bridge, and the window resynchronizes when control is released. The ringing step explicitly homes RV unless done: true is set. A later bridge.set waits for any recorded move to finish and uses the recorded position; it homes again when a stale runner lock indicates an interrupted run or when no vane position has been recorded.
epr-auto validate protocols/preliminary_tuning.yaml
epr-auto run protocols/preliminary_tuning.yaml --test
The dry-run completes all six stages with canned acquisitions, automatically continues checkpoints, and creates no acquisition or handoff files. A live supervised run uses launcher dialogs or a real terminal for checkpoints, with the Atomize GUI open for plotting. GUI dry runs keep the checkpoint dialogs. Full parameter details are in the step reference.
Coarse power stage — tune.power_for_length¶
The microwave power is set mechanically, by a RV in the bridge that attenuates the drive by a settable number of decibels. The step's job is to find the vane position at which a π pulse lands at the requested length while the AWG amplitude is held at a fixed, comfortable value (95 % of full scale in the example), leaving headroom for the fine calibration to work in.
It measures the current π length with a length nutation at the held amplitude,
then moves the vane and repeats. The move size comes straight from the physics:
the B₁ field the spins see scales as 10^(-dB/20) with vane attenuation, and a
π rotation needs a fixed B₁·τ product, so to bring a measured π length to the
target the attenuation is stepped by
which would hit the target in one move if B₁ were exactly this ideal function of attenuation; a few iterations (default four) absorb the residual nonlinearity of the real vane.
Two mechanical details matter. The vane is always driven to its target from above — if the new setting is a larger attenuation, the step first overshoots to a higher attenuation and comes back down — so that every approach compresses the same gear backlash the same way and the position is repeatable. And after commanding a move the step waits for the calibrated mechanical travel and settling before the next acquisition.
Because a vane move changes B₁ for everything, it invalidates the receiver
phase and the fine amplitude calibration — see
Calibration flow and invalidation below.
This is why the coarse stage runs once, near the top of a protocol, and why the
example marks it checkpoint: true: a vane move is the kind of action worth
confirming before it proceeds.
Each iteration is hard-gated on its own nutation's echo_snr: if the length
nutation is in the noise the step aborts rather than commanding a vane move from
an argmin of noise, and the final iteration's calibration judges ride along into
the step's judge list and the manifest. If the step runs out of iterations
without hitting the target it does not move the vane past the last
measurement — the reported attenuation and π length always describe the state the
vane is actually left in.
Because a cold start has no echo to nutate on until the magnet is on the line,
epr-auto validate warns when a tune.power_for_length (like tune.auto_phase
and tune.pi_calibration) appears before any field.* step; the warning is
informational — tuning at a manually pre-set field is legitimate.
Signal search — field.edfs with range: auto¶
With power set, the run finds the resonance line by an echo-detected field
sweep. range: auto turns the step into a blind signal search: it predicts the
centre field from the resonance condition at the true observation frequency
— the synthesizer/LO readout minus the preset's DETECTION-pulse intermediate
frequency (this bridge upconverts lower-sideband, so the spins see
ν = ν_LO − ν_IF):
then sweeps ± a span (250 G by default) around it; the log shows the arithmetic
(LO 9750 MHz - IF 50 MHz = ...). The magnet on this endstation is not
absolutely calibrated, so a known setup shift is passed as offset and folded
into the predicted centre — offset is the magnet-calibration shift only,
the IF contribution is already accounted for. The step reports back the
measured line-minus-centre distance as shift_g, which is exactly the value to
feed in as offset next time. pick: max takes the working field at the
magnitude maximum of the sweep and sets the magnet there.
The search escalates once. If the echo-SNR judge fails — no line above the noise floor in the first span — the span doubles and the sweep re-runs a single time. If it fails again the step stops without moving the magnet to a noise maximum and surfaces a diagnosis naming what to check:
- "weak line found" — something did clear the weak-line threshold near a named field, so the advice is to add scans or narrow the range around it;
- "flat everywhere" — nothing anywhere in the range, so the advice points at the resonator tuning, temperature, g-factor guess and sample position.
An explicit range: [start, end] skips the prediction and sweeps the given
window; pick: value names a field independent of the echo, in which case a
weak sweep neither escalates nor blocks — the magnet is still parked at the
requested field.
Echo window — tune.echo_window¶
The integration window is the time gate over which the echo is summed into a
single complex point. tune.echo_window sets it from an averaged echo trace: it
finds the centre at the maximum of the smoothed magnitude |V(t)|, measures the
echo's full width at half maximum, and opens a window of FWHM × factor (factor
2 by default) around the centre, rounding the edges outward onto the ADC grid and
clamping them to the acquisition window. The magnitude is smoothed with a fixed
physical box (~3 ns), not a fraction of the trace length, so measuring the FWHM
does not inflate the width of a short, sharp echo.
search_from excludes early trace artifacts from the echo search; min_width rejects peaks too narrow to be an echo. Set these for the expected echo position and width. A rejected narrow peak is masked out and the search continues.
The window is stored relative to the DETECTION pulse start, not as an absolute time. That is the same frame the preset's own "Window left/right" live in, so the measured window transfers to any later preset whose echo sits at a different absolute delay — a T₂ decay and a T₁ recovery built from the same detection block reuse one window without re-measuring it.
The judge checks that the echo is fully resolved inside the trace: an FWHM edge sitting on the trace boundary means the acquisition window is cutting the echo, and the result is flagged.
This step must run before tune.auto_phase, because auto-phase integrates
the signal over exactly this window to measure the residual phase. A phase
measured over a wrong or unset window is meaningless, which is why the chain
fixes the window first.
Receiver phase — tune.auto_phase¶
The digitizer demodulates the echo against a reference whose zero-order phase
offset must be tuned so the signal lands on the real axis. tune.auto_phase
acquires a short echo run, integrates it over the echo window, and measures the
residual signal phase by the principal-axis method (auto_phase_zero in the FFT
module — a sign-blind Σ S² estimate that phases a bipolar trace correctly). The
demodulator rotates by exp(-i·zero_order), so the correction is a direction:
The step acquires 16 sweep points by default. The phase_coherence judge that
gates it has an N-aware pass floor, max(0.7, 3/√n), so a trace of fewer than
about ten points can never clear the noise floor — 16 gives the gate something
to work with while staying a quick acquisition.
The updated zero-order is stored and flows into every later acquisition's build. Auto-phase also records the temperature it was measured at, which is what lets a later temperature move decide whether the phase is still valid.
Fine calibration — tune.pi_calibration¶
The coarse stage put a π near the target length at a fixed amplitude; the fine
stage pins the pulse amplitudes precisely. The nutation presets are
inversion-detection sequences — the pulse being swept precedes a fixed selective
echo pair, so the echo amplitude goes as cos(θ) of the swept pulse, with π at
the first minimum and π/2 at the zero crossing.
The rotation angle is fitted as a quadratic in the swept quantity,
The quadratic term absorbs amplifier compression — the rotation slows slightly
as the amplitude climbs — so amp(π) and amp(π/2) come out independently
from the fit rather than forcing the ideal amp(π) = 2·amp(π/2). (Because
compression only ever slows the rotation, the fitted chirp is bounded to a
physical range; without that bound the fit has a spurious accelerating-chirp
basin that wrecks the π/2 root.) The π extremum is de-biased against the fit's
envelope/decay degeneracy with a model-free parabola vertex at the observed
minimum, and π/2 is then re-fitted with the angle map anchored at that measured
π, which makes the π/2 root as reliable as π itself.
mode: amplitude sweeps the AWG amplitude in percent at fixed length — the usual
fine calibration; mode: length runs a linear-time length nutation and is what
the coarse stage uses internally. After an amplitude calibration the soft
detection pair (the selective refocusing/detection pulses, recognised as the
active pulses at ≥ 2.5× the swept pulse's length) is re-scaled onto the
calibrated value by the flip-angle relation — the on-resonance rotation goes as
amp · L · ⟨envelope⟩, so the amplitude scales by the calibrated L·⟨envelope⟩
over the target slot's, the AWG amplitude being nearly linear. The envelope area
factor ⟨envelope⟩ is 1 for a SINE pulse and the mean of the peak-referenced
envelope for a shaped one (0.543 for the shipped GAUSS); a transfer across
envelope shapes for which no area factor exists — the adiabatic WURST/SECH — is
refused rather than mis-scaled. refine: true re-runs the nutation once with the
re-scaled pair patched in, as insurance on a new sample.
If the fitted π amplitude runs into the rails — π unreachable within the sweep,
or above the amplitude ceiling — the step fails on the amplitude_rails judge;
an out-of-sweep π/2 rails the step the same way, so a π/2 outside the swept range
is never shipped. A mode: length calibration is rail-checked identically by a
length_rails judge. The retries: 1 in the example grants one plain
re-attempt; on a rail
failure specifically, the runner additionally re-runs the coarse power stage
declared earlier in the protocol (and the auto-phase steps tuned after it, which
the vane move invalidates) once and retries the calibration, so a sample that
needs more power can recover without operator intervention. This fallback exists
only when the protocol itself declared a tune.power_for_length step earlier;
with no coarse stage to fall back to, the rail failure is reported as-is.
Repetition rate — tune.rep_rate¶
tune.rep_rate keeps the FPGA open at a fixed field and fixed τ while scanning a logarithmic grid of rates, slowest first. Each ordinary, nonempty curve returned by digitizer_get_curve(live_mode=1) provides a complex echo integral in the selected integration window. Each curve holds one complete phase cycle, which may span several ADC buffers when the cycle is longer than one buffer. The result is consumed as returned, including when its underlying buffers contain old or mixed-rate packets; no packet tags or epoch filtering are used. The card continues running between rates; there is no separate warmup train or one-second delay.
By default, three consecutive curves qualify when (max |sig| − min |sig|) / mean |sig| ≤ 0.05. Their mean complex signal supplies the amplitude at that rate, and the acquisition advances to the next rate. points sets the number of consecutive curves (default and minimum 3); it no longer sweeps τ. scans requests disjoint stable groups (default 1), with the combined groups also required to agree within 5%.
The scanned tune.rep_rate grid has a 10 Hz lower bound and a 10 Hz default rate_min. This floor applies only to the tuning grid; ordinary acquisition rates and fitted recommendations may still use the hardware's 0.1 Hz lower bound, subject to sequence timing. During tuning the Worker pins the ADC stream buffer to 512 KB before opening the card, regardless of the ADC window or rate, then restores the previous value after the card closes, including on Stop or failure. This tuning-only allocation does not add live-rate-specific changes to Insys_FPGA. max_wait: 120 s limits the wait at each rate, including buffer arrival; low rates or greater averaging may need a longer limit. A timeout fails the step without a new recommendation. The numbered *_rep_rate_live.csv file retains observations and their validity flags, including partial history on failure or Stop. The completed amplitude curve is saved separately as *_rep_rate_curve.csv.
The step applies automatic fine pulse calibration when available and otherwise preserves the selected preliminary pulse values; field, echo window and phase are inherited. Nd:YAG has a fixed 9.9 Hz repetition rate, so variable-rate tuning is rejected. It fits the saturation of the sequence repeated every period T = 1/rate:
The fitted T1_eff is the recovery time the sequence actually sees. From it the
step recommends a working rate in one of two modes:
quantitative(default): period =factor × T1_eff, withfactor5 giving under 1 % residual saturation — the right choice when the saturation itself would bias a relaxation measurement;sensitivity: period =1.26 × T1_eff, theA(T)/√Toptimum that maximises echo per unit √time. It accepts partial saturation and is fine for tuning and EDFS but not for quantitative relaxation runs.
A recommendation never exceeds the fastest tested rate, including after rounding. If the fitted recovery period requires less than the supported minimum of 0.1 Hz, the step fails instead of shortening that period. A non-flat curve must pass the saturation fit quality check (adjusted R² at least 0.9); a poor fit fails the hard rep_rate_fit check and stores no new recommendation.
Two edge cases are handled explicitly. If the amplitude spread is below 5% across the grid, the step reports the fastest tested rate with no resolved T1_eff. This tolerance does not establish the quantitative mode’s nominal under-1% saturation bound. If the fitted residual saturation at the slowest rate is 15% or more, the grid is insufficient and no recommendation is stored; when rate_min is above 10 Hz, extend it toward the 10 Hz floor, while a grid already starting at 10 Hz reports that the recovery is not covered at the tuning limit. Accepted recommendations are available through rep_rate: auto on tune.find_echo, tune.maximize_echo, exp.t1 and exp.t2.
The recovery model still assumes the sequence sufficiently resets longitudinal magnetization. A 5% stability result is a live-buffer steadiness check, not proof of quantitative 1% saturation accuracy. A flat curve selects the fastest tested rate under the same 5% tolerance; existing fit and coverage gates still apply. Check stability against a hand-measured recovery curve and with increased averaging before relying on the recommendation for quantitative work, especially after pulse calibration changes. Offline checks cover convergence, rate transitions, partial history and cleanup; validation on the spectrometer remains pending.
The step refuses to recommend a rate off something that is not an echo: all acquisitions concatenated must share one phase (the same resultant-length gate auto-phase uses), and on a strongly saturating curve the amplitude deviations must additionally lie on that phase axis — a constant instrumental phasor (LO leakage surviving the phase cycle) can fake the shared phase but not the structure, and is rejected with a note naming it. The live step requires exactly two active microwave pulses after DETECTION and clears their sweep increments, so Log Time or Amplitude labels are not themselves a hard rejection; a wrong pulse count fails with tune.rep_rate needs a two-pulse echo preset at fixed tau.
The --test dry-run pre-flights every rate of the grid, so a rate_max
whose period is shorter than the sequence fits in fails the dry-run rather than
aborting a live run.
Calibration flow and invalidation¶
Every tuning step writes its result into the session state, and every later build reads it back (Presets lists which preset fields each override replaces). This is how a value measured once propagates without being repeated:
| Stored by | Flows into |
|---|---|
tune.echo_window |
the integration window of every later acquisition |
tune.auto_phase |
the demodulator zero-order of every later acquisition |
field.edfs / field.set |
the working field of every later acquisition |
tune.rep_rate |
tune.find_echo, tune.maximize_echo and exp.* steps that set rep_rate: auto |
tune.pi_calibration |
exp.* steps via apply_cal |
The pulse calibration reaches the experiment presets through the apply_cal
parameter on the exp.* steps. By default the runner infers which pulses to
patch from the preset itself: among the hard microwave pulses it reads the two
amplitude levels the preset already carries and maps the higher to π and the
lower to π/2 (or, for an equal-amplitude preset, the 2×-longer pulse to π). When
that is ambiguous you give an explicit map, e.g. apply_cal: {P2: pi2, P3: pi};
apply_cal: none opts a step out of patching entirely and runs the
preset-stored amplitudes. Patched amplitudes are transferred to pulses of a
different length — or a different envelope shape — by the same flip-angle
relation the detection pair uses (the amplitude scales as the calibrated
L·⟨envelope⟩ over the target slot's), so a calibration measured at one pulse
length applies correctly at another; a transfer across envelope shapes with no
area factor (adiabatic WURST/SECH) is refused. A calibration that itself railed
is rejected here rather than shipped.
Calibrations do not survive changes that physically invalidate them. The rules are grounded in measured behaviour of this endstation:
| Change | Dropped | Kept | Why |
|---|---|---|---|
| Rotary-vane move | auto_phase and pi_calibration |
rep_rate state |
The current policy clears phase and pulse calibration but retains the stored rate. Effective recovery depends on the complete sequence; remeasure after changing RV or pulse calibration before relying on the rate for quantitative work. |
Temperature setpoint move (beyond rephase_delta) |
auto_phase and rep_rate |
pi_calibration |
Temperature detunes the resonator, so the demod zero-order drifts — a measured temperature series showed a monotonic swing of roughly a degree of phase per kelvin, which sets the small default rephase_delta. And T₁ — the whole basis of the tune.rep_rate recommendation — changes strongly with temperature, often by orders of magnitude across a cryostat range. B₁ is untouched, so the amplitude calibration still holds. |
| Field move | nothing | all three | The current policy retains these results across field moves. This is a reuse policy, not a guarantee of identical recovery at every field; remeasure when the sample or selected transition requires it. |
The temperature rule compares the new setpoint against the temperature each
value was actually measured at (auto-phase and rep-rate both record it, and
they are checked independently — in a series they may have been tuned at
different points); only a move larger than rephase_delta drops a value, so
small excursions do not force a needless re-tune. Dropping is not
re-measuring: once the stored phase is gone, later acquisitions fall back to
the preset's own zero-order — and once the stored rate is gone, a later
rep_rate: auto step fails loudly instead of running saturated. A protocol
that changes temperature should therefore follow its temp.wait with a fresh
tune.auto_phase (and a fresh tune.rep_rate if the experiments use
rep_rate: auto) — the invalidation guarantees a stale value is never
silently carried, and the fresh step measures the new one.
Running the chain¶
This exercises the whole chain end to end with no hardware attached: every step pre-flights its exact worker arguments under a test-mode device, the judges and the state flow run, and canned measurements stand in for the acquisitions. It is the pre-flight to run after editing a protocol. The endstation itself is described on the endstation page; the steps used here, with their full parameter tables, are in the step reference.