You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+22-30Lines changed: 22 additions & 30 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -367,6 +367,7 @@ for board in info["boards"]:
367
367
raw_text = h.info(raw=True) # the verbatim hackrf_info output
368
368
```
369
369
370
+
370
371
### Bounded Capture to a File
371
372
372
373
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:
415
416
416
417
### Spectrum Sweep
417
418
418
-
`hackrf_sweep` emits CSV rows continuously; the library yields them parsed.
419
+
`hackrf_sweep` emits CSV rows continuously.
419
420
420
421
```python
421
422
from hackrfpy import HackRF
@@ -436,7 +437,7 @@ with h.sweep_stream(88e6, 108e6) as rows:
436
437
437
438
### Scan-then-Capture Pipeline
438
439
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.
440
441
441
442
```python
442
443
import numpy as np
@@ -491,29 +492,22 @@ fc = meta["captures"][0]["core:frequency"]
491
492
492
493
### Sample Data (no board required)
493
494
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.
498
497
499
498
```python
500
499
from hackrfpy import load_iq, read_sigmf_meta
501
500
iq = load_iq("examples/sample_data/fm_2Msps.iq")
502
501
meta = read_sigmf_meta("examples/sample_data/fm_2Msps.iq")
503
502
```
504
503
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:
508
505
509
506
```bash
510
507
uv run python examples/collect_sample_data.py --band fm --band ism433
511
508
```
512
509
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.
517
511
518
512
### Runnable Examples
519
513
@@ -538,7 +532,7 @@ Run any of them through uv so the project environment is used, e.g. `uv run pyth
538
532
539
533
## Method Reference
540
534
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.
542
536
543
537
### Receive / Capture
544
538
@@ -571,7 +565,7 @@ The `HackRF` class is composed from mixins. Methods build a `hackrf-tools` comma
***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.
@@ -657,7 +651,7 @@ with h.open_receiver(100e6, 8e6) as rx:
657
651
658
652
### Device Management (advanced)
659
653
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.
661
655
662
656
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.
663
657
@@ -700,7 +694,7 @@ Note that `-r` requires a clock number (e.g. `-r 3`); calling `clock("-r")` alon
700
694
701
695
***Builds:**`hackrf_debug <args...>`
702
696
***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.
#### SPI flash and CPLD — `spiflash_*`, `cpldjtag`
713
707
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.
715
709
716
710
| Method | Builds | Guard |
717
711
|---|---|---|
@@ -740,15 +734,15 @@ The `confirm=True` requirement exists because flashing firmware is the single mo
740
734
741
735
### Power and Relative Calibration
742
736
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.
744
738
745
739
| Method | Purpose |
746
740
|---|---|
747
741
|`power_dbfs(iq)`| mean power of a `complex64` block in dBFS (≤ 0; raw, uncalibrated). |
748
742
|`gain_db(lna, vga, amp)`| total RX gain through the chain in dB. The quantity that makes a raw dBFS reading ambiguous. |
749
743
|`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. |
750
744
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.
752
746
753
747
### Mode, Validation, and Feedback
754
748
@@ -764,7 +758,7 @@ The key property: the **same physical signal reads the same `relative_power_db`
764
758
765
759
## Device Capability Coverage
766
760
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.
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:
796
790
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.
798
792
*`hackrf_transfer -c` (constant-wave signal source) → **`transmit_cw(freq, sample_rate, amplitude=...)`**, TX-gated like any transmit.
799
793
800
-
### Genuinely not wrapped
801
-
802
-
What remains unexposed is minor and listed here so the boundary is explicit, not accidental:
794
+
### Not wrapped
803
795
804
796
*`hackrf_transfer -H` (hardware sync / wait-for-trigger) — a multi-device synchronization feature outside the single-device model here.
805
797
* 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.
806
798
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.
808
800
809
801
810
802
## CLI Reference
@@ -862,7 +854,7 @@ This is a brief section for anyone who jumped in a little ahead of the reading.
862
854
863
855
### What an SDR Is (and Isn't)
864
856
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.
866
858
867
859
### Some General HackRF Notes
868
860
@@ -881,15 +873,15 @@ For scripting and automating HackRF capture, sweep, and transmit from Python whe
881
873
882
874
### Does this replace the official hackrf-tools?
883
875
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.
885
877
886
878
### Why wrap the binaries instead of using libhackrf bindings?
887
879
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.
889
881
890
882
### Will there be signal processing (demod, FFTs, waterfalls)?
891
883
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`.
0 commit comments