from types import SimpleNamespace import numpy as np def decompress_shape( compressed_shape: SimpleNamespace, force_decompression: bool = False ) -> np.ndarray: """ Decompress a gradient or pulse shape compressed with a run-length compression scheme on the derivative. The given shape is structure with the following fields: - num_samples - the number of samples in the uncompressed waveform - data - containing the compressed waveform See also `compress_shape.py`. Parameters ---------- compressed_shape : SimpleNamespace Run-length encoded shape. force_decompression : bool, default=False Returns ------- decompressed_shape : numpy.ndarray Decompressed shape. """ data_pack = compressed_shape.data data_pack_len = len(data_pack) num_samples = int(compressed_shape.num_samples) if not force_decompression and num_samples == data_pack_len: # Uncompressed shape decompressed_shape = data_pack return decompressed_shape decompressed_shape = np.zeros(num_samples) # Pre-allocate result matrix # Decompression starts here data_pack_diff = data_pack[1:] - data_pack[:-1] # When data_pack_diff == 0 the subsequent samples are equal ==> marker for repeats (run-length encoding) data_pack_markers = np.where(data_pack_diff == 0.0)[0] count_pack = 0 # Points to current compressed sample count_unpack = 0 # Points to current uncompressed sample for i in range(len(data_pack_markers)): # This index may have "false positives", e.g. if the value 3 repeats 3 times, then we will have 3 3 3 next_pack = data_pack_markers[i] current_unpack_samples = next_pack - count_pack if current_unpack_samples < 0: # Rejects false positives continue elif current_unpack_samples > 0: # We have an unpacked block to copy decompressed_shape[ count_unpack : count_unpack + current_unpack_samples ] = data_pack[count_pack:next_pack] count_pack += current_unpack_samples count_unpack += current_unpack_samples # Packed/repeated section rep = int(data_pack[count_pack + 2] + 2) decompressed_shape[count_unpack : (count_unpack + rep)] = data_pack[count_pack] count_pack += 3 count_unpack += rep # Samples left? if count_pack <= data_pack_len - 1: assert data_pack_len - count_pack == num_samples - count_unpack # Copy the rest of the shape, it is unpacked decompressed_shape[count_unpack:] = data_pack[count_pack:] decompressed_shape = np.cumsum(decompressed_shape) return decompressed_shape