Skip to content

Commit a3db02a

Browse files
committed
README check pt
1 parent 09cbffd commit a3db02a

1 file changed

Lines changed: 22 additions & 30 deletions

File tree

README.md

Lines changed: 22 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,7 @@ for board in info["boards"]:
367367
raw_text = h.info(raw=True) # the verbatim hackrf_info output
368368
```
369369

370+
370371
### Bounded Capture to a File
371372

372373
A fixed number of samples to an `.iq` file, with a SigMF metadata sidecar written alongside so the recording is self-describing.
@@ -415,7 +416,7 @@ with h.capture_stream(433.92e6, 8e6) as blocks:
415416

416417
### Spectrum Sweep
417418

418-
`hackrf_sweep` emits CSV rows continuously; the library yields them parsed.
419+
`hackrf_sweep` emits CSV rows continuously.
419420

420421
```python
421422
from hackrfpy import HackRF
@@ -436,7 +437,7 @@ with h.sweep_stream(88e6, 108e6) as rows:
436437

437438
### Scan-then-Capture Pipeline
438439

439-
A pipeline the class can express that CLI-only tools cannot: sweep a band, find the strongest bin, capture there.
440+
A pipeline the class can express that CLI-only tools cannot: sweep a band, find the strongest bin, and capture there.
440441

441442
```python
442443
import numpy as np
@@ -491,29 +492,22 @@ fc = meta["captures"][0]["core:frequency"]
491492

492493
### Sample Data (no board required)
493494

494-
The repo ships **real recordings** under `examples/sample_data/` so you can try
495-
the library — and downstream signal processing — without owning a HackRF. Each
496-
`.iq` is interleaved int8 I/Q with a `.sigmf-meta` sidecar, plus sweep CSVs and a
497-
provenance README noting the firmware/tools that produced them.
495+
Ral recordings have been included under `examples/sample_data/` so the library basics can be tested without hardware. Each
496+
`.iq` is interleaved int8 I/Q with a `.sigmf-meta` sidecar, plus sweep CSVs. The README in `examples/sample_data/`notes the firmware/tools that produced the data.
498497

499498
```python
500499
from hackrfpy import load_iq, read_sigmf_meta
501500
iq = load_iq("examples/sample_data/fm_2Msps.iq")
502501
meta = read_sigmf_meta("examples/sample_data/fm_2Msps.iq")
503502
```
504503

505-
To collect your own (read-only; never transmits), use the sample collector. It
506-
preflights for a board, then captures real IQ + sweep data per band with SigMF
507-
metadata:
504+
To collect your own (read-only; never transmits), use the sample collector. It preflights for a board, then captures real IQ + sweep data per band with SigMF metadata:
508505

509506
```bash
510507
uv run python examples/collect_sample_data.py --band fm --band ism433
511508
```
512509

513-
Defaults are kept small (0.5 s at 2 Msps) so they can live in the repo; use
514-
`--seconds` / `--sample-rate` for larger local datasets. This is distinct from
515-
`tests/collect_real_data.py`, which freezes tiny verbatim slices as *parser test
516-
fixtures* rather than usable sample datasets.
510+
Defaults are kept small (0.5 s at 2 Msps) so they can live in the repo; use `--seconds` / `--sample-rate` for larger local datasets. This is distinct from `tests/collect_real_data.py`, which freezes tiny verbatim slices as parser test fixtures rather than usable sample datasets.
517511

518512
### Runnable Examples
519513

@@ -538,7 +532,7 @@ Run any of them through uv so the project environment is used, e.g. `uv run pyth
538532

539533
## Method Reference
540534

