Segmented ArbitraryStream excitation with a cosine on/off envelope; ArbitraryLoop as a peer mode
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 38/100
Research direction
Start with backends/base.py, the shipped configs/rtsfreerun_hsts.yml, and the linked work items #5, #11, #13, #14, #16, #19, and #20. Check the existing fake harness and stream/loop entry points before defining the segmented stream path, analysed window, stop handling, and config wiring. Done means both modes remain supported and the specified tests and underrun checks cover their distinct envelope behavior.
Written by the indexing model from the issue text.
Description
Supersedes the design consequence of #9. Design detail: spec §2.3 (new), with the two corrections it rests on boxed in §2.2 and §8b.
Why
_soft_start_stop (backends/base.py) is scipy.signal.windows.tukey — a cosine taper, C¹, slope starts at zero. ArbitraryLoop.start(ramptime) is set_gain(0) then set_gain(g, ramptime=…) → awgSetGain — a linear gain ramp with a slope discontinuity at both ends. The 2026-08-04 operator run measured that linearity to five decimals (#27).
A cosine taper is gentler on the actuator and is therefore preferred. So "use the transport's own ramp", which #9's diagnosis implied, is an envelope-quality downgrade, not a neutral swap. It was only acceptable because a looped array leaves no alternative: tapering the staged array turns the taper into periodic AM and the seam re-excites the plant every cycle — measured 2.81e-1 FRF error (spec §2.2).
What
Build the entire excitation as one array carrying a cosine on/off envelope and inject it once with awg.ArbitraryStream, in four temporal segments:
| # | segment | duration | envelope | analysed? |
|---|---|---|---|---|
| 1 | ramp-on | t_ramp |
cosine, rising | no |
| 2 | settle — the system under test's transient decays | warmup_s |
flat, full amplitude | no, discarded |
| 3 | main excitation — read back, FRF measured | segment_duration × n_segments |
flat, full amplitude | yes |
| 4 | ramp-off | t_ramp |
cosine, falling | no |
Segments 1, 2 and 4 sit outside the analysed window, so the taper never touches the periods entering the FRF and segment 3 stays exactly periodic. read() must window segment 3 only (#14), and a config check should enforce that t_ramp fits the lead it tapers into.
No new config keys. The four durations derive from keys that already exist and already carry this meaning in the twin and rtsfreerun backends: t_ramp, warmup_s, segment_duration, n_segments. One meaning per knob across all backends. Shipped configs/rtsfreerun_hsts.yml gives 3 + 24 + 96 + 3 = 126 s.
Two findings that make this viable
Both verified read-only against awg 4.1.4 on the deployment machine:
ArbitraryStreamexists, andArbitraryLoopsubclasses it —class ArbitraryLoop(ArbitraryStream). Available:open(),append(data, scale),close(),send(),abort()→SIStrAbort,set_gain(gain, ramptime, wait)→awgSetGain.- A blocking claim in §2.2 is false. It argued against the one-shot construction because "
ramp_downwould have to rewrite an already-queued array, which is impossible." Not so:set_gainandabortare independent of the queued data. Soramp_down(channel, secs) → set_gain(0, ramptime=secs)maps exactly (#16), and #19's mandatory out-of-band STOP getsabort().
Note §2.2's own table already scored one-shot lead+record+tail at 8.8e-12 — as good as the ramptime path (1.4e-11). The preferred construction was measured all along, and it is the RTSfreerunBackend construction, so twin and hardware share one envelope construction instead of two.
ArbitraryLoop stays a first-class peer — cds.exc_mode: stream | loop
Not a deprecated path. Both modes supported, both tested, both in the #5 fake harness. exc_mode defaults to stream; choosing loop is a deliberate config choice and emits a warning naming the consequence — linear envelope, harsher on the actuator.
loop's four roles:
- Fallback if stream injection misbehaves on hardware.
- Development pipeline — simpler to drive.
- Valid on request, or wherever a linear ramp carries no disadvantage for the use case.
- Very long excitations — with the justification corrected below.
Role 4, computed rather than asserted
Per the feasibility gate in CLAUDE.md — no claimed limit without the number:
- Shipped config, one experiment = 126 s. At
fs_hw = 16384, float64:126 × 16384 × 8≈ 16.5 MB. Memory is a non-issue. - Q-limited physics-sized record:
T ≥ ~Q/f0givesT_perseg ≈ 1493 sfor Q = 1000 at 0.67 Hz, so 6 periods ≈ 8955 s ≈ 2.5 h → ≈1.17 GB per channel, ≈3.5 GB for 3 DoF simultaneous. Large, still tractable, and campaign estimates already run to hours. - But
appendcan be fed in chunks. The segmented envelope needs only the total length up front, not the samples, so the array need never be resident — it can be generated period by period. The memory rationale dissolves. - Stream underrun does not dissolve.
appendpushes data in real time from Python; a stall (GC, NFS, network) starves the stream and leaves a gap in the excitation.ArbitraryLoopis structurally immune — the front end repeats a staged buffer with no further Python involvement.
So role 4 is justified on underrun-immunity, not memory. Stronger argument, and it scales with record duration rather than being a remote edge case.
Underrun must be detected explicitly — it is a #8-class fault
A gap in the excitation is exactly what H_err and coherence cannot see (spec §3.3: they are period-to-period scatter, so a corrupted FRF can still report coherence ≈ 1). Reuse the two detections that already exist rather than inventing a third:
- #8's independent check — measured
Xbarphase vs the known injected multisine phase. - Compare the
_EXCreadback against the commanded array. Newly known feasible: §8b measured the excitation readback as NDS-readable at the model rate, round-tripping amplitude and shape exactly. A gap shows up directly.
Work items
-
#5— addFakeArbitraryStreamrecordingappendsizes/order so chunking and underrun are testable; keepFakeArbitraryLoop. -
#11— transport seam expresses both modes. -
#13— stream mode builds the segmented enveloped array; the_soft_start_stoppass-through override applies to loop mode only. -
#14— analysed read window = segment 3 only. -
#15— segment 2 is the settle. -
#16—set_gain(0, ramptime=…)graceful,abort()hard; underrun handling in thefinally. -
#19—abort()as the STOP path on the stream. -
#20—exc_modein the config schema and--cdswiring, with the loop-mode warning. -
base.pyramp contract — keep it two-branch, but each branch must state which envelope shape it produces and that cosine is preferred. That omission is what let the two be treated as equivalent. - Rework spec §4.1 / §4.2, which still specify
ArbitraryLoopas the only transport.
Deferred — planned, not implemented
- Shrinking segment 2, via #1 (Local Polynomial Method / GraFIT). LPM estimates the transient/leakage term instead of waiting it out, so the discarded settle — the dominant fixed cost per experiment — could shrink. Defer planning.
- MIMO. Segments 1–4 constitute one 'experiment'; a MIMO measurement contains one experiment per input, so
n_experiments = n_inputs. Defer planning.
Not in scope
Any hardware injection. ArbitraryStream has never been driven at the site — see #27 for the stream-specific unknowns. Every injection needs its own separate operator approval regardless of what came before.
- Dominant language
- Python
- Stars
- 0
- Forks
- 1
- PR merge metrics
- No merged PRs in 30d
Getting set up
This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from CaltechExperimentalGravity/system_ident
-
Difficulty 4/5 3-5 days Newbie friendliness 35/100
-
enhancement
Difficulty 5/5 Over a week Newbie friendliness 15/100
-
enhancement
Difficulty 5/5 Over a week Newbie friendliness 35/100
-
enhancement
Difficulty 5/5 Over a week Newbie friendliness 35/100
-
enhancement
Difficulty 5/5 Over a week Newbie friendliness 25/100
All issues in CaltechExperimentalGravity/system_ident
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
nansencenter/geodataset#39 ·
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
awslabs/visual-asset-management-system#804 ·
Maintainers usually reply within 1 day
-
follow-up
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
odoo/odoo#297927 · 1 comment ·
Maintainers usually reply within 1 day
-
link-rot
Difficulty 1/5 Under an hour Newbie friendliness 67/100