Quickstart
Here we discuss how to do all basic operations with SigMF.
Install
To install from PyPI using pip:
$ pip install sigmf
To install using conda or mamba (available via Miniforge):
$ conda install sigmf # or
$ mamba install sigmf
Read a SigMF Recording
import sigmf
handle = sigmf.fromfile("example.sigmf")
# reading data
handle.read_samples() # read all timeseries data
handle[10:50] # read memory slice of samples 10 through 50
# accessing metadata
handle.sample_rate # get sample rate attribute
handle.get_global_info() # returns 'global' dictionary
handle.get_captures() # returns list of 'captures' dictionaries
handle.get_annotations() # returns list of all annotations
Verify SigMF Integrity & Compliance
$ sigmf_validate example.sigmf
Save a Numpy array as a SigMF Recording
import numpy as np
import sigmf
# suppose we have a complex timeseries signal
data = np.zeros(1024, dtype=np.complex64)
# create SigMFFile from array — datatype is inferred from the numpy array
meta = sigmf.fromarray(data)
# optional additional metadata
meta.sample_rate = 48000
meta.description = "an example recording"
meta.add_capture(start_index=0, metadata={sigmf.FREQUENCY_KEY: 915e6})
# write to separate .sigmf-meta and .sigmf-data files
meta.tofile("example")
# or write to a SigMF archive (example.sigmf)
meta.tofile("example.sigmf")
# or write to a compressed archive (example.sigmf.xz)
meta.tofile("example.sigmf.xz")
The SigMFFile object can be modified before writing to add additional
captures, annotations, or global metadata fields.
Save a Numpy array with Full Metadata (Advanced)
For full control over global fields, captures, and annotations:
import numpy as np
from sigmf import SigMFFile
from sigmf.utils import get_data_type_str, get_sigmf_iso8601_datetime_now
# suppose we have a complex timeseries signal
data = np.zeros(1024, dtype=np.complex64)
# write those samples to file in cf32_le
data.tofile("example.sigmf-data")
# create the metadata
meta = SigMFFile(
data_file="example.sigmf-data", # extension is optional
global_info={
sigmf.DATATYPE_KEY: get_data_type_str(data), # in this case, "cf32_le"
sigmf.SAMPLE_RATE_KEY: 48000,
sigmf.AUTHOR_KEY: "jane.doe@domain.org",
sigmf.DESCRIPTION_KEY: "All zero complex float32 example file.",
},
)
# create a capture key at time index 0
meta.add_capture(
0,
metadata={
sigmf.FREQUENCY_KEY: 915000000,
sigmf.DATETIME_KEY: get_sigmf_iso8601_datetime_now(),
},
)
# add an annotation at sample 100 with length 200 & 10 KHz width
meta.add_annotation(
100,
200,
metadata={
sigmf.FREQ_LOWER_EDGE_KEY: 914995000.0,
sigmf.FREQ_UPPER_EDGE_KEY: 915005000.0,
sigmf.COMMENT_KEY: "example annotation",
},
)
# validate & write to disk
meta.tofile("example.sigmf-meta") # extension is optional
Attribute Access for Global Fields
SigMF-Python provides convenient attribute read/write access for core global metadata fields, allowing you use simple dot notation alongside the traditional method-based approach.
import sigmf
# read some recording
meta = sigmf.SigMFFile("sigmf_logo")
# read global metadata
print(f"Sample rate: {meta.sample_rate}")
print(f"License: {meta.license}")
# set global metadata
meta.description = "Updated description via attribute access"
meta.author = "jane.doe@domain.org"
# validate & write changes to disk
meta.tofile("sigmf_logo_updated")
Note
Only core global fields support attribute access. Capture and annotation
fields must still be accessed using the traditional get_captures() and
get_annotations() methods.
Control Fixed-Point Data Scaling
For fixed-point datasets, you can control whether samples are automatically scaled to floating-point values:
import sigmf
# Default behavior: autoscale fixed-point data to [-1.0, 1.0] range
handle = sigmf.fromfile("fixed_point_data.sigmf")
samples = handle.read_samples() # Returns float32/complex64
# Disable autoscaling to access raw integer values
handle_raw = sigmf.fromfile("fixed_point_data.sigmf", autoscale=False)
raw_samples = handle_raw.read_samples() # Returns original integer types
# Both slicing and read_samples() respect the autoscale setting
assert handle[0:10].dtype == handle.read_samples(count=10).dtype