Skip to content

Commit

Permalink
Track Specification within Repo & Polish (#57)
Browse files Browse the repository at this point in the history
* Add new sigmf.__specification__ variable that tracks SigMF Spec release version
* Fix logo visible in PyPi (only png works there, although svg does render on GitHub)
* Replace outdated links from gnuradio to sigmf organization.
* Adjust sigmf_convert_wav so that it reads xyz.wav and produces a simple xyz.sigmf. Also basic polish of implementation
* Make license statement same across all files.
  • Loading branch information
Teque5 committed Apr 15, 2024
1 parent 1b03f63 commit ccc1cbf
Show file tree
Hide file tree
Showing 22 changed files with 262 additions and 254 deletions.
4 changes: 2 additions & 2 deletions README.md
@@ -1,11 +1,11 @@
<p align="center"><img src="https://github.com/gnuradio/SigMF/blob/sigmf-v1.x/logo/sigmf_logo.svg" alt="Rendered SigMF Logo"/></p>
<p align="center"><img src="https://github.com/sigmf/SigMF/blob/v1.2.0/logo/sigmf_logo.png" alt="Rendered SigMF Logo"/></p>

This python module makes it easy to interact with Signal Metadata Format
(SigMF) recordings. This module works with Python 3.7+ and is distributed
freely under the terms GNU Lesser GPL v3 License.

The [SigMF specification document](https://github.com/sigmf/SigMF/blob/HEAD/sigmf-spec.md)
is located in the [SigMF](https://github.com/gnuradio/SigMF) repository.
is located in the [SigMF](https://github.com/sigmf/SigMF) repository.

# Installation

Expand Down
4 changes: 3 additions & 1 deletion pyproject.toml
@@ -1,7 +1,7 @@
[project]
name = "SigMF"
description = "Easily interact with Signal Metadata Format (SigMF) recordings."
keywords = ["gnuradio"]
keywords = ["gnuradio", "radio"]
classifiers = [
"Development Status :: 5 - Production/Stable",
"License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)",
Expand All @@ -13,6 +13,8 @@ classifiers = [
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Topic :: Scientific/Engineering",
"Topic :: Communications :: Ham Radio",
]
dynamic = ["version", "readme"]
requires-python = ">=3.7"
Expand Down
11 changes: 7 additions & 4 deletions sigmf/__init__.py
@@ -1,19 +1,22 @@
# Copyright: Multiple Authors
#
# This file is part of SigMF. https://github.com/sigmf/sigmf-python
# This file is part of sigmf-python. https://github.com/sigmf/sigmf-python
#
# SPDX-License-Identifier: LGPL-3.0-or-later

__version__ = "1.2.0"
# version of this python module
__version__ = "1.2.1"
# matching version of the SigMF specification
__specification__ = "1.2.0"

from .archive import SigMFArchive
from .sigmffile import SigMFFile, SigMFCollection
from .archivereader import SigMFArchiveReader

from . import archive
from . import archivereader
from . import error
from . import schema
from . import sigmffile
from . import validate
from . import utils
from . import archivereader
from . import validate
37 changes: 26 additions & 11 deletions sigmf/apps/convert_wav.py
@@ -1,29 +1,35 @@
# Copyright: Multiple Authors
#
# This file is part of SigMF. https://github.com/sigmf/sigmf-python
# This file is part of sigmf-python. https://github.com/sigmf/sigmf-python
#
# SPDX-License-Identifier: LGPL-3.0-or-later

"""converter for wav containers"""

import os
import tempfile
import datetime
import pathlib
import argparse
import datetime
import getpass
import logging
import os
import pathlib
import tempfile

from scipy.io import wavfile

from .. import SigMFFile, __specification__
from .. import __version__ as toolversion
from .. import archive
from ..sigmffile import SigMFFile
from ..utils import get_data_type_str

log = logging.getLogger()


def convert_wav(input_wav_filename, archive_filename=None, start_datetime=None, author=None):
"""
read a .wav and write a .sigmf archive
"""
input_path = pathlib.Path(input_wav_filename)
input_stem = input_path.stem
samp_rate, wav_data = wavfile.read(input_wav_filename)

global_info = {
Expand All @@ -33,27 +39,27 @@ def convert_wav(input_wav_filename, archive_filename=None, start_datetime=None,
SigMFFile.NUM_CHANNELS_KEY: 1 if len(wav_data.shape) < 2 else wav_data.shape[1],
SigMFFile.RECORDER_KEY: os.path.basename(__file__),
SigMFFile.SAMPLE_RATE_KEY: samp_rate,
SigMFFile.VERSION_KEY: __specification__,
}

if start_datetime is None:
fname = pathlib.Path(input_wav_filename)
mtime = datetime.datetime.fromtimestamp(fname.stat().st_mtime)
mtime = datetime.datetime.fromtimestamp(input_path.stat().st_mtime)
start_datetime = mtime.isoformat() + "Z"

capture_info = {SigMFFile.START_INDEX_KEY: 0}
if start_datetime is not None:
capture_info[SigMFFile.DATETIME_KEY] = start_datetime

tmpdir = tempfile.mkdtemp()
sigmf_data_filename = input_wav_filename + archive.SIGMF_DATASET_EXT
sigmf_data_filename = input_stem + archive.SIGMF_DATASET_EXT
sigmf_data_path = os.path.join(tmpdir, sigmf_data_filename)
wav_data.tofile(sigmf_data_path)

meta = SigMFFile(data_file=sigmf_data_path, global_info=global_info)
meta.add_capture(0, metadata=capture_info)

if archive_filename is None:
archive_filename = os.path.basename(input_wav_filename) + archive.SIGMF_ARCHIVE_EXT
archive_filename = input_stem + archive.SIGMF_ARCHIVE_EXT
meta.tofile(archive_filename, toarchive=True)
return os.path.abspath(archive_filename)

Expand All @@ -65,13 +71,22 @@ def main():
parser = argparse.ArgumentParser(description="Convert .wav to .sigmf container.")
parser.add_argument("input", type=str, help="Wavfile path")
parser.add_argument("--author", type=str, default=None, help=f"set {SigMFFile.AUTHOR_KEY} metadata")
parser.add_argument('-v', '--verbose', action='count', default=0)
parser.add_argument('--version', action='version', version=f'%(prog)s v{toolversion}')
args = parser.parse_args()

level_lut = {
0: logging.WARNING,
1: logging.INFO,
2: logging.DEBUG,
}
logging.basicConfig(level=level_lut[min(args.verbose, 2)])

out_fname = convert_wav(
input_wav_filename=args.input,
author=args.author,
)
print("Wrote", out_fname)
log.info(f"Write {out_fname}")


if __name__ == "__main__":
Expand Down
17 changes: 10 additions & 7 deletions sigmf/apps/gui.py
@@ -1,17 +1,20 @@
# Copyright: Multiple Authors
#
# This file is part of SigMF. https://github.com/sigmf/sigmf-python
# This file is part of sigmf-python. https://github.com/sigmf/sigmf-python
#
# SPDX-License-Identifier: LGPL-3.0-or-later

'''GUI for creating & editing SigMF Files'''

import os
import argparse
import logging
import os

from PySimpleGUI import *

from ..sigmffile import SigMFFile, fromarchive, dtype_info
from .. import __version__ as toolversion
from ..archive import SIGMF_ARCHIVE_EXT
from ..sigmffile import SigMFFile, dtype_info, fromarchive

log = logging.getLogger()

Expand Down Expand Up @@ -381,13 +384,10 @@ def add_capture(capture_data_input, values, capture_selector_dict, file_data, fr


def main():
import argparse
from sigmf import __version__ as toolversion

parser = argparse.ArgumentParser(description='Edit SigMF Archive.')
parser.add_argument('-i', '--input', help='Input SigMF Archive Path.', default=None)
parser.add_argument('-v', '--verbose', action='count', default=0)
parser.add_argument('--version', action='version', version=f'%(prog)s {toolversion}')
parser.add_argument('--version', action='version', version=f'%(prog)s v{toolversion}')
args = parser.parse_args()

level_lut = {
Expand Down Expand Up @@ -638,3 +638,6 @@ def main():
break

window.Close()

if __name__ == "__main__":
main()
3 changes: 1 addition & 2 deletions sigmf/archive.py
@@ -1,6 +1,6 @@
# Copyright: Multiple Authors
#
# This file is part of SigMF. https://github.com/sigmf/sigmf-python
# This file is part of sigmf-python. https://github.com/sigmf/sigmf-python
#
# SPDX-License-Identifier: LGPL-3.0-or-later

Expand All @@ -13,7 +13,6 @@

from .error import SigMFFileError


SIGMF_ARCHIVE_EXT = ".sigmf"
SIGMF_METADATA_EXT = ".sigmf-meta"
SIGMF_DATASET_EXT = ".sigmf-data"
Expand Down
8 changes: 4 additions & 4 deletions sigmf/archivereader.py
@@ -1,6 +1,6 @@
# Copyright: Multiple Authors
#
# This file is part of SigMF. https://github.com/gnuradio/SigMF
# This file is part of sigmf-python. https://github.com/sigmf/SigMF
#
# SPDX-License-Identifier: LGPL-3.0-or-later

Expand All @@ -11,11 +11,11 @@
import tarfile
import tempfile

from . import __version__ #, schema, sigmf_hash, validate
from . import __version__
from .archive import SIGMF_ARCHIVE_EXT, SIGMF_DATASET_EXT, SIGMF_METADATA_EXT, SigMFArchive
from .error import SigMFFileError
from .sigmffile import SigMFFile
from .archive import SigMFArchive, SIGMF_DATASET_EXT, SIGMF_METADATA_EXT, SIGMF_ARCHIVE_EXT
from .utils import dict_merge
from .error import SigMFFileError


class SigMFArchiveReader():
Expand Down
2 changes: 1 addition & 1 deletion sigmf/error.py
@@ -1,6 +1,6 @@
# Copyright: Multiple Authors
#
# This file is part of SigMF. https://github.com/sigmf/sigmf-python
# This file is part of sigmf-python. https://github.com/sigmf/sigmf-python
#
# SPDX-License-Identifier: LGPL-3.0-or-later

Expand Down
4 changes: 2 additions & 2 deletions sigmf/schema-collection.json
@@ -1,5 +1,5 @@
{
"$id": "https://github.com/gnuradio/SigMF",
"$id": "https://github.com/sigmf/SigMF",
"$schema": "http://json-schema.org/draft-07/schema",
"default": {},
"required": [
Expand All @@ -20,7 +20,7 @@
"$id": "#/properties/collection/properties/core%3Aversion",
"description": "The version of the SigMF specification used to create the Collection file.",
"examples": [
"1.0.0"
"1.2.0"
],
"type": "string"
},
Expand Down
2 changes: 1 addition & 1 deletion sigmf/schema-meta.json
@@ -1,5 +1,5 @@
{
"$id": "https://github.com/gnuradio/SigMF",
"$id": "https://github.com/sigmf/SigMF",
"$schema": "http://json-schema.org/draft-07/schema",
"default": [
"global",
Expand Down
4 changes: 2 additions & 2 deletions sigmf/schema.py
@@ -1,13 +1,13 @@
# Copyright: Multiple Authors
#
# This file is part of SigMF. https://github.com/sigmf/sigmf-python
# This file is part of sigmf-python. https://github.com/sigmf/sigmf-python
#
# SPDX-License-Identifier: LGPL-3.0-or-later

'''Schema IO'''

import os
import json
import os

from . import utils

Expand Down
2 changes: 1 addition & 1 deletion sigmf/sigmf_hash.py
@@ -1,6 +1,6 @@
# Copyright: Multiple Authors
#
# This file is part of SigMF. https://github.com/sigmf/sigmf-python
# This file is part of sigmf-python. https://github.com/sigmf/sigmf-python
#
# SPDX-License-Identifier: LGPL-3.0-or-later

Expand Down
16 changes: 9 additions & 7 deletions sigmf/sigmffile.py
@@ -1,24 +1,26 @@
# Copyright: Multiple Authors
#
# This file is part of SigMF. https://github.com/sigmf/sigmf-python
# This file is part of sigmf-python. https://github.com/sigmf/sigmf-python
#
# SPDX-License-Identifier: LGPL-3.0-or-later

'''SigMFFile Object'''

from collections import OrderedDict
import codecs
import json
import tarfile
import tempfile
from os import path
import warnings
from collections import OrderedDict
from os import path

import numpy as np

from . import __version__, schema, sigmf_hash, validate
from .archive import SigMFArchive, SIGMF_DATASET_EXT, SIGMF_METADATA_EXT, SIGMF_ARCHIVE_EXT, SIGMF_COLLECTION_EXT
from . import __specification__, __version__, schema, sigmf_hash, validate
from .archive import SIGMF_ARCHIVE_EXT, SIGMF_COLLECTION_EXT, SIGMF_DATASET_EXT, SIGMF_METADATA_EXT, SigMFArchive
from .error import SigMFAccessError, SigMFFileError
from .utils import dict_merge
from .error import SigMFFileError, SigMFAccessError


class SigMFMetafile():
VALID_KEYS = {}
Expand Down Expand Up @@ -174,7 +176,7 @@ def __init__(self, metadata=None, data_file=None, global_info=None, skip_checksu
if metadata is None:
self._metadata = {self.GLOBAL_KEY:{}, self.CAPTURE_KEY:[], self.ANNOTATION_KEY:[]}
self._metadata[self.GLOBAL_KEY][self.NUM_CHANNELS_KEY] = 1
self._metadata[self.GLOBAL_KEY][self.VERSION_KEY] = "1.0.0"
self._metadata[self.GLOBAL_KEY][self.VERSION_KEY] = __specification__
elif isinstance(metadata, dict):
self._metadata = metadata
else:
Expand Down
7 changes: 4 additions & 3 deletions sigmf/utils.py
@@ -1,16 +1,17 @@
# Copyright: Multiple Authors
#
# This file is part of SigMF. https://github.com/sigmf/sigmf-python
# This file is part of sigmf-python. https://github.com/sigmf/sigmf-python
#
# SPDX-License-Identifier: LGPL-3.0-or-later

"""Utilities"""

import re
import sys
from copy import deepcopy
from datetime import datetime
import sys

import numpy as np
import re

from . import error

Expand Down
18 changes: 7 additions & 11 deletions sigmf/validate.py
@@ -1,14 +1,19 @@
# Copyright: Multiple Authors
#
# This file is part of SigMF. https://github.com/sigmf/sigmf-python
# This file is part of sigmf-python. https://github.com/sigmf/sigmf-python
#
# SPDX-License-Identifier: LGPL-3.0-or-later

'''SigMF Validator'''

import argparse
import json
import logging

import jsonschema

from . import schema
from . import __version__ as toolversion
from . import error, schema, sigmffile


def extend_with_default(validator_class):
Expand Down Expand Up @@ -80,15 +85,6 @@ def validate(metadata, ref_schema=schema.get_schema()):


def main():
import argparse
import logging
import json

from . import sigmffile
from . import error

from sigmf import __version__ as toolversion

parser = argparse.ArgumentParser(description='Validate SigMF Archive or file pair against JSON schema.',
prog='sigmf_validate')
parser.add_argument('filename', help='SigMF path (extension optional).')
Expand Down

0 comments on commit ccc1cbf

Please sign in to comment.