Skip to content

Commit 517e5a9

Browse files
committed
test set one and issue reporting templates
1 parent 1e3121d commit 517e5a9

11 files changed

Lines changed: 1166 additions & 21 deletions

File tree

‎.github/ISSUE_TEMPLATE/bug_report.yml‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,10 @@ body:
118118
attributes:
119119
label: sdr_dsp version
120120
description: |
121-
Not on PyPI yet, so this is a commit — `git rev-parse --short HEAD`.
122-
placeholder: "e.g. local checkout at a1b2c3d"
121+
`python -c "import sdr_dsp; print(sdr_dsp.__version__)"` for an
122+
installed release; for a checkout, the commit as well
123+
(`git rev-parse --short HEAD`).
124+
placeholder: "e.g. 0.2.0 from PyPI, or local checkout at a1b2c3d"
123125
validations:
124126
required: true
125127

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
name: Hardware / bench report
2+
description: Results from running sdr_dsp against a real radio (works, doesn't, or partly)
3+
labels: ["hardware"]
4+
body:
5+
- type: markdown
6+
attributes:
7+
value: |
8+
Use this for anything that only shows up with a board attached: the
9+
`-m hardware` tests, a live capture or transmit example, a new SDR
10+
adapter, or a modulation that has only ever been verified against
11+
synthetic signals (everything except FM receive -- see
12+
`docs/MODULATIONS.md`). **A report that something works is as useful
13+
as one that something doesn't:** the hardware items in `docs/TODO.md`
14+
are gated on exactly these runs, and the maintainer has one HackRF One
15+
at one location.
16+
17+
If a HackRF is involved, please run the preflight first and paste its
18+
verdict below -- it checks that the board is healthy and the antenna
19+
receives anything at all, which is where most "nothing works" reports
20+
end up:
21+
22+
```bash
23+
uv run python tools/preflight_collection.py
24+
```
25+
26+
- type: dropdown
27+
id: outcome
28+
attributes:
29+
label: Outcome
30+
options:
31+
- "Works as documented"
32+
- "Works with caveats (describe below)"
33+
- "Does not work"
34+
- "Mixed / partial"
35+
validations:
36+
required: true
37+
38+
- type: dropdown
39+
id: what
40+
attributes:
41+
label: What was exercised
42+
options:
43+
- "pytest -m hardware (the hardware-marked test suite)"
44+
- "Live receive example (FM listen / band explorer / capture)"
45+
- "Transmit example (hackrf_sink / two_station_link / transmit_examples)"
46+
- "Gain search / calibrate / preflight tooling"
47+
- "A non-FM demodulator on a real off-air signal"
48+
- "A new or non-HackRF device adapter"
49+
- "Other (describe below)"
50+
validations:
51+
required: true
52+
53+
- type: textarea
54+
id: radio
55+
attributes:
56+
label: Radio and setup
57+
description: Board, firmware, how it is driven, and the RF side of things.
58+
value: |
59+
- radio (model, firmware/hardware revision):
60+
- driver path (hackrfpy version / hackrf-tools version / other):
61+
- antenna and band:
62+
- sample rate and center frequency used:
63+
- amp / LNA / VGA (or equivalent gain) settings:
64+
- location type (urban / rural; any known strong local transmitters):
65+
render: markdown
66+
validations:
67+
required: true
68+
69+
- type: textarea
70+
id: command
71+
attributes:
72+
label: Exact command(s)
73+
render: shell
74+
validations:
75+
required: true
76+
77+
- type: textarea
78+
id: readiness
79+
attributes:
80+
label: preflight_collection.py output (HackRF only)
81+
render: shell
82+
validations:
83+
required: false
84+
85+
- type: textarea
86+
id: results
87+
attributes:
88+
label: What happened
89+
description: |
90+
For a pass: anything that was borderline (pilot SNR near threshold,
91+
clip backoff triggered, drops reported). For a failure: the full
92+
output / traceback, and whether the same capture replays correctly
93+
from a file (that separates a DSP problem from a streaming one).
94+
validations:
95+
required: true
96+
97+
- type: textarea
98+
id: capture
99+
attributes:
100+
label: Capture available?
101+
description: |
102+
A short (few-second) IQ recording with its `.sigmf-meta` sidecar is
103+
the single most valuable attachment; it lets the problem be
104+
reproduced without your board. Link it here (release asset, cloud
105+
drive, etc.). Do not paste binary data into the issue.
106+
validations:
107+
required: false
108+
109+
- type: input
110+
id: env
111+
attributes:
112+
label: OS / Python / sdr_dsp version
113+
placeholder: "e.g. Windows 11, Python 3.12, sdr_dsp 0.2.0 (or commit a1b2c3d)"
114+
validations:
115+
required: true

‎.github/PULL_REQUEST_TEMPLATE.md‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<!-- Thanks. Short is fine; the checklist is what CI will ask anyway. -->
2+
3+
## What this changes
4+
5+
<!-- One or two sentences. Link the issue if there is one: "Fixes #12". -->
6+
7+
## Why
8+
9+
<!-- The problem or motivation. For a DSP change, what was wrong with the
10+
output before, and how you know it is right now (scipy oracle,
11+
synthetic ground truth, a real capture). -->
12+
13+
## Checklist
14+
15+
- [ ] `uv run pytest -m "not hardware"` passes locally
16+
- [ ] `uv run ruff check .` and `uv run mypy` are clean
17+
- [ ] New public functions and methods have docstrings (`test_public_surface.py` enforces this)
18+
- [ ] New tests seed randomness explicitly (`np.random.default_rng(seed)`), never the builtin `hash()`
19+
- [ ] New files are LF line-ended (CI checks the index)
20+
- [ ] `CHANGELOG.md` has an entry under **Unreleased** if a user would notice the change
21+
- [ ] Docs updated if behaviour or the public API changed (`docs/sdr_dsp_REFERENCE.md`, `docs/MODULATIONS.md`, `docs/EXAMPLES.md` as relevant)
22+
23+
## Hardware
24+
25+
<!-- Delete if not applicable. If this touches a device adapter, a live
26+
example, or the TX path: was it run on a board? Which one, which band,
27+
what happened? "Not run on hardware" is an acceptable answer -- it just
28+
needs saying, since most of the digital path is software-verified only. -->

‎.github/workflows/tests.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ jobs:
3939
# macos keep the pure-DSP core honest cross-platform. No board is
4040
# needed -- every hardware test self-skips without one.
4141
os: [windows-latest, ubuntu-latest, macos-latest]
42-
python-version: ["3.11", "3.12", "3.13"]
42+
python-version: ["3.11", "3.12", "3.13", "3.14"]
4343

4444
steps:
4545
- uses: actions/checkout@v4

‎CHANGELOG.md‎

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# Changelog
2+
3+
User-facing changes to `sdr_dsp`, newest first. The format follows
4+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and versions follow
5+
[Semantic Versioning](https://semver.org/) -- while the major version is 0,
6+
minor releases may change the public API.
7+
8+
The dated engineering narrative behind these entries (what was tried, what
9+
was measured, why a design went the way it did) lives in `docs/LOG.md`.
10+
11+
## [Unreleased]
12+
13+
### Fixed
14+
- Docstrings on `GainStage.default_value` and
15+
`GainModel.by_name` / `min_settings` / `default_frontend` / `frontend_db`;
16+
the public-surface test was failing on every CI platform without them.
17+
18+
### Changed
19+
- Package description and README links prepared for PyPI (absolute links to
20+
the docs; "personal" dropped from the one-line description).
21+
- Python 3.14 added to the CI matrix and the trove classifiers.
22+
23+
### Added
24+
- 89 edge-case tests (`tests/test_core_edges.py`, `test_io_link_edges.py`,
25+
`test_stream_sources_edges.py`) covering empty-input guards, argument
26+
validation, every streaming stage's `reset()`, and alternative branches
27+
that had no test (LSB modulation, the odd-length Hilbert kernel, the RRC
28+
singularity tap, explicit ASK/FSK decision levels, SigMF path resolution,
29+
gain-search escalation and back-off). Line coverage 95% -> 99.6%.
30+
- Hardware / bench-report issue form and a pull-request template.
31+
- This changelog.
32+
33+
## [0.2.0] - 2026-10-24
34+
35+
First version prepared for packaging. Not yet published to PyPI. Highlights
36+
of the tree as of this version, by area; see `docs/MODULATIONS.md` for what
37+
"supported" means per scheme and `docs/sdr_dsp_REFERENCE.md` for the full map.
38+
39+
### Added
40+
- **Receive:** FM demodulation with carrier-offset estimate/correct and a
41+
pilot-locked stereo decoder (validated on real over-the-air captures);
42+
AM / SSB / CW; OOK / ASK; 2-FSK and N-level FSK; the PSK family; QAM-16;
43+
spread-spectrum despreading. Digital schemes are verified against
44+
synthetic ground truth only.
45+
- **Transmit:** matching modulators with pulse shaping, driven through the
46+
`TXSink` seam; a loopback sink for closed-loop software tests.
47+
- **Link layer:** framing with CRC, and a windowed ARQ engine (window 1 =
48+
stop-and-wait) with pluggable drivers, proven in closed-loop simulation.
49+
- **Core DSP:** FIR design/apply, polyphase resampling, channelization,
50+
mixing, spectral estimates, AGC, timing and carrier sync, calibration,
51+
detection, measurement, and channel-impairment modelling with feature
52+
extraction (fingerprint F0-F2/F4/F5).
53+
- **Streaming:** `Pipeline` plus block-wise `StreamingFIR`,
54+
`StreamingResampler` and related stages for live use.
55+
- **I/O:** SigMF read/write; `FileSource` / `ArraySource`; WAV, IQ, plot and
56+
TX sinks. Device adapters (HackRF via `hackrfpy`) live in `examples/`,
57+
not the core.
58+
- **Gain search:** device-agnostic `GainModel` / `GainStage` with a
59+
measured-pilot quality metric and clip back-off.
60+
- `py.typed` marker (most signatures are still unannotated; see
61+
`docs/TODO.md`).
62+
63+
### Infrastructure
64+
- CI matrix: Windows / Ubuntu / macOS x Python 3.11-3.13, an oldest-declared
65+
numpy/scipy leg, a build-the-wheel-and-import-it leg, ruff + mypy, and an
66+
LF line-ending guard.
67+
68+
[Unreleased]: https://github.com/LC-Linkous/sdr_dsp_python/compare/v0.2.0...HEAD
69+
[0.2.0]: https://github.com/LC-Linkous/sdr_dsp_python/releases/tag/v0.2.0

‎TODO.md‎

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
# TODO
1+
# TODO (September 2026 session journal)
2+
3+
> **The live list is `docs/TODO.md`.** This file is the running journal from
4+
> the September 2026 review/repair sessions, kept for the reasoning behind
5+
> the open items. Items marked done below after 2026-10 were closed when
6+
> the tree was prepared for packaging; the hardware items remain open.
27
38
Running list from the September 2026 review/repair sessions. Done so far,
49
for context: capture_health mid-band noise reference, the channelized 19 kHz
@@ -184,7 +189,8 @@ files are untracked and must be `git add`ed -- see section 6.
184189
/ device adapter / hardware -- with a diagram, the sample-format
185190
normalization table (ci8/cu8/cf32 -> complex64), and the honest note that
186191
the gain-search/probe tooling is HackRF-shaped and not yet portable.
187-
- [ ] Add `[project.urls]` to pyproject when the repo is pushed.
192+
- [x] Add `[project.urls]` to pyproject when the repo is pushed. (Done; Homepage,
193+
Repository and Issues are set.)
188194

189195
## 6. Open issues & warnings
190196

@@ -221,20 +227,23 @@ tasks so much as things to keep in view.
221227
is random bits with no group framing -- decodable-looking, not decodable.
222228
Do NOT use it as RDS ground truth; the real capture's frames are the only
223229
valid reference (section 2 RDS item).
224-
- **py.typed is NOT shipped.** mypy's default checks are gated, but the
225-
package makes no typing promise to downstream users yet. Enabling strict
226-
flags before the annotation pass would fail every module (section 3).
230+
- **py.typed IS shipped (since patches 0019-0020) but overpromises**: most
231+
signatures are unannotated, so downstream mypy sees `Any` for much of the
232+
API. Strict flags come on per module as each is annotated (docs/TODO.md).
227233
- **Rare-flake vigilance.** The known flake (hash()-seeded fixtures) is
228234
fixed and CI pins PYTHONHASHSEED, but any NEW test using unseeded
229235
randomness or the builtin hash() for seeding can reintroduce order/run
230236
dependence. Always seed with default_rng(fixed) or hashlib.
231-
- **Untracked files must be `git add`ed before they push.** The 785-passing
237+
- ~~**Untracked files must be `git add`ed before they push.**~~ (Resolved: all
238+
listed files, including this TODO.md and .github/, are tracked on main.)
239+
Original note: The 785-passing
232240
state depends on files git does not yet track: TODO.md (kept untracked on
233241
purpose), the three test files that were never added (test_collection_fixes,
234242
test_real_demod_e2e, test_stereo_decode), and this session's new
235243
test_cfo.py, test_transmit_examples.py, examples/transmit_examples.py, and
236244
docs/MODULARITY.md. A clone/CI without them is NOT the tree tested here.
237-
- **Confirm .github/ is committed.** It was deleted in a working tree this
245+
- ~~**Confirm .github/ is committed.**~~ (Confirmed; tests.yml runs on main.)
246+
Original note: It was deleted in a working tree this
238247
session (unstaged); CI (patch 0014) only runs if the workflow is actually in
239248
the pushed tree. Check `git status` for a stray .github deletion before push.
240249
- **Line endings normalized 2026-09-20** -- tree is LF. Keep NEW files LF; a

‎sdr_dsp/README.md‎

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ flowgraph runtime; you call functions and compose them. The DSP core imports no
88
specific radio; device adapters (HackRF, files) live at the edges.
99

1010
Developed and tested against a **HackRF One**, but the core processes IQ
11-
regardless of source. See `docs/HARDWARE.md` for what that hardware can and
11+
regardless of source. See [`docs/HARDWARE.md`](https://github.com/LC-Linkous/sdr_dsp_python/blob/main/docs/HARDWARE.md) for what that hardware can and
1212
can't do, and why it's a good development platform.
1313

1414
## Status
@@ -18,21 +18,21 @@ Some features have hooks that aren't implemented. What is proven where:
1818

1919
- **FM receive** — validated on real, ear-verified over-the-air captures.
2020
- **Other demodulators (AM/SSB/CW, OOK/ASK, FSK, the PSK family, QAM-16)** —
21-
verified against synthetic ground truth; see `docs/MODULATIONS.md` for the
21+
verified against synthetic ground truth; see [`docs/MODULATIONS.md`](https://github.com/LC-Linkous/sdr_dsp_python/blob/main/docs/MODULATIONS.md) for the
2222
honest per-scheme status (Supported / Demonstrable / Visualize-only).
2323
- **Transmit and the ARQ link protocol** — proven in software (closed-loop
2424
simulation); the real-radio transmit path is not yet validated. See §12 of
25-
`docs/sdr_dsp_REFERENCE.md`.
25+
[`docs/sdr_dsp_REFERENCE.md`](https://github.com/LC-Linkous/sdr_dsp_python/blob/main/docs/sdr_dsp_REFERENCE.md).
2626

2727
## Start here
2828

29-
- `docs/sdr_dsp_REFERENCE.md` — the comprehensive technical reference
29+
- [`docs/sdr_dsp_REFERENCE.md`](https://github.com/LC-Linkous/sdr_dsp_python/blob/main/docs/sdr_dsp_REFERENCE.md) — the comprehensive technical reference
3030
(architecture, module map, usage, extension guide, limitations).
31-
- `docs/EXAMPLES.md` — a catalog of every runnable script in `examples/`.
32-
- `docs/MODULATIONS.md` — what's supported and what "supported" means.
33-
- `docs/HARDWARE.md`, `docs/DC_SPIKE.md` — device behavior and the offset-tuning
31+
- [`docs/EXAMPLES.md`](https://github.com/LC-Linkous/sdr_dsp_python/blob/main/docs/EXAMPLES.md) — a catalog of every runnable script in [`examples/`](https://github.com/LC-Linkous/sdr_dsp_python/blob/main/sdr_dsp/examples).
32+
- [`docs/MODULATIONS.md`](https://github.com/LC-Linkous/sdr_dsp_python/blob/main/docs/MODULATIONS.md) — what's supported and what "supported" means.
33+
- [`docs/HARDWARE.md`](https://github.com/LC-Linkous/sdr_dsp_python/blob/main/docs/HARDWARE.md), [`docs/DC_SPIKE.md`](https://github.com/LC-Linkous/sdr_dsp_python/blob/main/docs/DC_SPIKE.md) — device behavior and the offset-tuning
3434
recipe every direct-conversion capture needs.
35-
- `docs/LOG.md` — dated development log of major changes and fixes.
35+
- [`docs/LOG.md`](https://github.com/LC-Linkous/sdr_dsp_python/blob/main/docs/LOG.md) — dated development log of major changes and fixes.
3636

3737
Quick example:
3838

@@ -47,8 +47,22 @@ audio = fm_demod(fir_apply(iq, design_lowpass(100e3, fs)),
4747
deviation_hz=75e3, sample_rate=fs)
4848
```
4949

50-
Setup uses [uv](https://docs.astral.sh/uv/): `uv sync` for the library and
51-
tests, `uv sync --extra examples` for everything the examples can use.
50+
## Install
51+
52+
```bash
53+
pip install sdr-dsp # core: numpy + scipy only
54+
pip install "sdr-dsp[plotting]" # + matplotlib, for the analyzer/spectrogram examples
55+
pip install "sdr-dsp[audio]" # + sounddevice, for live FM playback
56+
pip install "sdr-dsp[examples]" # everything the examples can use (adds hackrfpy)
57+
```
58+
59+
Live capture additionally needs the `hackrf-tools` binaries installed at the
60+
OS level; the library itself never imports a device driver.
61+
62+
For development, clone the repo and use [uv](https://docs.astral.sh/uv/)
63+
from the `sdr_dsp/` directory: `uv sync` installs the library, the test
64+
suite and the dev tools; `uv run pytest -m "not hardware"` runs everything
65+
that needs no radio.
5266

5367
---
5468

‎sdr_dsp/pyproject.toml‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[project]
22
name = "sdr_dsp"
33
version = "0.2.0"
4-
description = "A personal, fully-functional DSP library for software-defined radio IQ."
4+
description = "A device-agnostic digital signal processing library for software-defined radio IQ."
55
readme = "README.md"
66
requires-python = ">=3.11"
77
license = "GPL-2.0-only"
@@ -14,9 +14,11 @@ classifiers = [
1414
"Programming Language :: Python :: 3.11",
1515
"Programming Language :: Python :: 3.12",
1616
"Programming Language :: Python :: 3.13",
17+
"Programming Language :: Python :: 3.14",
1718
"Topic :: Communications :: Ham Radio",
1819
"Topic :: Scientific/Engineering",
1920
"Operating System :: OS Independent",
21+
"Typing :: Typed",
2022
]
2123

2224
# Core depends only on numpy + scipy. scipy is used for filter-coefficient

0 commit comments

Comments
 (0)