Skip to content

Commit 9e9aac2

Browse files
authored
Merge pull request #5 from ssalonen/test-vectors
NA detection of signed fields corrected
2 parents 88ec719 + 5e3c85f commit 9e9aac2

File tree

4 files changed

+144
-55
lines changed

4 files changed

+144
-55
lines changed

CHANGELOG.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
# Changelog
22

3+
### v1.0.2
4+
5+
- Bug fix: detection of NA values in signed fields (temperate and acceleration) has been corrected.
6+
37
### v1.0.1
48

5-
- Added release/distribution management to the project. No functional changes in code.
9+
- Added release/distribution management to the project. No functional changes in code.
610

711
## v1.0.0
812

9-
- First release
13+
- First release

src/main/java/fi/tkgwf/ruuvi/common/parser/impl/DataFormat5Parser.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@
77

88
public class DataFormat5Parser implements DataFormatParser {
99

10-
private final int[] RUUVI_COMPANY_IDENTIFIER = {0x99, 0x04}; // 0x0499
10+
private final int[] RUUVI_COMPANY_IDENTIFIER = { 0x99, 0x04 }; // 0x0499
1111

1212
@Override
1313
public RuuviMeasurement parse(byte[] data) {
14-
if (data.length < 2 || (data[0] & 0xFF) != RUUVI_COMPANY_IDENTIFIER[0] || (data[1] & 0xFF) != RUUVI_COMPANY_IDENTIFIER[1]) {
14+
if (data.length < 2 || (data[0] & 0xFF) != RUUVI_COMPANY_IDENTIFIER[0]
15+
|| (data[1] & 0xFF) != RUUVI_COMPANY_IDENTIFIER[1]) {
1516
return null;
1617
}
1718
data = Arrays.copyOfRange(data, 2, data.length); // discard the first 2 bytes, the company identifier
@@ -21,7 +22,7 @@ public RuuviMeasurement parse(byte[] data) {
2122
RuuviMeasurement m = new RuuviMeasurement();
2223
m.setDataFormat(data[0] & 0xFF);
2324

24-
if (!ByteUtils.isMaxSignedShort(data[1], data[2])) {
25+
if (!ByteUtils.isMinSignedShort(data[1], data[2])) {
2526
m.setTemperature((data[1] << 8 | data[2] & 0xFF) / 200d);
2627
}
2728

@@ -33,13 +34,13 @@ public RuuviMeasurement parse(byte[] data) {
3334
m.setPressure((double) ((data[5] & 0xFF) << 8 | data[6] & 0xFF) + 50000);
3435
}
3536

36-
if (!ByteUtils.isMaxSignedShort(data[7], data[8])) {
37+
if (!ByteUtils.isMinSignedShort(data[7], data[8])) {
3738
m.setAccelerationX((data[7] << 8 | data[8] & 0xFF) / 1000d);
3839
}
39-
if (!ByteUtils.isMaxSignedShort(data[9], data[10])) {
40+
if (!ByteUtils.isMinSignedShort(data[9], data[10])) {
4041
m.setAccelerationY((data[9] << 8 | data[10] & 0xFF) / 1000d);
4142
}
42-
if (!ByteUtils.isMaxSignedShort(data[11], data[12])) {
43+
if (!ByteUtils.isMinSignedShort(data[11], data[12])) {
4344
m.setAccelerationZ((data[11] << 8 | data[12] & 0xFF) / 1000d);
4445
}
4546

src/main/java/fi/tkgwf/ruuvi/common/utils/ByteUtils.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,20 +31,33 @@ public static boolean isMaxUnsignedByte(byte b) {
3131
* @param b1 1st byte to check
3232
* @param b2 2nd byte to check
3333
* @return true if the pair of bytes represent the max value a signed short
34-
* can be
34+
* can be
3535
*/
3636
public static boolean isMaxSignedShort(byte b1, byte b2) {
3737
return isMaxSignedByte(b1) && isMaxUnsignedByte(b2);
3838
}
3939

40+
/**
41+
* Convenience method for checking whether the supplied bytes forming a
42+
* 16bit short is the min signed short.
43+
*
44+
* @param b1 1st byte to check
45+
* @param b2 2nd byte to check
46+
* @return true if the pair of bytes represent the min value a signed short
47+
* can be
48+
*/
49+
public static boolean isMinSignedShort(byte b1, byte b2) {
50+
return (b1 & 0xff) == 0x80 && (b2 & 0xff) == 0x00;
51+
}
52+
4053
/**
4154
* Convenience method for checking whether the supplied bytes forming a
4255
* 16bit short is the max unsigned short.
4356
*
4457
* @param b1 1st byte to check
4558
* @param b2 2nd byte to check
4659
* @return true if the pair of bytes represent the max value an unsigned
47-
* short can be
60+
* short can be
4861
*/
4962
public static boolean isMaxUnsignedShort(byte b1, byte b2) {
5063
return isMaxUnsignedByte(b1) && isMaxUnsignedByte(b2);

src/test/java/fi/tkgwf/ruuvi/common/ParserTest.java

Lines changed: 116 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,8 @@
88
import org.junit.Test;
99
import org.junit.Before;
1010

11-
import java.util.Arrays;
12-
1311
import org.apache.commons.codec.binary.Hex;
1412

15-
1613
public class ParserTest extends TestCase {
1714
private AnyDataFormatParser parser;
1815

@@ -56,9 +53,9 @@ public void setUp() {
5653
this.parser = new AnyDataFormatParser();
5754
}
5855

59-
6056
/**
61-
* test_decode_is_valid in https://github.com/ttu/ruuvitag-sensor/blob/master/tests/test_decoder.py
57+
* test_decode_is_valid in
58+
* https://github.com/ttu/ruuvitag-sensor/blob/master/tests/test_decoder.py
6259
*/
6360
@Test
6461
public void testDecodeEddystone() {
@@ -76,9 +73,9 @@ public void testDecodeEddystone() {
7673
assertNull(m.getTxPower());
7774
}
7875

79-
8076
/**
81-
* test_decode_is_valid_case in https://github.com/ttu/ruuvitag-sensor/blob/master/tests/test_decoder.py
77+
* test_decode_is_valid_case in
78+
* https://github.com/ttu/ruuvitag-sensor/blob/master/tests/test_decoder.py
8279
*/
8380
@Test
8481
public void testDecodeEddystone2() {
@@ -97,13 +94,14 @@ public void testDecodeEddystone2() {
9794
}
9895

9996
/**
100-
* test_decode_is_valid_weatherstation_2017_04_12 in https://github.com/ttu/ruuvitag-sensor/blob/master/tests/test_decoder.py
97+
* test_decode_is_valid_weatherstation_2017_04_12 in
98+
* https://github.com/ttu/ruuvitag-sensor/blob/master/tests/test_decoder.py
10199
*/
102100
@Test
103101
public void testDecodeEddystone3() {
104102
// https://ruu.vi/#AjUX1MAw0
105103
RuuviMeasurement m = parser.parse(eddystoneData("AjUX1MAw0"));
106-
// assertEquals(23.828125, m.getTemperature());
104+
// assertEquals(23.828125, m.getTemperature());
107105
assertEquals(99200.0, m.getPressure());
108106
assertEquals(26.5, m.getHumidity());
109107
assertEquals((Integer) 2, m.getDataFormat());
@@ -115,14 +113,15 @@ public void testDecodeEddystone3() {
115113
assertNull(m.getTxPower());
116114
}
117115

118-
119116
/**
120-
* test_df3decode_is_valid in https://github.com/ttu/ruuvitag-sensor/blob/master/tests/test_decoder.py
117+
* test_df3decode_is_valid in
118+
* https://github.com/ttu/ruuvitag-sensor/blob/master/tests/test_decoder.py
121119
*/
122120
@Test
123121
public void testDataFormat3() {
124122
// {format}{humidity}{temp}{pressure}{accX}{accY}{accZ}{batt}
125-
RuuviMeasurement m = parser.parse(dataWithCompany("03-29-1A1E-CE1E-FC18-F942-02CA-0B5300000000BB".replace("-", "")));
123+
RuuviMeasurement m = parser
124+
.parse(dataWithCompany("03-29-1A1E-CE1E-FC18-F942-02CA-0B5300000000BB".replace("-", "")));
126125
assertEquals(26.3, m.getTemperature());
127126
assertEquals(102766.0, m.getPressure());
128127
assertEquals(20.5, m.getHumidity());
@@ -135,7 +134,8 @@ public void testDataFormat3() {
135134
}
136135

137136
/**
138-
* test_df3decode_is_valid in https://github.com/ttu/ruuvitag-sensor/blob/master/tests/test_decoder.py
137+
* test_df3decode_is_valid in
138+
* https://github.com/ttu/ruuvitag-sensor/blob/master/tests/test_decoder.py
139139
*/
140140
@Test
141141
public void testDataFormat3Alt() {
@@ -153,12 +153,14 @@ public void testDataFormat3Alt() {
153153
}
154154

155155
/**
156-
* test_df3decode_is_valid_max_values in https://github.com/ttu/ruuvitag-sensor/blob/master/tests/test_decoder.py
156+
* test_df3decode_is_valid_max_values in
157+
* https://github.com/ttu/ruuvitag-sensor/blob/master/tests/test_decoder.py
157158
*/
158159
@Test
159160
public void testDataFormat3MaxValues() {
160161
// {format}{humidity}{temp}{pressure}{accX}{accY}{accZ}{batt}
161-
RuuviMeasurement m = parser.parse(dataWithCompany("03-C8-7F63-FFFF-03E8-03E8-03E8-FFFF-00000000BB".replace("-", "")));
162+
RuuviMeasurement m = parser
163+
.parse(dataWithCompany("03-C8-7F63-FFFF-03E8-03E8-03E8-FFFF-00000000BB".replace("-", "")));
162164
assertEquals(127.99, m.getTemperature());
163165
assertEquals(115535.0, m.getPressure());
164166
assertEquals(100.0, m.getHumidity());
@@ -173,12 +175,14 @@ public void testDataFormat3MaxValues() {
173175
}
174176

175177
/**
176-
* test_df3decode_is_valid_min_values in https://github.com/ttu/ruuvitag-sensor/blob/master/tests/test_decoder.py
178+
* test_df3decode_is_valid_min_values in
179+
* https://github.com/ttu/ruuvitag-sensor/blob/master/tests/test_decoder.py
177180
*/
178181
@Test
179182
public void testDataFormat3MinValues() {
180183
// {format}{humidity}{temp}{pressure}{accX}{accY}{accZ}{batt}
181-
RuuviMeasurement m = parser.parse(dataWithCompany("03-00-FF63-0000-FC18-FC18-FC18-0000-00000000BB".replace("-", "")));
184+
RuuviMeasurement m = parser
185+
.parse(dataWithCompany("03-00-FF63-0000-FC18-FC18-FC18-0000-00000000BB".replace("-", "")));
182186
assertEquals(-127.99, m.getTemperature());
183187
assertEquals(50000.0, m.getPressure());
184188
assertEquals(0.0, m.getHumidity());
@@ -193,12 +197,14 @@ public void testDataFormat3MinValues() {
193197
}
194198

195199
/**
196-
* test_df5decode_is_valid in https://github.com/ttu/ruuvitag-sensor/blob/master/tests/test_decoder.py
200+
* test_df5decode_is_valid in
201+
* https://github.com/ttu/ruuvitag-sensor/blob/master/tests/test_decoder.py
197202
*/
198203
@Test
199204
public void testDataFormat5() {
200205
// {format}{temp}{humidity}{pressure}{accX}{accY}{accZ}{power_info}{movement_counter}{measurement_sequence}{mac}
201-
RuuviMeasurement m = parser.parse(dataWithCompany("05-12FC-5394-C37C-0004-FFFC-040C-AC36-42-00CD-CBB8334C884F".replace("-", "")));
206+
RuuviMeasurement m = parser
207+
.parse(dataWithCompany("05-12FC-5394-C37C-0004-FFFC-040C-AC36-42-00CD-CBB8334C884F".replace("-", "")));
202208
assertEquals(24.3, m.getTemperature());
203209
assertEquals(100044.0, m.getPressure());
204210
assertEquals(53.49, m.getHumidity());
@@ -213,44 +219,109 @@ public void testDataFormat5() {
213219
}
214220

215221
/**
216-
* See https://github.com/ruuvi/ruuvi-sensor-protocols and "reference" python implementation
222+
* See https://github.com/ruuvi/ruuvi-sensor-protocols and "reference" python
223+
* implementation
217224
*/
218225
@Test
219-
public void testDataFormat5AllUnavailable() {
226+
public void testDataFormat5SomeUnavailable() {
220227
// {format}{temp}{humidity}{pressure}{accX}{accY}{accZ}{power_info}{movement_counter}{measurement_sequence}{mac}
221-
RuuviMeasurement m = parser.parse(dataWithCompany("05-7FFF-FFFF-FFFF-7FFF-7FFF-7FFF-FFFF-FF-FFFF-CBB8334C884F".replace("-", "")));
222-
assertNull( m.getTemperature());
223-
assertNull( m.getPressure());
224-
assertNull( m.getHumidity());
225-
assertNull( m.getBatteryVoltage());
226-
assertNull( m.getAccelerationX());
227-
assertNull( m.getAccelerationY());
228-
assertNull( m.getAccelerationZ());
228+
RuuviMeasurement m = parser
229+
.parse(dataWithCompany("05-12FC-FFFF-C37C-8000-8000-8000-FFFF-FF-FFFF-CBB8334C884F".replace("-", "")));
230+
assertEquals(24.3, m.getTemperature());
231+
assertEquals(100044.0, m.getPressure());
232+
assertNull(m.getHumidity());
233+
assertNull(m.getBatteryVoltage());
234+
assertNull(m.getAccelerationX());
235+
assertNull(m.getAccelerationY());
236+
assertNull(m.getAccelerationZ());
229237
assertEquals((Integer) 5, m.getDataFormat());
230-
assertNull( m.getMeasurementSequenceNumber());
231-
assertNull( m.getMovementCounter());
232-
assertNull( m.getTxPower());
238+
assertNull(m.getMeasurementSequenceNumber());
239+
assertNull(m.getMovementCounter());
240+
assertNull(m.getTxPower());
233241
}
234242

235-
236243
/**
237-
* See https://github.com/ruuvi/ruuvi-sensor-protocols and "reference" python implementation
244+
* "Valid data" official test vector
245+
* hhttps://docs.ruuvi.com/communication/bluetooth-advertisements/data-format-5-rawv2
238246
*/
239247
@Test
240-
public void testDataFormat5SomeUnavailable() {
241-
// {format}{temp}{humidity}{pressure}{accX}{accY}{accZ}{power_info}{movement_counter}{measurement_sequence}{mac}
242-
RuuviMeasurement m = parser.parse(dataWithCompany("05-12FC-FFFF-C37C-7FFF-7FFF-7FFF-FFFF-FF-FFFF-CBB8334C884F".replace("-", "")));
248+
public void testDataFormat5TestVectorValid() {
249+
RuuviMeasurement m = parser
250+
.parse(dataWithCompany("0512FC5394C37C0004FFFC040CAC364200CDCBB8334C884F"));
243251
assertEquals(24.3, m.getTemperature());
244252
assertEquals(100044.0, m.getPressure());
245-
assertNull( m.getHumidity());
246-
assertNull( m.getBatteryVoltage());
247-
assertNull( m.getAccelerationX());
248-
assertNull( m.getAccelerationY());
249-
assertNull( m.getAccelerationZ());
253+
assertEquals(53.49, m.getHumidity());
254+
assertEquals(2.9770000000000003, m.getBatteryVoltage());
255+
assertEquals(0.004, m.getAccelerationX());
256+
assertEquals(-0.004, m.getAccelerationY());
257+
assertEquals(1.036, m.getAccelerationZ());
250258
assertEquals((Integer) 5, m.getDataFormat());
251-
assertNull( m.getMeasurementSequenceNumber());
252-
assertNull( m.getMovementCounter());
253-
assertNull( m.getTxPower());
259+
assertEquals((Integer) 205, m.getMeasurementSequenceNumber());
260+
assertEquals((Integer) 66, m.getMovementCounter());
261+
assertEquals((Integer) 4, m.getTxPower());
262+
}
263+
264+
/**
265+
* "Maximum values" official test vector
266+
* https://docs.ruuvi.com/communication/bluetooth-advertisements/data-format-5-rawv2
267+
*/
268+
@Test
269+
public void testDataFormat5TestVectorMaxValues() {
270+
RuuviMeasurement m = parser
271+
.parse(dataWithCompany("057FFFFFFEFFFE7FFF7FFF7FFFFFDEFEFFFECBB8334C884F"));
272+
assertEquals(163.835, m.getTemperature());
273+
assertEquals(115534.0, m.getPressure());
274+
assertEquals(163.8350, m.getHumidity());
275+
assertEquals(3.646, m.getBatteryVoltage());
276+
assertEquals(32.767, m.getAccelerationX());
277+
assertEquals(32.767, m.getAccelerationY());
278+
assertEquals(32.767, m.getAccelerationZ());
279+
assertEquals((Integer) 5, m.getDataFormat());
280+
assertEquals((Integer) 65534, m.getMeasurementSequenceNumber());
281+
assertEquals((Integer) 254, m.getMovementCounter());
282+
assertEquals((Integer) 20, m.getTxPower());
283+
}
284+
285+
/**
286+
* "Minimum values" official test vector
287+
* https://docs.ruuvi.com/communication/bluetooth-advertisements/data-format-5-rawv2
288+
*/
289+
@Test
290+
public void testDataFormat5TestVectorMinValues() {
291+
RuuviMeasurement m = parser
292+
.parse(dataWithCompany("058001000000008001800180010000000000CBB8334C884F"));
293+
assertEquals(-163.835, m.getTemperature());
294+
assertEquals(50000.0, m.getPressure());
295+
assertEquals(0.0, m.getHumidity());
296+
assertEquals(1.6, m.getBatteryVoltage());
297+
assertEquals(-32.767, m.getAccelerationX());
298+
assertEquals(-32.767, m.getAccelerationY());
299+
assertEquals(-32.767, m.getAccelerationZ());
300+
assertEquals((Integer) 5, m.getDataFormat());
301+
assertEquals((Integer) 0, m.getMeasurementSequenceNumber());
302+
assertEquals((Integer) 0, m.getMovementCounter());
303+
assertEquals((Integer) (-40), m.getTxPower());
304+
}
305+
306+
/**
307+
* "Invalid values" official test vector
308+
* https://github.com/ruuvi/ruuvi-sensor-protocols/blob/master/dataformat_05.md
309+
*/
310+
@Test
311+
public void testDataFormat5TestVectorInvalidValues() {
312+
RuuviMeasurement m = parser
313+
.parse(dataWithCompany("058000FFFFFFFF800080008000FFFFFFFFFFFFFFFFFFFFFF"));
314+
assertNull(m.getTemperature());
315+
assertNull(m.getPressure());
316+
assertNull(m.getHumidity());
317+
assertNull(m.getBatteryVoltage());
318+
assertNull(m.getAccelerationX());
319+
assertNull(m.getAccelerationY());
320+
assertNull(m.getAccelerationZ());
321+
assertEquals((Integer) 5, m.getDataFormat());
322+
assertNull(m.getMeasurementSequenceNumber());
323+
assertNull(m.getMovementCounter());
324+
assertNull(m.getTxPower());
254325
}
255326

256327
@Test

0 commit comments

Comments
 (0)