mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-11-27 12:26:48 +01:00
huawei: Decode TruSleep data in test new functionality
Download TruSleep data and extract ACC and PPG packets. For now the data is only extracted and provided to an analysis function that does nothing as printing the data as the TruSleep algorithm is unknown. With this patch it's possible to do sleep analysis using open source algorithms, however it's not clear which one to use and how similar the results would be. A possible use case would be Sleep as Android. The extracted data contains of Accelerometer data (ACC) and photoplethysmogram (PPG) data. ACC packets: - about every 60 seconds - contain a timestamp - contain the activity on one accelerometer axis 0x00: no activity 0xff: high activity (like walking) PPG packets: - contain a timestamp - contain raw PPG samples, each with a 10msec based timestamp This commit allows more developers and researchers to look into sleep analysis as they gain access to the raw sleep data optained from the watch. For issue #3860. Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com>
This commit is contained in:
parent
ce7b0db5b1
commit
42b7153162
@ -16,10 +16,22 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.huawei;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiFileDownloadManager;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
|
||||
public class HuaweiTruSleepParser {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(HuaweiTruSleepParser.class);
|
||||
public static final int TAG_ACC = 1;
|
||||
public static final int TAG_PPG = 2;
|
||||
|
||||
public static class TruSleepStatus {
|
||||
public final int startTime;
|
||||
@ -45,7 +57,307 @@ public class HuaweiTruSleepParser {
|
||||
'}';
|
||||
}
|
||||
}
|
||||
public static class TruSleepDataAcc {
|
||||
public final int startTime;
|
||||
public final short flags;
|
||||
|
||||
/*
|
||||
Accelerometer (ACC):
|
||||
Accelerometer data is sampled every 60 seconds, 24h a day and provides activity data on 3 axis.
|
||||
Each axis activity is represented as byte, where 0x00 is no activity and 0xff is high activity.
|
||||
The 3rd axis is ignored as it's always high (due to gravity??).
|
||||
*/
|
||||
public TruSleepDataAcc(int startTime, short flags) {
|
||||
this.startTime = startTime;
|
||||
this.flags = flags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
TruSleepDataAcc that = (TruSleepDataAcc) o;
|
||||
if (startTime != that.startTime)
|
||||
return false;
|
||||
return flags == that.flags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TruSleepDataAcc{" +
|
||||
"startTime=" + startTime +
|
||||
", flags=" + flags +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Photoplethysmogram (PPG):
|
||||
Every sample has a millisecond timestamp.
|
||||
At night data is sampled every second, but at day it's sampled less often (about every 5 minutes).
|
||||
*/
|
||||
public static class TruSleepDataPpg {
|
||||
public final long startTime;
|
||||
public final short flags;
|
||||
|
||||
TruSleepDataPpg(long startTime, short flags) {
|
||||
this.startTime = startTime;
|
||||
this.flags = flags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
TruSleepDataPpg that = (TruSleepDataPpg) o;
|
||||
if (startTime != that.startTime)
|
||||
return false;
|
||||
return flags == that.flags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TruSleepDataPpg{" +
|
||||
"startTime=" + startTime +
|
||||
", flags=" + flags +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
public static class TruSleepData {
|
||||
public ArrayList<TruSleepDataAcc> dataACCs;
|
||||
public ArrayList<TruSleepDataPpg> dataPPGs;
|
||||
|
||||
public TruSleepData() {
|
||||
this.dataACCs = new ArrayList<>();
|
||||
this.dataPPGs = new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
TruSleepData that = (TruSleepData) o;
|
||||
if (!dataACCs.equals(that.dataACCs))
|
||||
return false;
|
||||
return dataPPGs.equals(that.dataPPGs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TruSleepData{" +
|
||||
"dataPPGs=" + dataPPGs.toString() +
|
||||
", dataACCs=" + dataACCs.toString() +
|
||||
'}';
|
||||
}
|
||||
|
||||
private static final byte TAG_COMPRESSION_RAW = (byte)0xaa;
|
||||
private static final byte TAG_COMPRESSION_COMP = (byte)0xbb;
|
||||
private static final byte TAG_COMPRESSION_RESTART = (byte)0xff;
|
||||
|
||||
public void decodeAcc(byte[] data) throws IllegalArgumentException {
|
||||
/*
|
||||
Format:
|
||||
- Timestamp (4 byte)
|
||||
- Flags (3 bytes)
|
||||
*/
|
||||
ByteBuffer buffer = ByteBuffer.wrap(data);
|
||||
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
if (buffer.remaining() < 4)
|
||||
throw new IllegalArgumentException("Timestamp is missing");
|
||||
|
||||
int startTime = buffer.getInt();
|
||||
if (buffer.remaining() < 2)
|
||||
throw new IllegalArgumentException("Flags are missing");
|
||||
|
||||
short flags = buffer.getShort();
|
||||
dataACCs.add(new TruSleepDataAcc(startTime, flags));
|
||||
}
|
||||
|
||||
public void decodePpg(byte[] data) throws IllegalArgumentException, IllegalStateException {
|
||||
/*
|
||||
Format:
|
||||
- Timestamp (4 byte)
|
||||
- Number of UINT16 (1 byte)
|
||||
- Compression tag (1 byte)
|
||||
- Compressed data
|
||||
- Compression tag (1 byte)
|
||||
- Compressed data
|
||||
*/
|
||||
ByteBuffer buffer = ByteBuffer.wrap(data);
|
||||
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
if (buffer.remaining() < 4)
|
||||
throw new IllegalArgumentException("Timestamp is missing");
|
||||
|
||||
int startTime = buffer.getInt();
|
||||
|
||||
if (buffer.remaining() < 1)
|
||||
throw new IllegalArgumentException("Count of short is missing");
|
||||
|
||||
byte countShort = buffer.get();
|
||||
|
||||
ArrayList<Short> peak = decodePeak(countShort, buffer);
|
||||
ArrayList<Short> amp = decodeAmp(countShort, buffer);
|
||||
|
||||
// Sanity check
|
||||
if (peak.size() != countShort || amp.size() != countShort)
|
||||
throw new IllegalStateException("Decoded arrays have different length");
|
||||
|
||||
for (int i = 0; i < countShort; i++) {
|
||||
dataPPGs.add(new TruSleepDataPpg((long)startTime * 1000 + peak.get(i) * 10, amp.get(i)));
|
||||
}
|
||||
LOG.debug("Buffer remaining {}", buffer.remaining());
|
||||
}
|
||||
|
||||
private ArrayList<Short> decodePeak(byte countShort, ByteBuffer buffer) throws IllegalArgumentException {
|
||||
if (countShort == 0)
|
||||
throw new IllegalArgumentException("Number of short to generate is invalid");
|
||||
|
||||
if (buffer.remaining() < 1)
|
||||
throw new IllegalArgumentException("Compression tag is missing");
|
||||
|
||||
byte tag = buffer.get();
|
||||
|
||||
ArrayList<Short> al = new ArrayList<Short>();
|
||||
if (tag == TAG_COMPRESSION_RAW) {
|
||||
if (buffer.remaining() < countShort * 2)
|
||||
throw new IllegalArgumentException("Not enough elements in buffer");
|
||||
|
||||
while (countShort > 0) {
|
||||
al.add(buffer.getShort());
|
||||
countShort--;
|
||||
}
|
||||
} else if (tag == TAG_COMPRESSION_COMP) {
|
||||
short working = buffer.getShort();
|
||||
al.add(working);
|
||||
countShort--;
|
||||
|
||||
while (countShort > 0) {
|
||||
byte c = buffer.get();
|
||||
if (c == TAG_COMPRESSION_RESTART) {
|
||||
if (buffer.remaining() < 2)
|
||||
throw new IllegalArgumentException("Not enough elements in buffer");
|
||||
working = buffer.getShort();
|
||||
} else {
|
||||
working += c;
|
||||
}
|
||||
al.add(working);
|
||||
countShort--;
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("Compression " + String.format("%02x", tag) + " is unsupported");
|
||||
}
|
||||
return al;
|
||||
}
|
||||
|
||||
public ArrayList<Short> decodeAmp(byte countShort, ByteBuffer buffer) throws IllegalArgumentException {
|
||||
if (countShort == 0)
|
||||
throw new IllegalArgumentException("Number of short to generate is invalid");
|
||||
|
||||
if (buffer.remaining() < 1)
|
||||
throw new IllegalArgumentException("Compression tag is missing");
|
||||
|
||||
byte tag = buffer.get();
|
||||
|
||||
ArrayList<Short> al = new ArrayList<Short>();
|
||||
if (tag == TAG_COMPRESSION_RAW) {
|
||||
if (buffer.remaining() < countShort * 2)
|
||||
throw new IllegalArgumentException("Not enough elements in buffer");
|
||||
|
||||
while (countShort > 0) {
|
||||
al.add(buffer.getShort());
|
||||
countShort--;
|
||||
}
|
||||
} else if (tag == TAG_COMPRESSION_COMP) {
|
||||
if (buffer.remaining() < 2)
|
||||
throw new IllegalArgumentException("Offset buffer is missing");
|
||||
|
||||
short offset = buffer.getShort();
|
||||
short working = 0;
|
||||
while (buffer.remaining() > 0) {
|
||||
byte c = buffer.get();
|
||||
if (c == TAG_COMPRESSION_RESTART) {
|
||||
if (buffer.remaining() < 2)
|
||||
throw new IllegalArgumentException("Raw buffer is missing");
|
||||
working = (short)(offset + buffer.getShort());
|
||||
} else {
|
||||
working = (short)(offset + c);
|
||||
}
|
||||
al.add(working);
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("Compression " + String.format("%02x", tag) + " is unsupported");
|
||||
}
|
||||
return al;
|
||||
}
|
||||
|
||||
public void parsePpgData(byte[] data) {
|
||||
LOG.debug("Decoding PPG: len= {}, data = {}", data.length, GB.hexdump(data));
|
||||
|
||||
try {
|
||||
decodePpg(data);
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to parse TrueSleep PPG data: {}", e.toString());
|
||||
}
|
||||
}
|
||||
public void parseAccData(byte[] data) {
|
||||
LOG.debug("Decoding ACC: len= {}, data = {}", data.length, GB.hexdump(data));
|
||||
|
||||
try {
|
||||
decodeAcc(data);
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to parse TrueSleep ACC data: {}", e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static TruSleepData parseData(byte[] data) {
|
||||
/*
|
||||
Format:
|
||||
- Zero padded
|
||||
- ACC data interleaved with PPG data
|
||||
- PPG data is compressed and has variable length
|
||||
*/
|
||||
TruSleepData sleepData = new TruSleepData();
|
||||
ByteBuffer buffer = ByteBuffer.wrap(data);
|
||||
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
byte c;
|
||||
while (buffer.remaining() > 0) {
|
||||
c = buffer.get();
|
||||
|
||||
// Skip over padding
|
||||
if (c != TAG_ACC && c != TAG_PPG)
|
||||
continue;
|
||||
|
||||
// The tag identifies the data packet
|
||||
if (c == TAG_ACC) {
|
||||
if (buffer.remaining() < 1)
|
||||
break;
|
||||
|
||||
// ACC has 1 byte length
|
||||
byte length = buffer.get();
|
||||
if (buffer.remaining() < length)
|
||||
break;
|
||||
|
||||
byte[] accData = new byte[length];
|
||||
buffer.get(accData);
|
||||
|
||||
sleepData.parseAccData(accData);
|
||||
} else if (c == TAG_PPG) {
|
||||
if (buffer.remaining() < 2)
|
||||
break;
|
||||
|
||||
// PPG has 2 byte length
|
||||
short length = buffer.getShort();
|
||||
if (buffer.remaining() < length)
|
||||
break;
|
||||
|
||||
byte[] ppgData = new byte[length];
|
||||
buffer.get(ppgData);
|
||||
|
||||
sleepData.parsePpgData(ppgData);
|
||||
}
|
||||
}
|
||||
|
||||
return sleepData;
|
||||
}
|
||||
public static TruSleepStatus[] parseState(byte[] stateData) {
|
||||
/*
|
||||
Format:
|
||||
@ -63,6 +375,7 @@ public class HuaweiTruSleepParser {
|
||||
while (stateData.length - buffer.position() >= 0x10) {
|
||||
int startTime = buffer.getInt();
|
||||
int endTime = buffer.getInt();
|
||||
|
||||
// Throw away for now because we don't know what it means, and we don't think we can implement this soon
|
||||
buffer.get(); buffer.get(); buffer.get();
|
||||
buffer.get(); buffer.get(); buffer.get(); buffer.get(); buffer.get();
|
||||
@ -71,4 +384,91 @@ public class HuaweiTruSleepParser {
|
||||
}
|
||||
return retv;
|
||||
}
|
||||
|
||||
private static void analyzeOneTimeframe(HuaweiSupportProvider provider, TruSleepStatus status, ArrayList<TruSleepDataAcc> acc, ArrayList<TruSleepDataPpg> ppg) {
|
||||
java.util.Date rangeStartTime = new java.util.Date((long)status.startTime*1000);
|
||||
java.util.Date rangeEndTime = new java.util.Date((long)status.endTime*1000);
|
||||
LOG.debug("FIXME: Analyse sleep range {} - {} not implemented", rangeStartTime, rangeEndTime);
|
||||
//FIXME: Sleep analysis algorithm implemented here
|
||||
for (HuaweiTruSleepParser.TruSleepDataAcc i : acc) {
|
||||
java.util.Date startTime = new java.util.Date((long)i.startTime*1000);
|
||||
LOG.debug("TruSleepDataAcc {} : {}", startTime, String.format("%04x", i.flags));
|
||||
}
|
||||
for (HuaweiTruSleepParser.TruSleepDataPpg i : ppg) {
|
||||
java.util.Date startTime = new java.util.Date((long) i.startTime);
|
||||
LOG.debug("TruSleepDataPpg {} : {}", startTime, String.format("%04x", i.flags));
|
||||
}
|
||||
}
|
||||
|
||||
public static void analyze(HuaweiSupportProvider provider, TruSleepStatus[] status, TruSleepData data) {
|
||||
|
||||
// Analyse each time range as specified by TruSleepStatus
|
||||
for (TruSleepStatus s : status) {
|
||||
|
||||
ArrayList<TruSleepDataAcc> acc = new ArrayList<>();
|
||||
ArrayList<TruSleepDataPpg> ppg = new ArrayList<>();
|
||||
|
||||
// Collect Accelerometer data for current time frame
|
||||
for (TruSleepDataAcc i : data.dataACCs) {
|
||||
if (i.startTime >= s.startTime && i.startTime <= s.endTime)
|
||||
acc.add(i);
|
||||
}
|
||||
|
||||
// Collect PPG data for current time frame
|
||||
for (TruSleepDataPpg i : data.dataPPGs) {
|
||||
if (i.startTime >= s.startTime && i.startTime <= s.endTime)
|
||||
ppg.add(i);
|
||||
}
|
||||
|
||||
// Analyse time frame
|
||||
analyzeOneTimeframe(provider, s, acc, ppg);
|
||||
}
|
||||
}
|
||||
|
||||
public static class SleepFileDownloadCallback extends HuaweiFileDownloadManager.FileDownloadCallback {
|
||||
private byte[] statusData;
|
||||
private byte[] sleepData;
|
||||
|
||||
private boolean statusSynced;
|
||||
private boolean dataSynced;
|
||||
protected HuaweiSupportProvider provider;
|
||||
public SleepFileDownloadCallback(HuaweiSupportProvider provider) {
|
||||
this.dataSynced = false;
|
||||
this.statusSynced = false;
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
public void syncComplete(byte[] statusData, byte[] sleepData) { }
|
||||
@Override
|
||||
public void downloadException(HuaweiFileDownloadManager.HuaweiFileDownloadException e) {
|
||||
if (e.fileRequest == null) {
|
||||
LOG.error("Failed to download TruSleep file: {}", e.toString());
|
||||
syncComplete(statusData, sleepData);
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.fileRequest.getFileType() == HuaweiFileDownloadManager.FileType.SLEEP_STATE) {
|
||||
statusSynced = true;
|
||||
} else if (e.fileRequest.getFileType() == HuaweiFileDownloadManager.FileType.SLEEP_DATA) {
|
||||
dataSynced = true;
|
||||
}
|
||||
if (statusSynced && dataSynced)
|
||||
syncComplete(statusData, sleepData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void downloadComplete(HuaweiFileDownloadManager.FileRequest fileRequest) {
|
||||
if (fileRequest.getFileType() == HuaweiFileDownloadManager.FileType.SLEEP_STATE) {
|
||||
statusData = fileRequest.getData();
|
||||
statusSynced = true;
|
||||
} else if (fileRequest.getFileType() == HuaweiFileDownloadManager.FileType.SLEEP_DATA) {
|
||||
sleepData = fileRequest.getData();
|
||||
dataSynced = true;
|
||||
}
|
||||
LOG.debug("Downloaded TruSleep file {}", fileRequest.getFileType());
|
||||
if (statusSynced && dataSynced)
|
||||
syncComplete(statusData, sleepData);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -31,6 +31,8 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
@ -2379,30 +2381,37 @@ public class HuaweiSupportProvider {
|
||||
|
||||
public void onTestNewFunction() {
|
||||
// Show to user
|
||||
gbDevice.setBusyTask("Downloading file...");
|
||||
gbDevice.setBusyTask("Downloading files...");
|
||||
gbDevice.sendDeviceUpdateIntent(getContext());
|
||||
|
||||
HuaweiTruSleepParser.SleepFileDownloadCallback callback = new HuaweiTruSleepParser.SleepFileDownloadCallback(this) {
|
||||
@Override
|
||||
public void syncComplete(byte[] statusData, byte[] sleepData) {
|
||||
LOG.debug("Sync of TruSleep status and data finished");
|
||||
|
||||
if (statusData == null || statusData.length == 0)
|
||||
return;
|
||||
|
||||
HuaweiTruSleepParser.TruSleepStatus[] results = HuaweiTruSleepParser.parseState(statusData);
|
||||
if (results.length == 0)
|
||||
return;
|
||||
|
||||
HuaweiTruSleepParser.TruSleepData data = HuaweiTruSleepParser.parseData(sleepData);
|
||||
HuaweiTruSleepParser.analyze(this.provider, results, data);
|
||||
}
|
||||
};
|
||||
|
||||
huaweiFileDownloadManager.addToQueue(HuaweiFileDownloadManager.FileRequest.sleepStateFileRequest(
|
||||
getHuaweiCoordinator().getSupportsTruSleepNewSync(),
|
||||
0,
|
||||
(int) (System.currentTimeMillis() / 1000),
|
||||
new HuaweiFileDownloadManager.FileDownloadCallback() {
|
||||
@Override
|
||||
public void downloadComplete(HuaweiFileDownloadManager.FileRequest fileRequest) {
|
||||
// Handle file contents
|
||||
}
|
||||
}
|
||||
callback
|
||||
), true);
|
||||
huaweiFileDownloadManager.addToQueue(HuaweiFileDownloadManager.FileRequest.sleepDataFileRequest(
|
||||
getHuaweiCoordinator().getSupportsTruSleepNewSync(),
|
||||
0,
|
||||
(int) (System.currentTimeMillis() / 1000),
|
||||
new HuaweiFileDownloadManager.FileDownloadCallback() {
|
||||
@Override
|
||||
public void downloadComplete(HuaweiFileDownloadManager.FileRequest fileRequest) {
|
||||
// Handle file contents
|
||||
}
|
||||
}
|
||||
callback
|
||||
), true);
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,8 @@ package nodomain.freeyourgadget.gadgetbridge.devices.huawei;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
|
||||
public class TestHuaweiTruSleepParser {
|
||||
@ -36,4 +38,192 @@ public class TestHuaweiTruSleepParser {
|
||||
|
||||
Assert.assertArrayEquals(expected, result);
|
||||
}
|
||||
@Test
|
||||
public void testParseDataPpg() {
|
||||
byte[] test1 = GB.hexStringToByteArray("02B000403B0B672AAADA13C109A915E707BE130C06E3112104F80F84182F0888109900390805121004850E4317EC07C911D4037610C701580E2A02310F6B022B100E0448119B075B154F099916390A07191A0E9B00D10E2D02A7105405AAE8FDE8FDBDDA92B767943B71104EE52AB907EE06AC08BD06BE067C07DB06CC05E2079B06C206E906920639063807E0056406D905E2067807870660074D03720A8406AA0624064005CA07DF062D088706D2084B0A00");
|
||||
byte[] test2 = GB.hexStringToByteArray("027500F61E0B6724BB0C016968685756585753545050535354565B595B5B5B5F5C5A5E5B61635D5E5B5756585656AADD401138452F7826FC1D61221F27DD2BFC1A7B248B11260A7A221E1C9128FE185025C827D41C1B206223E31B73126210EC22B620D212B232DC0A93115A322E279B116717F30E2E1500");
|
||||
byte[] test3 = GB.hexStringToByteArray("025A00B2180B671BBB410174706A726F6D6D747B757B776E6E6D6E6F777D77777670757570AA543D0E52276956406837B63C103EB22C6929C7265326EA2FCC2F743CC9380239673B9739EA2FC92CC526BB2E652D6433DD3506397E2F00");
|
||||
|
||||
// TODO: Unlike PPGs with 0xBB compression the timestamps are not linear
|
||||
// The data also looks different, hmmm....
|
||||
ArrayList<HuaweiTruSleepParser.TruSleepDataPpg> expected = new ArrayList<>();
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728789362820L, (short)0xfde8));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728789336970L, (short)0xfde8));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728789367450L, (short)0xdabd));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728789332230L, (short)0xb792));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728789362540L, (short)0x9467));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728789327480L, (short)0x713b));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728789357790L, (short)0x4e10));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728789322570L, (short)0x2ae5));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728789352880L, (short)0x07b9));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728789374760L, (short)0x06ee));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728789332950L, (short)0x08ac));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728789354320L, (short)0x06bd));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728789313530L, (short)0x06be));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728789333050L, (short)0x077c));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728789358130L, (short)0x06db));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728789322400L, (short)0x05cc));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728789349170L, (short)0x07e2));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728789371550L, (short)0x069b));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728789332280L, (short)0x06c2));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728789357530L, (short)0x06e9));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728789321800L, (short)0x0692));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728789354140L, (short)0x0639));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728789316550L, (short)0x0738));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728789348720L, (short)0x05e0));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728789317540L, (short)0x0664));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728789350890L, (short)0x05d9));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728789318190L, (short)0x06e2));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728789353390L, (short)0x0778));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728789322380L, (short)0x0687));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728789356240L, (short)0x0760));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728789331470L, (short)0x034d));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728789366670L, (short)0x0a72));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728789335830L, (short)0x0684));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728789369850L, (short)0x06aa));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728789338170L, (short)0x0624));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728789376070L, (short)0x0540));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728789348100L, (short)0x07ca));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728789313550L, (short)0x06df));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728789349930L, (short)0x082d));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728789317570L, (short)0x0687));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728789354630L, (short)0x08d2));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728789325640L, (short)0x0a4b));
|
||||
|
||||
HuaweiTruSleepParser.TruSleepData result = HuaweiTruSleepParser.parseData(test1);
|
||||
Assert.assertEquals(expected, result.dataPPGs);
|
||||
|
||||
expected = new ArrayList<>();
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728782072680L, (short)0x40dd));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728782073730L, (short)0x3811));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728782074770L, (short)0x2f45));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728782075810L, (short)0x2678));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728782076680L, (short)0x1dfc));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728782077540L, (short)0x2261));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728782078420L, (short)0x271f));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728782079290L, (short)0x2bdd));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728782080120L, (short)0x1afc));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728782080960L, (short)0x247b));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728782081760L, (short)0x118b));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728782082560L, (short)0x0a26));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728782083390L, (short)0x227a));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728782084220L, (short)0x1c1e));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728782085060L, (short)0x2891));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728782085920L, (short)0x18fe));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728782086830L, (short)0x2550));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728782087720L, (short)0x27c8));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728782088630L, (short)0x1cd4));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728782089540L, (short)0x201b));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728782090450L, (short)0x2362));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728782091400L, (short)0x1be3));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728782092320L, (short)0x1273));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728782093220L, (short)0x1062));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728782094160L, (short)0x22ec));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728782095070L, (short)0x20b6));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728782096040L, (short)0x12d2));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728782097030L, (short)0x32b2));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728782097960L, (short)0x0adc));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728782098900L, (short)0x1193));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728782099810L, (short)0x325a));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728782100680L, (short)0x272e));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728782101540L, (short)0x119b));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728782102420L, (short)0x1767));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728782103280L, (short)0x0ef3));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728782104140L, (short)0x152e));
|
||||
|
||||
result = HuaweiTruSleepParser.parseData(test2);
|
||||
Assert.assertEquals(expected, result.dataPPGs);
|
||||
|
||||
expected = new ArrayList<>();
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728780469210L, (short)0x3d54));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728780470370L, (short)0x520e));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728780471490L, (short)0x6927));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728780472550L, (short)0x4056));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728780473690L, (short)0x3768));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728780474800L, (short)0x3cb6));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728780475890L, (short)0x3e10));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728780476980L, (short)0x2cb2));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728780478140L, (short)0x2969));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728780479370L, (short)0x26c7));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728780480540L, (short)0x2653));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728780481770L, (short)0x2fea));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728780482960L, (short)0x2fcc));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728780484060L, (short)0x3c74));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728780485160L, (short)0x38c9));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728780486250L, (short)0x3902));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728780487350L, (short)0x3b67));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728780488460L, (short)0x3997));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728780489650L, (short)0x2fea));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728780490900L, (short)0x2cc9));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728780492090L, (short)0x26c5));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728780493280L, (short)0x2ebb));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728780494460L, (short)0x2d65));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728780495580L, (short)0x3364));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728780496750L, (short)0x35dd));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728780497920L, (short)0x3906));
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataPpg (1728780499040L, (short)0x2f7e));
|
||||
|
||||
result = HuaweiTruSleepParser.parseData(test3);
|
||||
Assert.assertEquals(expected, result.dataPPGs);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseDataAcc() {
|
||||
byte[] test = GB.hexStringToByteArray("0107F02A0B670000B6");
|
||||
|
||||
ArrayList<HuaweiTruSleepParser.TruSleepDataAcc> expected = new ArrayList<>();
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataAcc (0x670b2af0, (short)0x0000));
|
||||
|
||||
HuaweiTruSleepParser.TruSleepData result = HuaweiTruSleepParser.parseData(test);
|
||||
Assert.assertEquals(0, result.dataPPGs.size());
|
||||
Assert.assertEquals(expected, result.dataACCs);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParsePadding() {
|
||||
byte[] test1 = GB.hexStringToByteArray("000107F02A0B670000B6");
|
||||
byte[] test2 = GB.hexStringToByteArray("00000000000107F02A0B670000B6");
|
||||
|
||||
ArrayList<HuaweiTruSleepParser.TruSleepDataAcc> expected = new ArrayList<>();
|
||||
expected.add(new HuaweiTruSleepParser.TruSleepDataAcc (0x670b2af0, (short)0x0000));
|
||||
|
||||
HuaweiTruSleepParser.TruSleepData result = HuaweiTruSleepParser.parseData(test1);
|
||||
|
||||
Assert.assertEquals(0, result.dataPPGs.size());
|
||||
Assert.assertEquals(expected, result.dataACCs);
|
||||
|
||||
result = HuaweiTruSleepParser.parseData(test2);
|
||||
|
||||
Assert.assertEquals(0, result.dataPPGs.size());
|
||||
Assert.assertEquals(expected, result.dataACCs);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseFailure() {
|
||||
ArrayList<byte[]> tests = new ArrayList<byte[]>();
|
||||
// Length shorter than expected
|
||||
tests.add(GB.hexStringToByteArray("0105F02A0B670000B6"));
|
||||
// Zero length
|
||||
tests.add(GB.hexStringToByteArray("0100F02A0B670000B6"));
|
||||
// Length bigger than input data
|
||||
tests.add(GB.hexStringToByteArray("0108F02A0B670000B6"));
|
||||
|
||||
// Length bigger than input data
|
||||
tests.add(GB.hexStringToByteArray("027600F61E0B6724BB0C016968685756585753545050535354565B595B5B5B5F5C5A5E5B61635D5E5B5756585656AADD401138452F7826FC1D61221F27DD2BFC1A7B248B11260A7A221E1C9128FE185025C827D41C1B206223E31B73126210EC22B620D212B232DC0A93115A322E279B116717F30E2E1500"));
|
||||
// Number of UINT16 less than provided
|
||||
tests.add(GB.hexStringToByteArray("027500F61E0B6723BB0C016968685756585753545050535354565B595B5B5B5F5C5A5E5B61635D5E5B5756585656AADD401138452F7826FC1D61221F27DD2BFC1A7B248B11260A7A221E1C9128FE185025C827D41C1B206223E31B73126210EC22B620D212B232DC0A93115A322E279B116717F30E2E1500"));
|
||||
// Wrong compression tag
|
||||
tests.add(GB.hexStringToByteArray("025A00B2180B671BCC410174706A726F6D6D747B757B776E6E6D6E6F777D77777670757570AA543D0E52276956406837B63C103EB22C6929C7265326EA2FCC2F743CC9380239673B9739EA2FC92CC526BB2E652D6433DD3506397E2F00"));
|
||||
// Wrong compression tag 2
|
||||
tests.add(GB.hexStringToByteArray("025A00B2180B671BBB410174706A726F6D6D747B757B776E6E6D6E6F777D77777670757570DD543D0E52276956406837B63C103EB22C6929C7265326EA2FCC2F743CC9380239673B9739EA2FC92CC526BB2E652D6433DD3506397E2F00"));
|
||||
// Wrong restart tags in compressed data
|
||||
tests.add(GB.hexStringToByteArray("025A00B2180B671BBB410FFFFFFA726F6D6D747B757B776E6E6D6E6F777D77777670757570AA543D0E52276956406837B63C103EB22C6929C7265326EA2FCC2F743CC9380239673B9739EA2FC92CC526BB2E652D6433DD3506397E2F00"));
|
||||
|
||||
for (byte[] test: tests) {
|
||||
HuaweiTruSleepParser.TruSleepData result = HuaweiTruSleepParser.parseData(test);
|
||||
|
||||
Assert.assertEquals("Test input data " + GB.hexdump(test), 0, result.dataPPGs.size());
|
||||
Assert.assertEquals("Test input data " + GB.hexdump(test),0, result.dataACCs.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user