def deinterleave_array(ndarray: numpy.ndarray, offset: int, step: int) -> numpy.ndarray: """ Extracts a single channel type from an interleaved array. An interleaved channel contains multiple channel types in a single payload. This is useful for situations where a sensor produces several values for a single timestamp. For example, a GPS will produce a LATITUDE, LONGITUDE, ALTITUDE, and SPEED values with every update. An interleaved channel is an encoding that encodes multiple channel types into a single payload. Every channel has a field channel_types that list the channel types contained within the payload. For a GPS sensor, the channel_types array would look like [LATITUDE, LONGITUDE, ALTITUDE, SPEED]. The location of the channel type in channel_types determines the offset into the payload and the length of channel_types determines the step size. As such, a GPS channel payload would contain the following values: [LAT0, LNG0, ALT0, SPD0, LAT1, LNG1, ALT1, SPD1, ..., LATn, LNGn, ALTn, SPDn] This function will "deinterleave" the encoding and provide an array of a single channel type. :param ndarray: Interleaved array. :param offset: Offset into the array. :param step: The step size. :return: A numpy array of a single channel type. """ if offset < 0 or offset >= len(ndarray): raise exceptions.ReaderException( "offset {} out of range [{},{})".format(offset, 0, len(ndarray))) if offset >= step: raise exceptions.ReaderException( "offset {} must be smaller than step {}".format(offset, step)) if step <= 0 or step > len(ndarray): raise exceptions.ReaderException("step {} out of range [{},{})".format( step, 0, len(ndarray))) if len(ndarray) % step != 0: raise exceptions.ReaderException( "step {} is not a multiple of {}".format(step, len(ndarray))) # pylint: disable=C1801 if len(ndarray) == 0: return empty_array() return ndarray[offset::step]
def payload_median(self) -> numpy.float64: """Returns the median of this channel's payload. :return: The median of this channel's payload. """ payload_values = self.payload_values() if len(payload_values) <= 0: raise exceptions.ReaderException("Can't obtain median value of empty array") median = numpy.median(self.payload_values()) if isinstance(median, numpy.ndarray): return median[0] elif isinstance(median, numpy.float64): return median else: raise exceptions.ReaderException("Unknown type %s" % str(type(median)))
def get_value_std(self, channel_type: int) -> float: """ Returns the standard deviation value for a single channel type. :param channel_type: The channel type to extract the std from. :return: The standard deviation. """ idx = self.channel_index(channel_type) if idx < 0 or len(self.value_stds) == 0: raise exceptions.ReaderException("std DNE, is the payload empty?") return self.value_stds[idx]
def get_value_mean(self, channel_type: int) -> float: """ Returns the mean value for a single channel type. :param channel_type: The channel type to extract the mean from. :return: The mean value. """ idx = self.channel_index(channel_type) if idx < 0 or len(self.value_means) == 0: raise exceptions.ReaderException("mean DNE, is the payload empty?") return self.value_means[idx]
def interleave_arrays(arrays: typing.List[numpy.ndarray]) -> numpy.ndarray: """ Interleaves multiple arrays together. :param arrays: Arrays to interleave. :return: Interleaved arrays. """ if len(arrays) < 2: raise exceptions.ReaderException( "At least 2 arrays are required for interleaving") if len(set(map(len, arrays))) > 1: raise exceptions.ReaderException("all arrays must be same size") total_arrays = len(arrays) total_elements = sum(map(lambda array: array.size, arrays)) interleaved_array = numpy.empty((total_elements, ), dtype=arrays[0].dtype) for i in range(total_arrays): interleaved_array[i::total_arrays] = arrays[i] return interleaved_array
def lz4_decompress(buf: bytes) -> bytes: """ Decompresses an API 900 compressed buffer. :param buf: The buffer to decompress. :return: The uncompressed buffer. """ uncompressed_size = calculate_uncompressed_size(buf) if uncompressed_size <= 0: raise exceptions.ReaderException( "uncompressed size [{}] must be > 0".format(uncompressed_size)) return lz4.block.decompress( buf[4:], uncompressed_size=calculate_uncompressed_size(buf))
def get_metadata(metadata: typing.List[str], k: str) -> str: """ Given a meta-data key, extract the value. :param metadata: List of metadata to extract value from. :param k: The meta-data key. :return: The value corresponding to the key or an empty string. """ if len(metadata) % 2 != 0: raise exceptions.ReaderException( "metadata list must contain an even number of items") idx = safe_index_of(metadata, k) if idx < 0: return "" return metadata[idx + 1]
def get_metadata_as_dict(metadata: typing.List[str]) -> typing.Dict[str, str]: """ Since the metadata is inherently key-value, it may be useful to turn the metadata list into a python dictionary. :param metadata: The metadata list. :return: Metadata as a python dictionary. """ if not metadata: return {} if len(metadata) % 2 != 0: raise exceptions.ReaderException( "metadata list must contain an even number of items") metadata_dict = {} metadata_copy = metadata.copy() while len(metadata_copy) >= 2: metadata_key = metadata_copy.pop(0) metadata_value = metadata_copy.pop(0) if metadata_key not in metadata_dict: metadata_dict[metadata_key] = metadata_value return metadata_dict