CaltechExperimentalGravity / CaltechExperimentalGravity/system_ident
Stage D: CDSBackend construction + staging (channel maps, rate/periodicity checks)
- Dominant language
- Python
- Stars
- 0
- Forks
- 1
- PR merge metrics
- No merged PRs in 30d
Description
**Stage D (spec §4.2).** Fill part of the stub at [`src/system_ident/backends/cds.py:41`](https://github.com/CaltechExperimentalGravity/system_ident/blob/feat/cds-hardware-backend/src/system_ident/backends/cds.py#L41).
### `from_config(config)` is the constructor of record
**It must expose `exc_channels` and `readback_channels`** as `{channel: dof}`, the shape
`TwinBackend` and `RTSfreerunBackend` use. This is not cosmetic: [`src/system_ident/config.py:121`](https://github.com/CaltechExperimentalGravity/system_ident/blob/feat/cds-hardware-backend/src/system_ident/config.py#L121)
builds the watchdog with **no** channel maps and [`src/system_ident/safety.py:77`](https://github.com/CaltechExperimentalGravity/system_ident/blob/feat/cds-hardware-backend/src/system_ident/safety.py#L77) falls back to
`getattr(backend, "exc_channels", {})`. With neither attribute present:
- `Watchdog.evaluate` ([`src/system_ident/safety.py:100`](https://github.com/CaltechExperimentalGravity/system_ident/blob/feat/cds-hardware-backend/src/system_ident/safety.py#L100)) never matches a channel, so `breaches` is
always `[]` and **`check()` never raises** — actuator-saturation and RMS-ceiling auto-abort are both
dead on hardware.
- `Watchdog.abort` ([`src/system_ident/safety.py:137`](https://github.com/CaltechExperimentalGravity/system_ident/blob/feat/cds-hardware-backend/src/system_ident/safety.py#L137)) iterates `self.exc_channels`, so **`ramp_down`
is never called** — for a breach, an operator STOP, *or* the normal teardown at
[`src/system_ident/loop.py:187`](https://github.com/CaltechExperimentalGravity/system_ident/blob/feat/cds-hardware-backend/src/system_ident/loop.py#L187).
- [`src/system_ident/loop.py:232`](https://github.com/CaltechExperimentalGravity/system_ident/blob/feat/cds-hardware-backend/src/system_ident/loop.py#L232) reports `nan` and nothing complains.
One missing attribute silently removes all automatic safety. **Regression guard:**
`Watchdog(CDSBackend.from_config(cfg), limits).exc_channels != {}`.
**Probe the rate once and raise `ConfigError` unless** the hardware rate is an integer multiple of
`measurement.fs`, `T_perseg * fs_hw` is an integer, and `T_perseg * fs` is an integer (already implicitly
assumed at [`src/system_ident/loop.py:98`](https://github.com/CaltechExperimentalGravity/system_ident/blob/feat/cds-hardware-backend/src/system_ident/loop.py#L98)). Otherwise the drive is not periodic at `nperseg` after
decimation and the FRF cancellation guarantee (spec §2.1) evaporates. Print the probed rate and its
source channels — zero-risk, pre-injection, and worth more than any downstream cleverness.
### `inject(channel, ts, fs)` stages; it does not actuate
Following [`src/system_ident/backends/rtsfreerun_adapter.py:149`](https://github.com/CaltechExperimentalGravity/system_ident/blob/feat/cds-hardware-backend/src/system_ident/backends/rtsfreerun_adapter.py#L149), which already works this way:
1. **Stop any live injection on that channel first**, and bump a generation counter. Otherwise iteration
2 of *simultaneous* mode leaks a running excitation with no handle to stop it —
[`src/system_ident/loop.py:164`](https://github.com/CaltechExperimentalGravity/system_ident/blob/feat/cds-hardware-backend/src/system_ident/loop.py#L164)'s `ramp_down` is inside the `if sequential` branch only.
2. Resample **one period** with `sig.resample` and tile (see the `resample_poly` issue). Assert the length
is an exact multiple of the period.
3. **Do not** call `_soft_start_stop` — override it to a pass-through (see the ramp-contract issue).
4. Run the pre-injection drive check, then the approval gate, then construct but **do not start** the
`ArbitraryLoop`.
---
**Campaign:** CDS hardware backend · branch [`feat/cds-hardware-backend`](https://github.com/CaltechExperimentalGravity/system_ident/tree/feat/cds-hardware-backend)
· [spec](https://github.com/CaltechExperimentalGravity/system_ident/blob/feat/cds-hardware-backend/docs/superpowers/specs/2026-08-03-cds-hardware-backend-design.md) · [plan](https://github.com/CaltechExperimentalGravity/system_ident/blob/feat/cds-hardware-backend/docs/superpowers/plans/2026-08-03-cds-hardware-backend.md) · [handoff](https://github.com/CaltechExperimentalGravity/system_ident/blob/feat/cds-hardware-backend/notes/cds-hardware-bringup-2026-08.md)
*Code is deferred until the plan and issues have been reviewed.*
Contributor guide
No contributing guide indexed for this repository
Research direction
Start with src/system_ident/backends/cds.py:41 and compare staging behavior with src/system_ident/backends/rtsfreerun_adapter.py:149. Read the linked spec and plan, then trace config.py, safety.py, and loop.py to understand channel maps, rate checks, and teardown. Done means the regression guard passes, invalid rates raise ConfigError, and inject stages an unstarted ArbitraryLoop without live injection.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- backend, embedded-iot, testing-qa
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100