1#!/usr/bin/env python
2
3from construct import *
4
5BLOCK_TYPE = Enum(Int8ul,
6    RESERVED            = 0x00,
7    ARBITRARY_TIMESTAMP = 0x01,
8    CONSTANT_FREQUENCY  = 0x02,
9)
10
11SAMPLE_TYPE = Enum(Int16ul,
12    RESERVED              = 0x0000,
13    TEMPERATURE           = 0x0001,
14    ACCELERATION          = 0x0002,
15    ANGULAR_RATE          = 0x0003,
16    VOLTAGE               = 0x0004,
17    ECG                   = 0x0005,
18    HUMIDITY              = 0x0006,
19    PRESSURE              = 0x0007,
20    MAGNETIC_FLUX_DENSITY = 0x0008,
21
22    CUSTOM                = 0xF000,
23)
24
25resd_header = Struct(
26    "magic" / Const(b"RESD"),
27    "version" / Int8ul,
28    "reserved" / Padding(3)
29)
30
31blob = Struct(
32    "size" / Rebuild(Int32ul, len_(this.data)),
33    "data" / Int8ul[this.size],
34)
35
36data_block_metadata_item = Struct(
37    "key" / NullTerminated(GreedyRange(Int8ub)),
38    "type" / Int8ul,
39    "value" / Switch(this.type,
40    {
41        0x00: Int8sl,
42        0x01: Int8ul,
43        0x02: Int16sl,
44        0x03: Int16ul,
45        0x04: Int32sl,
46        0x05: Int32ul,
47        0x06: Int64sl,
48        0x07: Int64ul,
49        0x08: Float32l,
50        0x09: Float64l,
51        0x0A: NullTerminated(GreedyRange(Int8ul)),
52        0x0B: blob,
53    }),
54)
55
56data_block_metadata = Struct(
57    "size" / Int64ul,
58    "items" / FixedSized(this.size, GreedyRange(data_block_metadata_item)),
59)
60
61data_block_sample = lambda sample_type: Switch(sample_type, {
62    "TEMPERATURE": Int32sl,
63    "ACCELERATION": Struct(
64        "x" / Int32sl,
65        "y" / Int32sl,
66        "z" / Int32sl,
67    ),
68    "ANGULAR_RATE": Struct(
69        "x" / Int32sl,
70        "y" / Int32sl,
71        "z" / Int32sl,
72    ),
73    "VOLTAGE": Int32ul,
74    "ECG": Int32sl,
75    "HUMIDITY": Int32ul,
76    "PRESSURE": Int64ul,
77    "MAGNETIC_FLUX_DENSITY": Struct(
78        "x" / Int32sl,
79        "y" / Int32sl,
80        "z" / Int32sl,
81    ),
82})
83
84data_block_sample_arbitrary = lambda sample_type: Struct(
85    "timestamp" / Int64ul,
86    "sample" / data_block_sample(sample_type)
87)
88
89data_block_sample_arbitrary_subheader = Struct(
90    "start_time" / Int64ul,
91)
92
93data_block_sample_frequency = lambda sample_type: Struct(
94    "sample" / data_block_sample(sample_type)
95)
96
97data_block_sample_frequency_subheader = Struct(
98    "start_time" / Int64ul,
99    "period" / Int64ul,
100)
101
102data_block_sample_single = lambda type_, sample_type: Switch(type_, {
103    "ARBITRARY_TIMESTAMP": data_block_sample_arbitrary(sample_type),
104    "CONSTANT_FREQUENCY": data_block_sample_frequency(sample_type),
105})
106
107data_block_subheader = Switch(this.header.block_type, {
108    "ARBITRARY_TIMESTAMP": data_block_sample_arbitrary_subheader,
109    "CONSTANT_FREQUENCY": data_block_sample_frequency_subheader
110})
111
112data_block_header = Struct(
113    "block_type" / BLOCK_TYPE,
114    "sample_type" / SAMPLE_TYPE,
115    "channel_id" / Int16ul,
116    "data_size" / Int64ul,
117)
118
119data_block = Struct(
120    "header" / data_block_header,
121    "subheader" / data_block_subheader,
122    "metadata" / data_block_metadata,
123    "samples" / GreedyRange(data_block_sample_single(this.header.block_type, this._.header.sample_type))
124)
125
126resd = Struct(
127    "header" / resd_header,
128    "blocks" / GreedyRange(data_block)
129)
130