Skip to content

Commit ac7669c

Browse files
authored
fixed slicing for fixed-point complex files where scaling was not applied (#126)
1 parent 3a7d97f commit ac7669c

File tree

2 files changed

+50
-41
lines changed

2 files changed

+50
-41
lines changed

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.5.0"
8+
__version__ = "1.5.1"
99
# matching version of the SigMF specification
1010
__specification__ = "1.2.6"
1111

sigmf/sigmffile.py

Lines changed: 49 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -314,50 +314,59 @@ def __next__(self):
314314
raise StopIteration
315315

316316
def __getitem__(self, sli):
317-
mem = self._memmap[sli] # matches behavior of numpy.ndarray.__getitem__()
317+
"""
318+
Enable slicing and indexing into the dataset samples.
318319
319-
# apply _return_type conversion if set
320-
if self._return_type is None:
321-
# no special conversion needed
322-
if not self.autoscale:
323-
return mem
324-
else:
325-
# apply autoscaling for fixed-point data when autoscale=True
326-
dtype = dtype_info(self.get_global_field(self.DATATYPE_KEY))
327-
is_fixedpoint_data = dtype["is_fixedpoint"]
328-
329-
if is_fixedpoint_data:
330-
# apply scaling for fixed-point data
331-
is_unsigned_data = dtype["is_unsigned"]
332-
component_size = dtype["component_size"]
333-
data_type_out = np.dtype("f4") if not self.is_complex_data else np.dtype("f4, f4")
334-
335-
data = mem.astype(data_type_out)
336-
data = data.view(np.dtype("f4"))
320+
Should match behavior of ndarray.__getitem__() and apply autoscaling similar to read_samples().
321+
"""
322+
mem = self._memmap[sli]
323+
324+
# apply autoscaling for fixed-point data when autoscale=True
325+
if self.autoscale:
326+
dtype = dtype_info(self.get_global_field(self.DATATYPE_KEY))
327+
if dtype["is_fixedpoint"]:
328+
# extract scaling parameters
329+
is_unsigned_data = dtype["is_unsigned"]
330+
component_size = dtype["component_size"]
331+
332+
# convert to float and apply scaling
333+
if self.is_complex_data:
334+
# for complex data, mem is shaped (..., 2) where last dim is [real, imag]
335+
real_part = mem[..., 0].astype(np.float32)
336+
imag_part = mem[..., 1].astype(np.float32)
337+
338+
# apply scaling to both parts
339+
if is_unsigned_data:
340+
real_part -= 2 ** (component_size * 8 - 1)
341+
imag_part -= 2 ** (component_size * 8 - 1)
342+
real_part *= 2 ** -(component_size * 8 - 1)
343+
imag_part *= 2 ** -(component_size * 8 - 1)
344+
345+
# combine into complex numbers
346+
data = real_part + 1.0j * imag_part
347+
else:
348+
# for real data, direct scaling
349+
data = mem.astype(np.float32)
337350
if is_unsigned_data:
338351
data -= 2 ** (component_size * 8 - 1)
339352
data *= 2 ** -(component_size * 8 - 1)
340-
data = data.view(data_type_out)
341-
if self.is_complex_data:
342-
data = data.view(np.complex64)
343-
# for single-channel complex data, flatten the last dimension
344-
if data.ndim > 1 and self.num_channels == 1:
345-
data = data.flatten()
346-
return data[0] if isinstance(sli, int) else data
347-
else:
348-
# floating-point data, no scaling needed
349-
return mem
350-
351-
# handle complex data type conversion
352-
if self._memmap.ndim == 2:
353-
# num_channels == 1
354-
ray = mem[:, 0].astype(self._return_type) + 1.0j * mem[:, 1].astype(self._return_type)
355-
elif self._memmap.ndim == 3:
356-
# num_channels > 1
357-
ray = mem[:, :, 0].astype(self._return_type) + 1.0j * mem[:, :, 1].astype(self._return_type)
358-
else:
359-
raise ValueError("unhandled ndim in SigMFFile.__getitem__(); this shouldn't happen")
360-
return ray[0] if isinstance(sli, int) else ray # return element instead of 1-element array
353+
354+
return data
355+
356+
# handle complex data type conversion if _return_type is set (no autoscaling was applied)
357+
if self._return_type is not None:
358+
if self._memmap.ndim == 2:
359+
# num_channels == 1
360+
ray = mem[:, 0].astype(self._return_type) + 1.0j * mem[:, 1].astype(self._return_type)
361+
elif self._memmap.ndim == 3:
362+
# num_channels > 1
363+
ray = mem[:, :, 0].astype(self._return_type) + 1.0j * mem[:, :, 1].astype(self._return_type)
364+
else:
365+
raise ValueError("unhandled ndim in SigMFFile.__getitem__(); this shouldn't happen")
366+
return ray[0] if isinstance(sli, int) else ray # return element instead of 1-element array
367+
368+
# return raw data (no autoscaling, no complex conversion needed)
369+
return mem
361370

362371
def get_num_channels(self):
363372
"""Return integer number of channels."""

0 commit comments

Comments
 (0)