541-
The `HackRF` class is composed from mixins. Methods build a `hackrf-tools` command and run it through `_run` in one of four lifecycle modes (`blocking`, `timed`, `handle`, `stream`). Pass `print_cmd=True` to most methods to print the exact command that *would* run, without running it useful for debugging and for learning the underlying tool invocation.
535+
The `HackRF` class is composed from mixins. Methods build a `hackrf-tools` command and run it through `_run` in one of four lifecycle modes (`blocking`, `timed`, `handle`, `stream`). Pass `print_cmd=True` to most methods to print the exact command that *would* run, without running it. This is useful for debugging and for learning the underlying tool invocation.
542536

543537
### Receive / Capture
544538

@@ -571,7 +565,7 @@ The `HackRF` class is composed from mixins. Methods build a `hackrf-tools` comma
571565
#### `scan_frequencies`
572566
* **Signature:** `scan_frequencies(freqs, sample_rate, num_samples, *, on_capture=None, **kwargs)`
573567
* **Returns:** `{freq: complex64}` dict, or `None` if `on_capture` is given.
574-
* **Notes:** sequentially captures `num_samples` at each frequency, retuning between them. Makes multi-frequency capture one call. **Not gapless** — each retune is a fresh `hackrf_transfer` with a short re-open (the binary can't retune mid-stream); pass `on_capture(freq, iq)` to process-and-discard instead of holding every array.
568+
* **Notes:** sequentially captures `num_samples` at each frequency, retuning between them. Makes multi-frequency capture one call. **Not gapless**! Each retune is a fresh `hackrf_transfer` with a short re-open (the binary can't retune mid-stream); pass `on_capture(freq, iq)` to process-and-discard instead of holding every array.
575569

576570
#### `open_receiver`
577571
* **Signature:** `open_receiver(freq, sample_rate, *, lna=16, vga=20, amp=False, baseband_bw=None, read_samples=131072)`
@@ -657,7 +651,7 @@ with h.open_receiver(100e6, 8e6) as rx:
657651

658652
### Device Management (advanced)
659653

660-
These wrap the less-common tools. The firmware-writing operations are **brick-guarded** — they refuse unless `confirm=True` is passed explicitly, because a bad image or interrupted write can permanently disable the board.
654+
These wrap the less-common tools. The firmware-writing operations are **brick-guarded** (for lack of a better term). They refuse unless `confirm=True` is passed explicitly, because a bad image or interrupted write can permanently disable the board.
661655

662656
The goal here is **full scripting access**: even where the library doesn't model every flag of a tool, the wrapping method forwards arbitrary arguments so nothing on the device is unreachable from Python.
663657

@@ -700,7 +694,7 @@ Note that `-r` requires a clock number (e.g. `-r 3`); calling `clock("-r")` alon
700694

701695
* **Builds:** `hackrf_debug <args...>`
702696
* **Signature:** `debug(*args, print_cmd=False)`
703-
* **Full passthrough.** Low-level read/write of the radio's chip registers (MAX2837, Si5351C, RFFC5071) for debugging and advanced configuration. **This is a sharp tool** writing registers can put the radio in an undefined state. Read access is harmless; write access is your responsibility.
697+
* **Full passthrough.** Low-level read/write of the radio's chip registers (MAX2837, Si5351C, RFFC5071) for debugging and advanced configuration. **Use with care:** writing registers can put the radio in an undefined state. Read access is harmless; write access is your responsibility.
704698

705699
```python
706700
h = HackRF()
@@ -711,7 +705,7 @@ h.debug("--rffc5071", "-r") # dump RFFC5071 registers
711705

712706
#### SPI flash and CPLD — `spiflash_*`, `cpldjtag`
713707

714-
These read and (dangerously) write the device's firmware storage. The write paths are brick-guarded.
708+
These read and (dangerously) write the device's firmware storage. The write paths are brick-guarded. DO NOT USE THIS IF YOU DON'T KNOW WHAT YOU'RE DOING.
715709

716710
| Method | Builds | Guard |
717711
|---|---|---|
@@ -740,15 +734,15 @@ The `confirm=True` requirement exists because flashing firmware is the single mo
740734

741735
### Power and Relative Calibration
742736

743-
The HackRF is **not a calibrated instrument** — its raw dBFS amplitude is relative to ADC full scale, not power at the antenna, and it depends on the gain you set. These helpers (Level 1) make readings *consistent*; turning them into approximate dBm (Levels 2–3) is a hardware-and-reference workflow shown in `examples/calibrate.py`.
737+
The HackRF is **not a calibrated instrument**. Its raw dBFS amplitude is relative to ADC full scale, not power at the antenna, and it depends on the gain you set. These helpers (Level 1) make readings *consistent*; turning them into approximate dBm (Levels 2–3) is a hardware-and-reference workflow shown in `examples/calibrate.py`. This calibration example is not meant to be echaustive, and the value it adds to an individual project or process is dependent on external tools.
744738

745739
| Method | Purpose |
746740
|---|---|
747741
| `power_dbfs(iq)` | mean power of a `complex64` block in dBFS (≤ 0; raw, uncalibrated). |
748742
| `gain_db(lna, vga, amp)` | total RX gain through the chain in dB. The quantity that makes a raw dBFS reading ambiguous. |
749743
| `relative_power_db(iq_or_dbfs, *, lna=, vga=, amp=, offset_db=0, freq_hz=, freq_correction=)` | gain-normalized power: subtracts the gain chain so readings at *different gains are comparable*. Gains default from `last_params`. Pass `offset_db` (from a calibration run) to approximate dBm, and a `freq_correction(freq)→dB` callable to flatten the front-end response. |
750744

751-
The key property: the **same physical signal reads the same `relative_power_db` value regardless of gain**, where raw dBFS would shift by the gain delta. The library does only pure math here — it never invents reference data or claims a dBm it can't back up. To derive `offset_db` (feed a known power) or a `freq_correction` curve (sweep a flat source), run `examples/calibrate.py`; it saves a `calibration.json` you feed back in.
745+
The key property: the **same physical signal reads the same `relative_power_db` value regardless of gain**, where raw dBFS would shift by the gain delta. The library does only pure math here. To derive `offset_db` (feed a known power) or a `freq_correction` curve (sweep a flat source), run `examples/calibrate.py`; it saves a `calibration.json` you feed back in.
752746

753747
### Mode, Validation, and Feedback
754748

@@ -764,7 +758,7 @@ The key property: the **same physical signal reads the same `relative_power_db`
764758

765759
## Device Capability Coverage
766760

767-
Great Scott Gadgets ships **eight** `hackrf-tools` binaries. This library wraps **all eight** — there is no device-interfacing tool it can't reach. (For comparison, some popular wrappers omit clock, cpldjtag, debug, and spiflash.)
761+
Great Scott Gadgets ships eight `hackrf-tools` binaries. This library wraps all eight. Not all eight are needed by the average device user.
768762

769763
| Binary | Wrapped by | Coverage |
770764
|---|---|---|
@@ -794,17 +788,15 @@ h.debug("--si5351c", "-n", "0", "-r") # read a register
794788

795789
The handful of flags that don't fit the main capture/sweep paths each have a dedicated method, so they're first-class rather than escape-hatch-only:
796790

797-
* `hackrf_sweep -B` / `-I` (binary / inverse-FFT output) → **`sweep_to_file(..., binary=True)`** / **`sweep_to_file(..., inverse_fft=True)`**. The text generator (`sweep`) stays CSV; the file method owns the binary formats. (The library does not *parse* the binary output — you own that on read.)
791+
* `hackrf_sweep -B` / `-I` (binary / inverse-FFT output) → **`sweep_to_file(..., binary=True)`** / **`sweep_to_file(..., inverse_fft=True)`**. The text generator (`sweep`) stays CSV; the file method owns the binary formats. The library does not *parse* the binary output, that has to be done in code later.
798792
* `hackrf_transfer -c` (constant-wave signal source) → **`transmit_cw(freq, sample_rate, amplitude=...)`**, TX-gated like any transmit.
799793

800-
### Genuinely not wrapped
801-
802-
What remains unexposed is minor and listed here so the boundary is explicit, not accidental:
794+
### Not wrapped
803795

804796
* `hackrf_transfer -H` (hardware sync / wait-for-trigger) — a multi-device synchronization feature outside the single-device model here.
805797
* The exact flag set of `clock`/`operacake`/`debug` is version-dependent; the library forwards whatever you pass but doesn't validate it against your tools version.
806798

807-
If you need `-H` before it's wrapped, drop to the internal runner `h._run(["transfer", "-t", "f.iq", "-H", ...], mode="handle", kind="tx")`accepting that you're then responsible for the invocation. Wrapping it properly (a typed parameter + a construction test) is a small, welcome addition.
799+
If you need `-H` before it's wrapped, drop to the internal runner ( `h._run(["transfer", "-t", "f.iq", "-H", ...], mode="handle", kind="tx")`) accepting that you're then responsible for the invocation. Wrapping it properly (a typed parameter + a construction test) has not been added to this library yet.
808800

809801

810802
## CLI Reference
@@ -862,7 +854,7 @@ This is a brief section for anyone who jumped in a little ahead of the reading.
862854

863855
### What an SDR Is (and Isn't)
864856

865-
A software-defined radio moves the radio's "knobs" tuning, filtering, demodulation into software. The HackRF is a *wide-band, half-duplex, 8-bit* SDR: it covers an enormous frequency range but sees one band at a time, can't receive and transmit simultaneously, and quantizes coarsely (8-bit). That makes it a superb tool for exploration, capture, and experimentation, and a poor choice when you need high dynamic range or full-duplex operation. It is not a spectrum analyzer (though `hackrf_sweep` approximates one), and it is not a vector network analyzer.
857+
A software-defined radio moves the radio's "knobs" (that is, functions for for tuning, filtering, demodulation) into software. The HackRF is a *wide-band, half-duplex, 8-bit* SDR: it covers an enormous frequency range but sees one band at a time, can't receive and transmit simultaneously, and quantizes coarsely (8-bit). That makes it a superb tool for exploration, capture, and experimentation, and a poor choice when you need high dynamic range or full-duplex operation. It is not a spectrum analyzer (though `hackrf_sweep` approximates one), and it is not a vector network analyzer.
866858

867859
### Some General HackRF Notes
868860

@@ -881,15 +873,15 @@ For scripting and automating HackRF capture, sweep, and transmit from Python whe
881873

882874
### Does this replace the official hackrf-tools?
883875

884-
No — it *wraps* them. You still install `hackrf-tools`; this library orchestrates them and gives you a Python API and a friendlier CLI on top.
876+
No. What this library does is *wraps* `hackrf-tools` to make working with the HackRF One a bit easier on Windows. You still install `hackrf-tools`; this library orchestrates them and gives you a Python API and a friendlier CLI on top.
885877

886878
### Why wrap the binaries instead of using libhackrf bindings?
887879

888-
Portability, especially on Windows. No C extension to compile means no toolchain matching, no build failures, and an install that just works once the binaries are present. The trade-off is the system dependency on `hackrf-tools` and a dependence on their stdout/exit behavior, which the library probes and version-checks.
880+
Portability, especially on Windows. No C extension to compile means no toolchain matching, no build failures, and an install that works once the binaries are present. The trade-off is the system dependency on `hackrf-tools` and a dependence on their stdout/exit behavior, which the library probes and version-checks.
889881

890882
### Will there be signal processing (demod, FFTs, waterfalls)?
891883

892-
Not in this library. That is the planned second project. This one stops at delivering `complex64`; see [project_summary.md](project_summary.md) for the split.
884+
Not in this library. That is the planned second project. This one stops at delivering `complex64`.
893885

894886
### How often is this updated?
895887

0 commit comments

Comments
 (0)