Skip to content

Commit 71090c4

Browse files
authored
TLM Non-Standard Byte Sized Data Type Support (#51)
1 parent a3251e6 commit 71090c4

2 files changed

Lines changed: 76 additions & 10 deletions

File tree

lib/tlm/tlm_interpreter.go

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ type TelemetryPacket struct {
2626

2727
// InterpretUnsignedInteger interprets a byte slice as an unsigned integer.
2828
// The endianness parameter specifies the byte order of the data.
29-
// Size of the data must be 1, 2, 4, or 8 bytes.
29+
// Size of the data must be 1-8 bytes.
3030
func InterpretUnsignedInteger(data []byte, endianness string) (interface{}, error) {
3131
switch len(data) {
3232
case 1:
@@ -47,14 +47,34 @@ func InterpretUnsignedInteger(data []byte, endianness string) (interface{}, erro
4747
}
4848
return binary.BigEndian.Uint64(data), nil
4949
default:
50-
return nil, fmt.Errorf("unsupported data length: %d", len(data))
50+
// I will crash out if we ever need to support integer sizes larger than 8 bytes - Aaron
51+
if len(data) < 1 || len(data) > 8 {
52+
return nil, fmt.Errorf("unsupported size for unsigned integer: %d bytes", len(data))
53+
}
54+
55+
var val uint64
56+
57+
if endianness == "little" {
58+
for i := 0; i < len(data); i++ {
59+
val |= uint64(data[i]) << (8 * i)
60+
}
61+
} else {
62+
for i := 0; i < len(data); i++ {
63+
val |= uint64(data[i]) << (8 * (len(data) - 1 - i))
64+
}
65+
}
66+
67+
if len(data) <= 4 {
68+
return uint32(val), nil
69+
}
70+
71+
return val, nil
5172
}
52-
// TODO: Support non-aligned bytes less than 8?
5373
}
5474

5575
// InterpretSignedInteger interprets a byte slice as a signed integer.
5676
// The endianness parameter specifies the byte order of the data.
57-
// Size of the data must be 1, 2, 4, or 8 bytes.
77+
// Size of the data must be 1-8 bytes.
5878
func InterpretSignedInteger(data []byte, endianness string) (interface{}, error) {
5979
unsigned, err := InterpretUnsignedInteger(data, endianness)
6080
if err != nil {
@@ -67,8 +87,22 @@ func InterpretSignedInteger(data []byte, endianness string) (interface{}, error)
6787
case uint16:
6888
return int16(v), nil
6989
case uint32:
90+
if len(data) < 4 {
91+
bitLen := uint(8 * len(data))
92+
if v&(1<<(bitLen-1)) != 0 {
93+
v |= ^uint32(0) << bitLen
94+
}
95+
}
96+
7097
return int32(v), nil
7198
case uint64:
99+
if len(data) < 8 {
100+
bitLen := uint(8 * len(data))
101+
if v&(1<<(bitLen-1)) != 0 {
102+
v |= ^uint64(0) << bitLen
103+
}
104+
}
105+
72106
return int64(v), nil
73107
default:
74108
return nil, fmt.Errorf("unsupported integer type for signed conversion: %T", v)
@@ -77,7 +111,7 @@ func InterpretSignedInteger(data []byte, endianness string) (interface{}, error)
77111

78112
// InterpretFloat interprets a byte slice as a floating point number.
79113
// The endianness parameter specifies the byte order of the data.
80-
// Size of the data must be 4 or 8 bytes.
114+
// Size of the data must be 1-8 bytes.
81115
func InterpretFloat(data []byte, endianness string) (interface{}, error) {
82116
unsigned, err := InterpretUnsignedInteger(data, endianness)
83117
if err != nil {
@@ -103,9 +137,10 @@ func InterpretMeasurementValue(measurement Measurement, data []byte) (interface{
103137
switch measurement.Type {
104138
case "int":
105139
if measurement.Unsigned {
106-
return InterpretUnsignedInteger(data, measurement.Endianness)
140+
result, err = InterpretUnsignedInteger(data, measurement.Endianness)
141+
} else {
142+
result, err = InterpretSignedInteger(data, measurement.Endianness)
107143
}
108-
result, err = InterpretSignedInteger(data, measurement.Endianness)
109144
case "float":
110145
result, err = InterpretFloat(data, measurement.Endianness)
111146
default:

lib/tlm/tlm_interpreter_test.go

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ func TestInterpretUnsignedInteger(t *testing.T) {
1818
{"uint32 big endian", []byte{0x12, 0x34, 0x56, 0x78}, "big", uint32(0x12345678)},
1919
{"uint64 little endian", []byte{0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0}, "little", uint64(0xF0DEBC9A78563412)},
2020
{"uint64 big endian", []byte{0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0}, "big", uint64(0x123456789ABCDEF0)},
21-
{"unsupported length", []byte{0x12, 0x34, 0x56}, "", nil},
21+
{"unsupported length", []byte{0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF, 0xCA, 0xFE, 0xBA, 0xBE, 0xDE, 0xAD, 0xBE, 0xE5}, "", nil},
2222
}
2323

2424
for _, tt := range tests {
@@ -45,7 +45,7 @@ func TestInterpretSignedInteger(t *testing.T) {
4545
{"int32 big endian", []byte{0xFF, 0xFF, 0xFF, 0x82}, "big", int32(-126)},
4646
{"int64 little endian", []byte{0x82, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, "little", int64(-126)},
4747
{"int64 big endian", []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x82}, "big", int64(-126)},
48-
{"unsupported length", []byte{0x12, 0x34, 0x56}, "", nil},
48+
{"unsupported length", []byte{0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF, 0xCA, 0xFE, 0xBA, 0xBE, 0xDE, 0xAD, 0xBE, 0xE5}, "", nil},
4949
}
5050

5151
for _, tt := range tests {
@@ -69,7 +69,7 @@ func TestInterpretFloat(t *testing.T) {
6969
{"float32 big endian", []byte{0x3F, 0x80, 0x00, 0x00}, "big", float32(1.0)},
7070
{"float64 little endian", []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x3F}, "little", 1.0},
7171
{"float64 big endian", []byte{0x3F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, "big", 1.0},
72-
{"unsupported length", []byte{0x12, 0x34, 0x56}, "", nil},
72+
{"unsupported length", []byte{0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF, 0xCA, 0xFE, 0xBA, 0xBE, 0xDE, 0xAD, 0xBE, 0xE5}, "", nil},
7373
}
7474

7575
for _, tt := range tests {
@@ -104,3 +104,34 @@ func TestInterpretMeasurementValue(t *testing.T) {
104104
})
105105
}
106106
}
107+
108+
func TestInterpretUnalignedMeasurementValue(t *testing.T) {
109+
tests := []struct {
110+
name string
111+
measurement Measurement
112+
data []byte
113+
expected interface{}
114+
}{
115+
{"unsigned 3 byte", Measurement{Type: "int", Unsigned: true, Endianness: "big", ScalingFactor: 1.0}, []byte{0x01, 0x02, 0x03}, uint32(0x010203)},
116+
// Note the hard-coded decimal value is validated. int32ing the 3-byte value 0xDEADFF results in 14593535 and doing 0xFFDEADFF will overflow the int32 :P
117+
{"signed 3 byte", Measurement{Type: "int", Unsigned: false, Endianness: "big", ScalingFactor: 1.0}, []byte{0xDE, 0xAD, 0xFF}, int32(-2183681)},
118+
119+
{"unsigned 5 byte", Measurement{Type: "int", Unsigned: false, Endianness: "little", ScalingFactor: 1.0}, []byte{0x01, 0x02, 0x03, 0x04, 0x05}, int64(0x0504030201)},
120+
{"signed 5 byte", Measurement{Type: "int", Unsigned: true, Endianness: "little", ScalingFactor: 1.0}, []byte{0xDE, 0xAD, 0xBE, 0xEF, 0xFF}, uint64(0xFFEFBEADDE)},
121+
122+
{"unsigned 6 byte", Measurement{Type: "int", Unsigned: false, Endianness: "big", ScalingFactor: 1.0}, []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}, int64(0x010203040506)},
123+
{"signed 6 byte", Measurement{Type: "int", Unsigned: true, Endianness: "big", ScalingFactor: 1.0}, []byte{0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}, uint64(0xDEADBEEFFEED)},
124+
125+
{"unsigned 7 byte", Measurement{Type: "int", Unsigned: false, Endianness: "little", ScalingFactor: 1.0}, []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, int64(0x07060504030201)},
126+
{"signed 7 byte", Measurement{Type: "int", Unsigned: true, Endianness: "little", ScalingFactor: 1.0}, []byte{0xCA, 0xFE, 0xBA, 0xBE, 0xBB, 0xBA, 0xD0}, uint64(0xD0BABBBEBAFECA)},
127+
}
128+
129+
for _, tt := range tests {
130+
t.Run(tt.name, func(t *testing.T) {
131+
result, _ := InterpretMeasurementValue(tt.measurement, tt.data)
132+
if result != tt.expected {
133+
t.Errorf("expected %v, got %v", tt.expected, result)
134+
}
135+
})
136+
}
137+
}

0 commit comments

Comments
 (0)