Skip to content

Commit dcd8d7a

Browse files
committed
rewrite docs & improve sigmf_convert entry point
1 parent d212c41 commit dcd8d7a

5 files changed

Lines changed: 144 additions & 103 deletions

File tree

docs/source/converters.rst

Lines changed: 61 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
1-
=================
2-
Format Converters
3-
=================
1+
==========
2+
Converters
3+
==========
44

5-
The SigMF Python library includes converters to import data from various file formats into SigMF format.
5+
The SigMF Python library includes converters to import data from various RF recording formats into SigMF.
66
Converters can create standard SigMF file pairs or Non-Conforming Datasets (NCDs) that reference the original files.
77

88
Overview
99
--------
1010

11-
Converters are available for:
11+
Conversion is available for:
1212

13-
* **BLUE files** - MIDAS Blue and Platinum BLUE RF recordings (``.cdif``)
13+
* **BLUE files** - MIDAS Blue and Platinum BLUE RF recordings (usually ``.cdif``)
1414
* **WAV files** - Audio recordings (``.wav``)
1515

16-
All converters return a :class:`~sigmf.SigMFFile` object. Auto-detection is available through :func:`~sigmf.sigmffile.fromfile`.
16+
All converters return a :class:`~sigmf.SigMFFile` object with converted metadata.
1717

1818

19-
Auto-Detection
20-
~~~~~~~~~~~~~~
19+
Fromfile Auto-Detection
20+
~~~~~~~~~~~~~~~~~~~~~~~
2121

2222
The :func:`~sigmf.sigmffile.fromfile` function automatically detects file formats and creates Non-Conforming Datasets:
2323

@@ -30,35 +30,66 @@ The :func:`~sigmf.sigmffile.fromfile` function automatically detects file format
3030
meta = sigmf.fromfile("recording.wav") # WAV file
3131
meta = sigmf.fromfile("recording.sigmf") # SigMF archive
3232
33-
samples = meta.read_samples()
33+
all_samples = meta.read_samples()
34+
sample_rate = meta.sample_rate
35+
36+
37+
Python API
38+
~~~~~~~~~~~
39+
40+
For programmatic access, use the individual converter functions directly:
41+
42+
.. code-block:: python
43+
44+
from sigmf.convert.wav import wav_to_sigmf
45+
from sigmf.convert.blue import blue_to_sigmf
46+
47+
# convert WAV to SigMF archive
48+
_ = wav_to_sigmf(wav_path="recording.wav", out_path="recording", create_archive=True)
49+
50+
# convert BLUE to SigMF pair and return metadata for new files
51+
meta = blue_to_sigmf(blue_path="recording.cdif", out_path="recording")
3452
3553
3654
Command Line Usage
3755
~~~~~~~~~~~~~~~~~~
3856

39-
Converters can be used from the command line:
57+
Converters are accessed through a unified command-line interface that automatically detects file formats:
4058

4159
.. code-block:: bash
4260
43-
sigmf_convert_blue recording.cdif
44-
sigmf_convert_wav recording.wav
45-
46-
or by using module execution:
61+
# unified converter
62+
sigmf_convert input_file output_file
4763
48-
.. code-block:: bash
64+
# examples
65+
sigmf_convert recording.cdif recording.sigmf
66+
sigmf_convert recording.wav recording.sigmf
4967
50-
python -m sigmf.convert.blue recording.cdif
51-
python -m sigmf.convert.wav recording.wav
68+
The converter uses magic byte detection to automatically identify BLUE and WAV file formats.
69+
No need to remember format-specific commands!
5270

5371

5472
Output Options
5573
~~~~~~~~~~~~~~
5674

57-
Converters support multiple output modes:
75+
The unified converter supports multiple output modes:
76+
77+
.. code-block:: bash
78+
79+
# standard conversion (creates out.sigmf-data and out.sigmf-meta files)
80+
sigmf_convert in.wav out
5881
59-
* **Standard conversion**: Creates ``.sigmf-data`` and ``.sigmf-meta`` files
60-
* **Archive mode**: Creates single ``.sigmf`` archive with ``--archive``
61-
* **Non-Conforming Dataset**: Creates metadata-only file referencing original data with ``--ncd``
82+
# archive mode (creates single out.sigmf archive)
83+
sigmf_convert in.wav out --archive
84+
85+
# non-conforming dataset (creates out.sigmf-meta only, references original file)
86+
sigmf_convert in.wav out --ncd
87+
88+
# extra verbose output
89+
sigmf_convert in.wav out -vv
90+
91+
**Important**: When using ``--ncd``, the input and output files must be in the same directory.
92+
This ensures proper relative path references in the metadata.
6293

