@iotile/iotile-device
Version:
A typescript library for interfacing with IOTile BLE devices
245 lines • 9.13 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var iotile_common_1 = require("@iotile/iotile-common");
var heatshrink_ts_1 = require("heatshrink-ts");
var constants_1 = require("./constants");
var flexible_dict_report_1 = require("../../common/flexible-dict-report");
/**
* Calculate the maximum time that a given threshold is exceeded.
*
* @param data A list of samples along a single axis in G's
* @param threshold The threshold that we should count time above
* @param samplingRate The sampling rate of the data in Hz
*
* @returns The number of ms that data is above threshold in a single continuous
* event.
*/
function timeAboveThreshold(data, threshold, samplingRate) {
var maxCount = 0;
var currCount = 0;
var above = false;
var lastSample = 0.0;
for (var _i = 0, data_1 = data; _i < data_1.length; _i++) {
var sample = data_1[_i];
if (above === false && Math.abs(sample) >= threshold) {
above = true;
currCount = 0;
}
else if (above) {
currCount += 1;
if (Math.abs(sample) < threshold || lastSample * sample < 0.0) {
above = false;
if (currCount > maxCount)
maxCount = currCount;
}
}
lastSample = sample;
}
if (above && currCount > maxCount)
maxCount = currCount;
return maxCount / samplingRate * 1000.0;
}
exports.timeAboveThreshold = timeAboveThreshold;
function calculateDeltaV(data, threshold, samplingRate) {
var maxDV = 0;
var currDV = 0;
var above = false;
var lastSample = 0.0;
var G_CONST = 9.80665;
for (var _i = 0, data_2 = data; _i < data_2.length; _i++) {
var sample = data_2[_i];
if (above === false && Math.abs(sample) >= threshold) {
above = true;
currDV = sample;
}
else if (above) {
currDV += sample;
if (Math.abs(sample) < threshold || lastSample * sample < 0.0) {
above = false;
if (Math.abs(currDV) > Math.abs(maxDV))
maxDV = currDV;
}
}
lastSample = sample;
}
if (above && Math.abs(currDV) > Math.abs(maxDV))
maxDV = currDV;
return maxDV / samplingRate * G_CONST;
}
exports.calculateDeltaV = calculateDeltaV;
function maxAbs(data) {
var max = 0.0;
for (var _i = 0, data_3 = data; _i < data_3.length; _i++) {
var value = data_3[_i];
if (Math.abs(value) > max) {
max = Math.abs(value);
}
}
return max;
}
exports.maxAbs = maxAbs;
function summarizeAxis(data, threshold, samplingRate) {
return {
deltaV: calculateDeltaV(data, threshold, samplingRate),
peak: maxAbs(data),
timeAboveThreshold: timeAboveThreshold(data, threshold, samplingRate)
};
}
exports.summarizeAxis = summarizeAxis;
function summarizeWaveform(wave) {
var xSummary = summarizeAxis(wave.acceleration_data.x, 1.0, wave.sampling_rate);
var ySummary = summarizeAxis(wave.acceleration_data.y, 1.0, wave.sampling_rate);
var zSummary = summarizeAxis(wave.acceleration_data.z, 1.0, wave.sampling_rate);
var peakAxis = 'x';
var peak = xSummary.peak;
var duration = xSummary.timeAboveThreshold;
if (ySummary.peak > peak) {
peakAxis = 'y';
peak = ySummary.peak;
duration = ySummary.timeAboveThreshold;
}
if (zSummary.peak > peak) {
peakAxis = 'z';
peak = zSummary.peak;
duration = zSummary.timeAboveThreshold;
}
return {
peak: peak,
axis: peakAxis,
duration: duration,
delta_v_x: xSummary.deltaV,
delta_v_y: ySummary.deltaV,
delta_v_z: zSummary.deltaV
};
}
exports.summarizeWaveform = summarizeWaveform;
/**
* Unpack an array buffer that corresponds to a list of VLE encoded integers.
*
* The function assumes that the input buffer was created from a list of integers
* in the range of -4095 to 4095 that were processed in the following way to
* create this binary buffer:
*
* - They were delta encoded, so all entries after the first were stored as
* the difference between the value and the last value.
* - They were zig-zag encoded so that all negative values became positive.
* - They were packed as variable length integers where 7-bit values are stored
* as a single byte and all other values are stored as two bytes.
*
* This function undoes all three of these packing steps
*
* @param input A list of integers that have been encoded in the following
* way: first they were delta encoded, then they were zigzag
* encoded and finally they were delta encoded.
*/
function unpackVLEIntegerList(input) {
var inputBytes = new Uint8Array(input);
var outputNumbers = [];
var accum = 0;
var shift = 0;
/*
* First undo the variant length encoding, in which numbers
* between -64 and 63, inclusive are encoded in a single byte
* with the 7th bit clear and other numbers are encoded in
* two bytes with the 7th bit of the first byte set. The
* packing is little-endian.
*
* We simultaneously remove the zig-zag encoding to turn these
* values back into signed integers.
*/
for (var i = 0; i < inputBytes.byteLength; ++i) {
var val = inputBytes[i];
if (val & (1 << 7)) {
accum |= (val & (0x7f));
shift = 7;
}
else {
accum |= ((val & 0x7f) << shift);
var zigzag = ((accum >>> 1)) ^ -(accum & 1);
accum = 0;
shift = 0;
outputNumbers.push(zigzag);
}
}
/**
* Undo the delta encoding on each of the numbers now that
* we have them back in their pristine state.
*/
for (var i = 1; i < outputNumbers.length; ++i) {
outputNumbers[i] += outputNumbers[i - 1];
}
return outputNumbers;
}
exports.unpackVLEIntegerList = unpackVLEIntegerList;
function decompressWaveforms(rawWaveforms) {
var decoder = new heatshrink_ts_1.HeatshrinkDecoder(constants_1.WINDOW_BITS, constants_1.LOOKAHEAD_BITS, constants_1.INPUT_BUFFER_LENGTH);
var accelerationData = [];
var waveforms = {};
for (var wave in rawWaveforms) {
var rawWaveform = rawWaveforms[wave];
accelerationData = [];
/*
* Un-heatshrink each waveform separately since they are encoded separately.
* In particular, we do not want any sliding window shared between subsequent
* waveforms.
*/
decoder.reset();
decoder.process(rawWaveforms[wave].rawWaveform);
var expanded = decoder.getOutput();
// variable length decoding
var vleDecoded = unpackVLEIntegerList(expanded.buffer);
if (vleDecoded.length != 3072) {
throw new iotile_common_1.InvalidDataError('Waveform Decompression Error', "Received number of data points is incorrect; parsed " + vleDecoded.length + " of 3072");
}
// convert from device internal storage to Gs
for (var _i = 0, vleDecoded_1 = vleDecoded; _i < vleDecoded_1.length; _i++) {
var v = vleDecoded_1[_i];
accelerationData.push(v * .049);
}
var waveformData = { acceleration_data: { x: accelerationData.slice(0, 1024),
y: accelerationData.slice(1024, 2048),
z: accelerationData.slice(2048) },
sampling_rate: constants_1.SAMPLING_RATE,
crc_code: rawWaveform.crcCode
};
waveforms[wave] = {
deviceTimestamp: rawWaveform.timestamp,
utcTimestamp: tryConvertUTCTimestamp(rawWaveform.timestamp),
summary: summarizeWaveform(waveformData),
waveform: waveformData
};
/**
* If the waveform has an embedded UTC timestamp, promote it to the waveform's UTC time.
*/
}
return waveforms;
}
exports.decompressWaveforms = decompressWaveforms;
function tryConvertUTCTimestamp(deviceTimestamp) {
if (deviceTimestamp === 0xFFFFFFFF)
return null;
if (!(deviceTimestamp & (1 << 31)))
return null;
//Mask out the high bit to leave the lower 31 bits
var y2kDelta = deviceTimestamp & ((1 << 31) - 1);
var y2k = new Date('2000-01-01T00:00:00Z');
var timestamp = y2k.getTime() + y2kDelta * 1000;
var utcDate = new Date(timestamp);
return utcDate;
}
exports.tryConvertUTCTimestamp = tryConvertUTCTimestamp;
/**
* Create IOTileEvents for all waveforms.
*/
function createWaveformEvents(waveforms) {
var events = [];
var streamID = 0x5020;
for (var uniqueId in waveforms) {
var waveform = waveforms[uniqueId];
var event_1 = new flexible_dict_report_1.IOTileEvent(streamID, waveform.deviceTimestamp, waveform.summary, waveform.waveform, +uniqueId, waveform.utcTimestamp);
events.push(event_1);
}
return events;
}
exports.createWaveformEvents = createWaveformEvents;
//# sourceMappingURL=utilities.js.map