Skip to content

Commit bbb066b

Browse files
authored
Platinum BLUE converter (#122)
* Non-SigMF Converters * Initial code by KelseyCreekSoftware * fromfile() now autodetects SigMF, BLUE, & WAV formats automatically * allow converters to --archive (.sigmf) or create SigMF pairs (.sigmf-data & .sigmf-meta) * Converters now support conversion to non-conforming dataset without writing datafiles back to disk * BLUE * Validated implementation against lots of files beyond nonsigmf-examples repO * parse into (fixed, adjunct, extended) keys * add CRC validation for bluefile metadata * add support for metadata-only (0 sample) BLUE files * WAV * Drop support for float WAV files; tricky to support NCD * Single entry point (sigmf_convert) * conversion returns metadata for newly created file * enforce checksum calculation * account for metadata-only files with heading & trailing bytes * ensure for NCD conversion that both input & output are in same folder * homologate API for wav & blue converter * integrate feedback from KelseyCreekSoftware * add conversion checks * add UX improvements * windows path issues * require input and output files for converters * NCD Improvements * fixed some usage edge cases with all NCD files * Fix bug in sigmffile._count_samples for NCD files * Fig bug in read_samples when using some NCD files with header & trailing bytes * Hashing * simplify implementation * add related tests * move from sigmf_hash to hashing -> API change * Docs * new docs for converters * simplify doc build procedure * rewrite general use patterns * added support table for BLUE format codes * Tests * add tests for blue (.cdif) and wav (.wav) converters * added tests for all supported BLUE types * split converter tests into separate files * allow epsilon differences due to float conversion patterns * simplify wav tests * Project * Increment to v1.6.0 * Simplified install: drop scipy optional dependency and [apps] entirely * Add utils.get_magic_bytes() for autodetection purposes
1 parent ac7669c commit bbb066b

25 files changed

+2054
-234
lines changed

.github/workflows/main.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ jobs:
2222
- name: Install dependencies
2323
run: |
2424
python -m pip install --upgrade pip
25-
pip install .[test,apps]
25+
pip install .[test]
2626
- name: Test with pytest
2727
run: |
2828
coverage run

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ __pycache__/
33
*.swp
44
*.py[cod]
55
.cache
6+
.vscode
67

78
# packaging related
89
dist/

.readthedocs.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ python:
1818
path: .
1919
extra_requirements:
2020
- test
21-
- apps
2221
- requirements: docs/requirements.txt
2322

2423
# Build documentation in the "docs/" directory with Sphinx

README.md

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,28 @@ freely under the terms GNU Lesser GPL v3 License.
1212

1313
This module follows the SigMF specification [html](https://sigmf.org/)/[pdf](https://sigmf.github.io/SigMF/sigmf-spec.pdf) from the [spec repository](https://github.com/sigmf/SigMF).
1414

15-
To install the latest PyPI release, install from pip:
15+
### Install
1616

1717
```bash
1818
pip install sigmf
1919
```
2020

21-
**[Please visit the documentation for examples & more info.](https://sigmf.readthedocs.io/en/latest/)**
21+
### Read SigMF
22+
23+
```python
24+
import sigmf
25+
26+
# read SigMF recording
27+
meta = sigmf.fromfile("recording.sigmf-meta")
28+
samples = meta[0:1024] # get first 1024 samples
29+
sample_rate = meta.sample_rate # get sample rate
30+
31+
32+
# read other formats containing RF time series as SigMF
33+
meta = sigmf.fromfile("recording.wav") # WAV
34+
meta = sigmf.fromfile("recording.cdif") # BLUE / Platinum
35+
```
36+
37+
### Docs
38+
39+
**[Please visit our documentation for full API reference and more info.](https://sigmf.readthedocs.io/en/latest/)**

docs/Makefile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@ BUILDDIR = build
1212
help:
1313
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
1414

15-
.PHONY: help Makefile
15+
clean:
16+
rm -rf "$(BUILDDIR)"
17+
rm -rf "$(SOURCEDIR)/_autosummary"
18+
19+
.PHONY: help clean Makefile
1620

1721
# Catch-all target: route all unknown targets to Sphinx using the new
1822
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).

docs/source/api.rst

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,13 @@ SigMF API
77
:template: custom-module-template.rst
88
:recursive:
99

10-
sigmf.apps.convert_wav
1110
sigmf.archive
1211
sigmf.archivereader
12+
sigmf.convert.blue
13+
sigmf.convert.wav
1314
sigmf.error
1415
sigmf.schema
15-
sigmf.sigmf_hash
16+
sigmf.hashing
1617
sigmf.sigmffile
1718
sigmf.utils
1819
sigmf.validate

docs/source/converters.rst

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
==========
2+
Converters
3+
==========
4+
5+
The SigMF Python library includes converters to import data from various RF recording formats into SigMF.
6+
Converters can create standard SigMF file pairs or Non-Conforming Datasets (NCDs) that reference the original files.
7+
8+
Overview
9+
--------
10+
11+
Conversion is available for:
12+
13+
* **BLUE files** - MIDAS Blue and Platinum BLUE RF recordings (usually ``.cdif``)
14+
* **WAV files** - Audio recordings (``.wav``)
15+
16+
All converters return a :class:`~sigmf.SigMFFile` object with converted metadata.
17+
18+
19+
Fromfile Auto-Detection
20+
~~~~~~~~~~~~~~~~~~~~~~~
21+
22+
The :func:`~sigmf.sigmffile.fromfile` function automatically detects input file
23+
formats and reads without writing any output files:
24+
25+
.. code-block:: python
26+
27+
import sigmf
28+
29+
# auto-detect and create NCD for any supported format
30+
meta = sigmf.fromfile("recording.cdif") # BLUE file
31+
meta = sigmf.fromfile("recording.wav") # WAV file
32+
meta = sigmf.fromfile("recording.sigmf") # SigMF archive
33+
34+
all_samples = meta.read_samples()
35+
sample_rate = meta.sample_rate
36+
37+
38+
Python API
39+
~~~~~~~~~~~
40+
41+
For programmatic access, use the individual converter functions directly:
42+
43+
.. code-block:: python
44+
45+
from sigmf.convert.wav import wav_to_sigmf
46+
from sigmf.convert.blue import blue_to_sigmf
47+
48+
# convert WAV to SigMF archive
49+
_ = wav_to_sigmf(wav_path="recording.wav", out_path="recording", create_archive=True)
50+
51+
# convert BLUE to SigMF pair and return metadata for new files
52+
meta = blue_to_sigmf(blue_path="recording.cdif", out_path="recording")
53+
54+
55+
Command Line Usage
56+
~~~~~~~~~~~~~~~~~~
57+
58+
Converters are accessed through a unified command-line interface that automatically detects file formats:
59+
60+
.. code-block:: bash
61+
62+
# unified converter
63+
sigmf_convert input_file output_file
64+
65+
# examples
66+
sigmf_convert recording.cdif recording.sigmf
67+
sigmf_convert recording.wav recording.sigmf
68+
69+
The converter uses magic byte detection to automatically identify BLUE and WAV file formats.
70+
No need to remember format-specific commands!
71+
72+
73+
Output Options
74+
~~~~~~~~~~~~~~
75+
76+
The unified converter supports multiple output modes:
77+
78+
.. code-block:: bash
79+
80+
# standard conversion (creates out.sigmf-data and out.sigmf-meta files)
81+
sigmf_convert in.wav out
82+
83+
# archive mode (creates single out.sigmf archive)
84+
sigmf_convert in.wav out --archive
85+
86+
# non-conforming dataset (creates out.sigmf-meta only, references original file)
87+
sigmf_convert in.wav out --ncd
88+
89+
# extra verbose output
90+
sigmf_convert in.wav out -vv
91+
92+
**Important**: When using ``--ncd``, the input and output files must be in the same directory.
93+
This ensures proper relative path references in the metadata.
94+
95+
96+
BLUE Converter
97+
--------------
98+
99+
The BLUE converter handles CDIF (.cdif) recordings while placing BLUE header information into the following global fields:
100+
101+
* ``blue:fixed`` - Fixed header information (at start of file).
102+
* ``blue:adjunct`` - Adjunct header information (after fixed header).
103+
* ``blue:extended`` - Extended header information (at end of file). Note any duplicate fields will have a suffix like ``_1``, ``_2``, etc appended.
104+
105+
.. autofunction:: sigmf.convert.blue.blue_to_sigmf
106+
107+
Examples
108+
~~~~~~~~
109+
110+
.. code-block:: python
111+
112+
from sigmf.convert.blue import blue_to_sigmf
113+
114+
# standard conversion
115+
meta = blue_to_sigmf(blue_path="recording.cdif", out_path="recording")
116+
117+
# create NCD automatically (metadata-only, references original file) but don't save any output file
118+
meta = blue_to_sigmf(blue_path="recording.cdif")
119+
120+
# access standard SigMF data & metadata
121+
all_samples = meta.read_samples()
122+
sample_rate = meta.sample_rate
123+
124+
# access BLUE-specific metadata
125+
blue_type = meta.get_global_field("blue:fixed")["type"] # e.g., 1000
126+
blue_version = meta.get_global_field("blue:fixed")["keywords"]["IO"] # e.g., "X-Midas"
127+
128+
Tested Formats
129+
~~~~~~~~~~~~~~
130+
131+
BLUE files use a 2-digit format code where the first is the sample type (row) and the second is the sample size (column).
132+
For example ``SB`` contains real values with 8 bits per sample and ``CF`` contains complex values with 32 bits per component (64 bits per sample).
133+
134+
The following table summarizes tested BLUE formats and their compatibility with the converter:
135+
136+
.. csv-table::
137+
:header-rows: 1
138+
:stub-columns: 1
139+
140+
"Code", ":abbr:`B (int8)`", ":abbr:`I (int16)`", ":abbr:`L (int32)`", ":abbr:`X (int64)`", ":abbr:`F (float32)`", ":abbr:`D (float64)`", ":abbr:`P (packed)`", ":abbr:`N (int4)`"
141+
":abbr:`S (scalar)`", "✅", "✅", "✅", "✅", "✅", "✅", "❌", "❌"
142+
":abbr:`C (complex)`", "✅", "✅", "✅", "✅", "✅", ""✅", "❌", "❌"
143+
144+
**Legend:**
145+
* ✅ = Tested and known working
146+
* ❌ = Unsupported
147+
148+
149+
WAV Converter
150+
-------------
151+
152+
Converts WAV audio recordings to SigMF format.
153+
154+
.. autofunction:: sigmf.convert.wav.wav_to_sigmf
155+
156+
Examples
157+
~~~~~~~~
158+
159+
.. code-block:: python
160+
161+
from sigmf.convert.wav import wav_to_sigmf
162+
163+
# standard conversion
164+
meta = wav_to_sigmf(wav_path="recording.wav", out_path="recording")
165+
166+
# create NCD automatically (metadata-only, references original file)
167+
meta = wav_to_sigmf(wav_path="recording.wav")
168+
169+
# access standard SigMF data & metadata
170+
all_samples = meta.read_samples()
171+
sample_rate_hz = meta.sample_rate

docs/source/developers.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,9 @@ To build the docs and host locally:
6060
.. code-block:: console
6161
6262
$ cd docs
63+
$ make clean
6364
$ make html
64-
$ cd build/html/
65-
$ python3 -m http.server
65+
$ python3 -m http.server --directory build/html/
6666
6767
--------------
6868
Find an Issue?

docs/source/index.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@ It offers a *simple* and *intuitive* API for Python developers.
88

99
..
1010
Note: The toolversion & specversion below are replaced dynamically during build.
11+
The root __init__.py file is used as the sole source of truth for these values.
1112
1213
This documentation is for version |toolversion| of the library, which is
1314
compatible with version |specversion| of the SigMF specification.
1415

15-
To get started, see the :doc:`quickstart` section or learn how to :ref:`install` the library.
16+
To get started, see `quickstart`.
1617

1718
-----
1819

@@ -23,6 +24,7 @@ To get started, see the :doc:`quickstart` section or learn how to :ref:`install`
2324

2425
quickstart
2526
advanced
27+
converters
2628
developers
2729

2830
.. toctree::

pyproject.toml

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,14 @@ dependencies = [
3333

3434
[project.scripts]
3535
sigmf_validate = "sigmf.validate:main"
36-
sigmf_convert_wav = "sigmf.apps.convert_wav:main [apps]"
36+
sigmf_convert = "sigmf.convert.__main__:main"
3737
[project.optional-dependencies]
3838
test = [
3939
"pylint",
4040
"pytest",
4141
"pytest-cov",
4242
"hypothesis", # next-gen testing framework
4343
]
44-
apps = [
45-
"scipy", # for wav i/o
46-
]
4744

4845
[tool.setuptools]
4946
packages = ["sigmf"]
@@ -106,6 +103,6 @@ legacy_tox_ini = '''
106103
107104
[testenv]
108105
usedevelop = True
109-
deps = .[test,apps]
106+
deps = .[test]
110107
commands = coverage run
111108
'''

0 commit comments

Comments
 (0)