6394

6495
BLUE Converter
@@ -73,19 +104,22 @@ The BLUE converter handles CDIF (.cdif) recordings while placing BLUE header inf
73104

74105
.. autofunction:: sigmf.convert.blue.blue_to_sigmf
75106

107+
Examples
108+
~~~~~~~~
109+
76110
.. code-block:: python
77111
78112
from sigmf.convert.blue import blue_to_sigmf
79113
80114
# standard conversion
81115
meta = blue_to_sigmf(blue_path="recording.cdif", out_path="recording")
82116
83-
# create NCD automatically (metadata-only, references original file)
117+
# create NCD automatically (metadata-only, references original file) but don't save any output file
84118
meta = blue_to_sigmf(blue_path="recording.cdif")
85119
86120
# access standard SigMF data & metadata
87121
all_samples = meta.read_samples()
88-
sample_rate_hz = meta.sample_rate
122+
sample_rate = meta.sample_rate
89123
90124
# access BLUE-specific metadata
91125
blue_type = meta.get_global_field("blue:fixed")["type"] # e.g., 1000
@@ -99,6 +133,9 @@ Converts WAV audio recordings to SigMF format.
99133

100134
.. autofunction:: sigmf.convert.wav.wav_to_sigmf
101135

136+
Examples
137+
~~~~~~~~
138+
102139
.. code-block:: python
103140
104141
from sigmf.convert.wav import wav_to_sigmf

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ dependencies = [
3333

3434
[project.scripts]
3535
sigmf_validate = "sigmf.validate:main"
36-
sigmf_convert = "sigmf.convert:main"
36+
sigmf_convert = "sigmf.convert.__main__:main"
3737
[project.optional-dependencies]
3838
test = [
3939
"pylint",

sigmf/convert/__init__.py

Lines changed: 0 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +0,0 @@
1-
# Copyright: Multiple Authors
2-
#
3-
# This file is part of sigmf-python. https://github.com/sigmf/sigmf-python
4-
#
5-
# SPDX-License-Identifier: LGPL-3.0-or-later
6-
7-
"""Unified converter for non-SigMF file formats"""
8-
9-
import argparse
10-
import logging
11-
from pathlib import Path
12-
13-
from .. import __version__ as toolversion
14-
from ..error import SigMFConversionError
15-
from ..utils import get_magic_bytes
16-
17-
18-
def main() -> None:
19-
"""
20-
Unified entry-point for SigMF conversion of non-SigMF files.
21-
"""
22-
parser = argparse.ArgumentParser(description="Convert non-SigMF files to SigMF format")
23-
parser.add_argument("input", type=str, help="Input file path")
24-
parser.add_argument("output", type=str, help="Output SigMF path (no extension)")
25-
parser.add_argument("-v", "--verbose", action="count", default=0)
26-
parser.add_argument(
27-
"-a", "--archive", action="store_true", help="Save as SigMF archive instead of separate meta/data files."
28-
)
29-
parser.add_argument(
30-
"--ncd", action="store_true", help="Process as Non-Conforming Dataset and write .sigmf-meta only."
31-
)
32-
parser.add_argument("--version", action="version", version=f"%(prog)s v{toolversion}")
33-
args = parser.parse_args()
34-
35-
level_lut = {
36-
0: logging.WARNING,
37-
1: logging.INFO,
38-
2: logging.DEBUG,
39-
}
40-
logging.basicConfig(level=level_lut[min(args.verbose, 2)])
41-
42-
input_path = Path(args.input)
43-
output_path = Path(args.output)
44-
45-
# validate that ncd files are in same directory as input
46-
if args.ncd and input_path.parent.resolve() != output_path.parent.resolve():
47-
raise SigMFConversionError(
48-
f"NCD files must be in the same directory as input file. "
49-
f"Input: {input_path.parent.resolve()}, Output: {output_path.parent.resolve()}"
50-
)
51-
52-
# detect file type using magic bytes (same logic as fromfile())
53-
magic_bytes = get_magic_bytes(input_path, count=4, offset=0)
54-
55-
if magic_bytes == b"RIFF":
56-
# WAV file
57-
from .wav import wav_to_sigmf
58-
59-
_ = wav_to_sigmf(wav_path=input_path, out_path=output_path, create_archive=args.archive, create_ncd=args.ncd)
60-
61-
elif magic_bytes == b"BLUE":
62-
# BLUE file
63-
from .blue import blue_to_sigmf
64-
65-
_ = blue_to_sigmf(blue_path=input_path, out_path=output_path, create_archive=args.archive, create_ncd=args.ncd)
66-
67-
else:
68-
raise SigMFConversionError(
69-
f"Unsupported file format. Magic bytes: {magic_bytes}. "
70-
f"Supported formats for conversion are WAV and BLUE/Platinum."
71-
)
72-
73-
74-
if __name__ == "__main__":
75-
main()

sigmf/convert/__main__.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# Copyright: Multiple Authors
2+
#
3+
# This file is part of sigmf-python. https://github.com/sigmf/sigmf-python
4+
#
5+
# SPDX-License-Identifier: LGPL-3.0-or-later
6+
7+
"""Unified converter for non-SigMF file formats"""
8+
9+
import argparse
10+
import logging
11+
import textwrap
12+
from pathlib import Path
13+
14+
from .. import __version__ as toolversion
15+
from ..error import SigMFConversionError
16+
from ..utils import get_magic_bytes
17+
from .blue import blue_to_sigmf
18+
from .wav import wav_to_sigmf
19+
20+
21+
def main() -> None:
22+
"""
23+
Unified entry-point for SigMF conversion of non-SigMF recordings.
24+
25+
This command-line interface converts various non-SigMF file formats into SigMF-compliant datasets.
26+
It currently supports WAV and BLUE/Platinum file formats.
27+
The converter detects the file type based on magic bytes and invokes the appropriate conversion function.
28+
29+
By default it will output a SigMF pair (.sigmf-meta and .sigmf-data).
30+
"""
31+
parser = argparse.ArgumentParser(
32+
description=textwrap.dedent(main.__doc__),
33+
formatter_class=argparse.RawDescriptionHelpFormatter,
34+
)
35+
parser.add_argument("input", type=str, help="Input recording path")
36+
parser.add_argument("output", type=str, help="Output SigMF path (no extension)")
37+
parser.add_argument("-v", "--verbose", action="count", default=0, help="Increase verbosity level")
38+
parser.add_argument("-a", "--archive", action="store_true", help="Output .sigmf archive only")
39+
parser.add_argument(
40+
"--ncd", action="store_true", help="Output .sigmf-meta only and process as a Non-Conforming Dataset (NCD)"
41+
)
42+
parser.add_argument("--version", action="version", version=f"%(prog)s v{toolversion}")
43+
args = parser.parse_args()
44+
45+
level_lut = {
46+
0: logging.WARNING,
47+
1: logging.INFO,
48+
2: logging.DEBUG,
49+
}
50+
logging.basicConfig(level=level_lut[min(args.verbose, 2)])
51+
52+
input_path = Path(args.input)
53+
output_path = Path(args.output)
54+
55+
# validate that ncd files are in same directory as input
56+
if args.ncd and input_path.parent.resolve() != output_path.parent.resolve():
57+
raise SigMFConversionError(
58+
f"NCD files must be in the same directory as input file. "
59+
f"Input: {input_path.parent.resolve()}, Output: {output_path.parent.resolve()}"
60+
)
61+
62+
# detect file type using magic bytes (same logic as fromfile())
63+
magic_bytes = get_magic_bytes(input_path, count=4, offset=0)
64+
65+
if magic_bytes == b"RIFF":
66+
# WAV file
67+
_ = wav_to_sigmf(wav_path=input_path, out_path=output_path, create_archive=args.archive, create_ncd=args.ncd)
68+
69+
elif magic_bytes == b"BLUE":
70+
# BLUE file
71+
_ = blue_to_sigmf(blue_path=input_path, out_path=output_path, create_archive=args.archive, create_ncd=args.ncd)
72+
73+
else:
74+
raise SigMFConversionError(
75+
f"Unsupported file format. Magic bytes: {magic_bytes}. "
76+
f"Supported formats for conversion are WAV and BLUE/Platinum."
77+
)
78+
79+
80+
if __name__ == "__main__":
81+
main()

sigmf/convert/blue.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,9 @@
2424
import numpy as np
2525
from packaging.version import InvalidVersion, Version
2626

27-
from .. import SigMFFile
2827
from .. import __version__ as toolversion
29-
from .. import fromfile
3028
from ..error import SigMFConversionError
31-
from ..sigmffile import get_sigmf_filenames
29+
from ..sigmffile import SigMFFile, fromfile, get_sigmf_filenames
3230
from ..utils import SIGMF_DATETIME_ISO8601_FMT
3331

3432
log = logging.getLogger()

0 commit comments

Comments
 (0)