Skip to content

Commit 14abb47

Browse files
committed
support for BLUE uint16 & uint32, with testing
1 parent 0dc2923 commit 14abb47

File tree

4 files changed

+44
-21
lines changed

4 files changed

+44
-21
lines changed

docs/source/converters.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -137,9 +137,9 @@ The following table summarizes tested BLUE formats and their compatibility with
137137
:header-rows: 1
138138
:stub-columns: 1
139139

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)`", "✅", "✅", "✅", "✅", "✅", "✅", "", "❌"
140+
"Code", ":abbr:`P (packed)`", ":abbr:`N (int4)`", ":abbr:`B (int8)`", ":abbr:`U (uint16)`", ":abbr:`I (int16)`", ":abbr:`V (uint32)`", ":abbr:`L (int32)`", ":abbr:`F (float32)`", ":abbr:`X (int64)`", ":abbr:`D (float64)`", ":abbr:`O (excess-128)`"
141+
":abbr:`S (scalar)`", "❌", "❌", "✅", "✅", "✅", "✅", "✅", "✅", "✅", "✅", "❌"
142+
":abbr:`C (complex)`", "❌", "❌", "✅", "✅", "✅", "✅", "✅", "✅", "✅", "✅", "❌"
143143

144144
**Legend:**
145145
* ✅ = Tested and known working

sigmf/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
# SPDX-License-Identifier: LGPL-3.0-or-later
66

77
# version of this python module
8-
__version__ = "1.6.1"
8+
__version__ = "1.6.2"
99
# matching version of the SigMF specification
1010
__specification__ = "1.2.6"
1111

sigmf/convert/blue.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,17 +67,19 @@
6767

6868
TYPE_MAP = {
6969
# BLUE format code to numpy dtype
70-
# note new non 1-1 mapping supported needs new handling in data_loopback
70+
# note: new non 1-1 mapping supported needs new handling in data_loopback
71+
# "P" : packed bits,
7172
"A": np.dtype("S1"), # ASCII for unpacking text fields
73+
# "N" : 4-bit integer,
7274
"B": np.int8,
75+
"U": np.uint16,
7376
"I": np.int16,
77+
"V": np.uint32,
7478
"L": np.int32,
75-
"X": np.int64,
7679
"F": np.float32,
80+
"X": np.int64,
7781
"D": np.float64,
78-
# unsupported codes
79-
# "P" : packed bits
80-
# "N" : 4-bit integer
82+
# "O": excess-128,
8183
}
8284

8385

@@ -108,7 +110,12 @@ def blue_to_sigmf_type_str(h_fixed: dict) -> str:
108110
bits = dtype_obj.itemsize * 8 # bytes to bits
109111

110112
# infer sigmf type from numpy kind
111-
sigmf_type = "i" if dtype_obj.kind in ("i", "u") else "f"
113+
if dtype_obj.kind == "u":
114+
sigmf_type = "u"
115+
elif dtype_obj.kind == "i":
116+
sigmf_type = "i"
117+
else:
118+
sigmf_type = "f"
112119

113120
# build datatype string
114121
prefix = "c" if is_complex else "r"

tests/test_convert_blue.py

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
import numpy as np
1616

1717
import sigmf
18-
from sigmf.utils import SIGMF_DATETIME_ISO8601_FMT
1918
from sigmf.convert.blue import TYPE_MAP, blue_to_sigmf
19+
from sigmf.utils import SIGMF_DATETIME_ISO8601_FMT
2020

2121
from .test_convert_wav import _validate_ncd
2222
from .testdata import get_nonsigmf_path
@@ -33,21 +33,27 @@ def setUp(self) -> None:
3333
self.format_tolerance = [
3434
("SB", 1e-1), # scalar int8
3535
("CB", 1e-1), # complex int8
36+
("SU", 1e-4), # scalar uint16
37+
("CU", 1e-4), # complex uint16
3638
("SI", 1e-4), # scalar int16
3739
("CI", 1e-4), # complex int16
38-
("SL", 1e-4), # scalar int32
39-
("CL", 1e-4), # complex int32
40+
("SV", 1e-7), # scalar uint32
41+
("CV", 1e-7), # complex uint32
42+
("SL", 1e-8), # scalar int32
43+
("CL", 1e-8), # complex int32
44+
# ("SX", 1e-8), # scalar int64, should work but not allowed by SigMF spec
45+
# ("CX", 1e-8), # complex int64, should work but not allowed by SigMF spec
4046
("SF", 1e-8), # scalar float32
4147
("CF", 1e-8), # complex float32
42-
("SD", 1e-8), # scalar float64
43-
("CD", 1e-8), # complex float64
48+
("SD", 0), # scalar float64
49+
("CD", 0), # complex float64
4450
]
4551

4652
self.samp_rate = 192e3
4753
num_samples = 1024
4854
ttt = np.linspace(0, num_samples / self.samp_rate, num_samples, endpoint=False)
4955
freq = 3520 # A7 note
50-
self.iq_data = (0.5 * np.exp(2j * np.pi * freq * ttt)).astype(np.complex64)
56+
self.iq_data = 0.5 * np.exp(2j * np.pi * freq * ttt) # complex64
5157
time_now = datetime.now(timezone.utc)
5258
self.datetime = time_now.strftime(SIGMF_DATETIME_ISO8601_FMT)
5359
self.timecode = (time_now - datetime(1950, 1, 1, tzinfo=timezone.utc)).total_seconds()
@@ -63,15 +69,26 @@ def write_minimal(self, format: bytes = b"CF") -> None:
6369
dtype = TYPE_MAP[chr(format[1])]
6470

6571
if np.issubdtype(dtype, np.integer):
66-
multiplier = 2 ** (np.dtype(dtype).itemsize * 8 - 1)
72+
scale = 2 ** (np.dtype(dtype).itemsize * 8 - 1)
6773
if is_complex:
68-
ci_real = (self.iq_data.real * multiplier).astype(dtype)
69-
ci_imag = (self.iq_data.imag * multiplier).astype(dtype)
74+
if np.dtype(dtype).kind == "u":
75+
# unsigned
76+
ci_real = (self.iq_data.real * scale + scale).astype(dtype)
77+
ci_imag = (self.iq_data.imag * scale + scale).astype(dtype)
78+
else:
79+
# signed
80+
ci_real = (self.iq_data.real * scale).astype(dtype)
81+
ci_imag = (self.iq_data.imag * scale).astype(dtype)
7082
iq_converted = np.empty((self.iq_data.size * 2,), dtype=dtype)
7183
iq_converted[0::2] = ci_real
7284
iq_converted[1::2] = ci_imag
7385
else:
74-
iq_converted = (self.iq_data.real * multiplier).astype(dtype)
86+
if np.dtype(dtype).kind == "u":
87+
# unsigned
88+
iq_converted = (self.iq_data.real * scale + scale).astype(dtype)
89+
else:
90+
# signed
91+
iq_converted = (self.iq_data.real * scale).astype(dtype)
7592
elif np.issubdtype(dtype, np.floating):
7693
if is_complex:
7794
ci_real = self.iq_data.real.astype(dtype)
@@ -186,7 +203,6 @@ def test_create_ncd(self):
186203
for blue_path in self.blue_paths:
187204
meta = blue_to_sigmf(blue_path=blue_path)
188205
_validate_ncd(self, meta, blue_path)
189-
print(len(meta), blue_path)
190206
if len(meta):
191207
# check sample read consistency
192208
np.testing.assert_allclose(meta.read_samples(count=10), meta[0:10], atol=1e-6)

0 commit comments

Comments
 (0)