Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
cff5af6
Initial commit of prototype converter for Rohde and Schwarz IQ.TAR fi…
KelseyCreekSoftware Apr 4, 2026
3ad18dc
Working on Codacy security code issues.
KelseyCreekSoftware Apr 4, 2026
09fd807
Adding mainly AI created pytest that seems to work.
KelseyCreekSoftware Apr 5, 2026
84a6d18
Updating byte offset for MagicBytes function.
KelseyCreekSoftware May 14, 2026
d85c82e
Adding additional flexibility for R&S XML - extend get_magic_bytes wi…
KelseyCreekSoftware May 17, 2026
2300d7d
Adding support for Python 3.9 with use of Optional.
KelseyCreekSoftware May 18, 2026
750c38e
Minor updates- need to merge in main to catch up on latest sigmf work
KelseyCreekSoftware Jun 3, 2026
6d2977f
Minor updates- need to merge in main to catch up on latest sigmf work
KelseyCreekSoftware Jun 3, 2026
bd190c0
Merge branch 'main' into feature/rohdeschwarz
KelseyCreekSoftware Jun 3, 2026
18c2209
Working on R&S converter fixes and updates.
KelseyCreekSoftware Jun 3, 2026
8bb2a50
Starting work on Co-Pilot code review. More to do here!
KelseyCreekSoftware Jun 4, 2026
c52499c
Ignore .venv environment file
KelseyCreekSoftware Jun 15, 2026
4557098
Fixing meta.to file signature to add overwrite.
KelseyCreekSoftware Jun 15, 2026
8082165
Additional work on Co-Pilot code review. One more fix to do.
KelseyCreekSoftware Jun 16, 2026
9e90017
Completed all Co-pilot review items, and then used Claude Haiku 4.5 t…
KelseyCreekSoftware Jun 16, 2026
6501d8c
Progress check-in for R&S converter and updates to associated test code.
KelseyCreekSoftware Jun 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@ docs/source/_autosummary/

# dev related
.vscode/
.venv/
35 changes: 32 additions & 3 deletions sigmf/convert/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@
"""Convert non-SigMF recordings to SigMF format"""

from pathlib import Path
from typing import Optional

from ..error import SigMFConversionError


def get_magic_bytes(file_path: Path, count: int = 4, offset: int = 0) -> bytes:
def get_magic_bytes(file_path: Path, count: int = 4, offset: int = 0, magic_bytes: Optional[bytes] = None) -> bytes:
"""
Get magic bytes from a file to help identify file type.

Expand All @@ -23,6 +24,8 @@ def get_magic_bytes(file_path: Path, count: int = 4, offset: int = 0) -> bytes:
Number of bytes to read. Default is 4.
offset : int, optional
Byte offset to start reading from. Default is 0.
magic_bytes : bytes, optional
If provided, search the entire file for this byte sequence.

Returns
-------
Expand All @@ -36,16 +39,30 @@ def get_magic_bytes(file_path: Path, count: int = 4, offset: int = 0) -> bytes:
"""
try:
with open(file_path, "rb") as handle:
# If magic_bytes is provided, search anywhere in the file
if magic_bytes is not None:
data = handle.read()
idx = data.find(magic_bytes)
if idx != -1:
return magic_bytes
raise SigMFConversionError(
f"Magic bytes {magic_bytes} not found anywhere in {file_path}"
)
Comment on lines 41 to +50

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There may be more work to do here, but not sure the recommended solution is worth the additional code.
Once everything is basically working, it would be good to do a code pass for efficiency. Having very large files, of all types, to test would also be good to make sure the magic bytes are locatable.


# Otherwise: read bytes at the given offset
handle.seek(offset)
magic_bytes = handle.read(count)
if len(magic_bytes) < count:
raise SigMFConversionError(f"File {file_path} too small to read {count} magic bytes at offset {offset}")
raise SigMFConversionError(
f"File {file_path} too small to read {count} bytes at offset {offset}"
)
return magic_bytes

except (IOError, OSError) as err:
raise SigMFConversionError(f"Cannot read magic bytes from {file_path}: {err}") from err


def detect_converter(file_path: Path):
def detect_converter(file_path: Path) -> str:
"""
Detect the appropriate converter for a non-SigMF file.

Expand Down Expand Up @@ -84,6 +101,18 @@ def detect_converter(file_path: Path):
f"Expected SignalHoundIQFile for Signal Hound Spike files."
)

elif file_path.suffix in [".tar"]:
# iq.tar file extensions are used by Rohde & Schwarz for their IQ data, but the .tar extension is also used by other formats.
# So parse the tar file to determine if it is a Rohde & Schwarz file or not.
rohde_schwarz_expanded_magic_bytes = get_magic_bytes(file_path, count=20, offset=0, magic_bytes=b"RS_IQ_TAR_FileFormat") # <RS_IQ_TAR_FileFormat>
if rohde_schwarz_expanded_magic_bytes == b"RS_IQ_TAR_FileFormat":
return "rohdeschwarz"
else:
raise SigMFConversionError(
f"Unsupported XML file format in tar file. Root element: {rohde_schwarz_expanded_magic_bytes}. "
f"Expected RS_IQ_TAR_FileFormat for IQ.TAR files."
)

Comment on lines +104 to +115
else:
raise SigMFConversionError(
f"Unsupported file format. Magic bytes: {magic_bytes}. "
Expand Down
13 changes: 6 additions & 7 deletions sigmf/convert/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,17 @@
from .. import __version__ as toolversion
from ..error import SigMFConversionError
from . import detect_converter
from .wav import wav_to_sigmf
from .blue import blue_to_sigmf
from .signalhound import signalhound_to_sigmf
from .wav import wav_to_sigmf

from .rohdeschwarz import rohdeschwarz_to_sigmf
Comment on lines 16 to +20

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.


def main() -> None:
"""
Unified entry-point for SigMF conversion of non-SigMF recordings.

This command-line interface converts various non-SigMF file formats into SigMF-compliant datasets.
It currently supports WAV and BLUE/Platinum file formats.
It currently supports WAV and BLUE/Platinum, Signal Hound Spike and Rohde and Schwarz IQ.TAR file formats.
The converter detects the file type based on magic bytes and invokes the appropriate conversion function.

By default it will output a SigMF pair (.sigmf-meta and .sigmf-data).
Expand Down Expand Up @@ -94,10 +94,9 @@ def main() -> None:
elif converter_type == "blue":
_ = blue_to_sigmf(blue_path=input_path, out_path=output_path, create_archive=args.archive, create_ncd=args.ncd)
elif converter_type == "signalhound":
_ = signalhound_to_sigmf(
signalhound_path=input_path, out_path=output_path, create_archive=args.archive, create_ncd=args.ncd
)

_ = signalhound_to_sigmf(signalhound_path=input_path, out_path=output_path, create_archive=args.archive, create_ncd=args.ncd)
elif converter_type == "rohdeschwarz":
_ = rohdeschwarz_to_sigmf(rohdeschwarz_path=input_path, out_path=output_path, create_archive=args.archive, create_ncd=args.ncd)

if __name__ == "__main__":
main()
Loading
Loading