Skip to content

Commit

Permalink
Merge pull request #108 from c19x/feature-81
Browse files Browse the repository at this point in the history
Unit tests - Validated
Great work! Part of #81 . Fixes #109 
Signed-off-by: Adam Fowler <[email protected]>
  • Loading branch information
adamfowleruk authored Dec 14, 2020
2 parents b08c291 + 667f374 commit 41089ef
Show file tree
Hide file tree
Showing 46 changed files with 1,790 additions and 115 deletions.
15 changes: 12 additions & 3 deletions .idea/codeStyles/Project.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions app/src/main/java/com/vmware/herald/app/AppDelegate.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ private int identifier() {
@Override
public void onCreate() {
super.onCreate();

appDelegate = this;
// Initialise sensor array for given payload data supplier
final PayloadDataSupplier payloadDataSupplier = new SonarPayloadDataSupplier(identifier());
Expand Down
10 changes: 7 additions & 3 deletions herald/src/main/java/com/vmware/herald/sensor/SensorArray.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,20 @@
import android.content.Intent;
import android.os.Build;

import com.vmware.herald.sensor.ble.BLESensorConfiguration;
import com.vmware.herald.sensor.ble.ConcreteBLESensor;
import com.vmware.herald.sensor.data.BatteryLog;
import com.vmware.herald.sensor.data.ConcreteSensorLogger;
import com.vmware.herald.sensor.data.ContactLog;
import com.vmware.herald.sensor.data.DetectionLog;
import com.vmware.herald.sensor.data.EventTimeIntervalLog;
import com.vmware.herald.sensor.data.SensorLogger;
import com.vmware.herald.sensor.data.StatisticsDidReadLog;
import com.vmware.herald.sensor.data.StatisticsLog;
import com.vmware.herald.sensor.datatype.Data;
import com.vmware.herald.sensor.datatype.PayloadData;
import com.vmware.herald.sensor.datatype.PayloadTimestamp;
import com.vmware.herald.sensor.datatype.TargetIdentifier;
import com.vmware.herald.sensor.datatype.TimeInterval;
import com.vmware.herald.sensor.service.ForegroundService;

import java.util.ArrayList;
Expand All @@ -36,7 +38,7 @@ public class SensorArray implements Sensor {

private final ConcreteBLESensor concreteBleSensor;

public SensorArray(Context context, PayloadDataSupplier payloadDataSupplier) {
public SensorArray(final Context context, PayloadDataSupplier payloadDataSupplier) {
this.context = context;
// Ensure logger has been initialised (should have happened in AppDelegate already)
ConcreteSensorLogger.context(context);
Expand All @@ -59,9 +61,11 @@ public SensorArray(Context context, PayloadDataSupplier payloadDataSupplier) {
if (com.vmware.herald.BuildConfig.DEBUG) {
add(new ContactLog(context, "contacts.csv"));
add(new StatisticsLog(context, "statistics.csv", payloadData));
add(new StatisticsDidReadLog(context, "statistics_didRead.csv", payloadData));
add(new DetectionLog(context,"detection.csv", payloadData));
new BatteryLog(context, "battery.csv");
if (BLESensorConfiguration.payloadDataUpdateTimeInterval != TimeInterval.never) {
add(new EventTimeIntervalLog(context, "statistics_didRead.csv", payloadData, EventTimeIntervalLog.EventType.read));
}
}
logger.info("DEVICE (payload={},description={})", payloadData.shortName(), deviceDescription);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import android.bluetooth.BluetoothGattCharacteristic;

import com.vmware.herald.sensor.data.SensorLoggerLevel;
import com.vmware.herald.sensor.datatype.RandomSource;
import com.vmware.herald.sensor.datatype.TimeInterval;

import java.util.UUID;
Expand Down Expand Up @@ -102,6 +103,12 @@ public class BLESensorConfiguration {
/// Advert refresh time interval
public static TimeInterval advertRefreshTimeInterval = TimeInterval.minutes(15);

/// Randomisation method for generating the pseudo device addresses, see PseudoDeviceAddress and RandomSource for details.
/// - Set to Random for reliable continuous operation, validated
/// - Other methods will cause blocking after 4-8 hours and interrupt operation on idle devices
/// - Blocking can also occur at app initialisation, advert refresh, and also impact system services
public static RandomSource.Method pseudoDeviceAddressRandomisation = RandomSource.Method.Random;

/// Interrogate standard Bluetooth services to obtain device make/model data
public static boolean deviceIntrospectionEnabled = false;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
import android.content.Context;

import com.vmware.herald.sensor.DefaultSensorDelegate;
import com.vmware.herald.sensor.datatype.Location;
import com.vmware.herald.sensor.datatype.PayloadData;
import com.vmware.herald.sensor.analysis.Sample;
import com.vmware.herald.sensor.datatype.Proximity;
import com.vmware.herald.sensor.datatype.SensorType;
import com.vmware.herald.sensor.datatype.TargetIdentifier;

Expand All @@ -19,16 +21,22 @@
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/// CSV log of didRead calls for post event analysis and visualisation
public class StatisticsDidReadLog extends DefaultSensorDelegate {
/// CSV log of event time intervals for post event analysis and visualisation
public class EventTimeIntervalLog extends DefaultSensorDelegate {
private final TextFile textFile;
private final PayloadData payloadData;
private final EventType eventType;
private final Map<TargetIdentifier, String> targetIdentifierToPayload = new ConcurrentHashMap<>();
private final Map<String, Date> payloadToTime = new ConcurrentHashMap<>();
private final Map<String, Sample> payloadToSample = new ConcurrentHashMap<>();
public enum EventType {
detect,read,measure,share,sharedPeer,visit
}

public StatisticsDidReadLog(final Context context, final String filename, final PayloadData payloadData) {
textFile = new TextFile(context, filename);
public EventTimeIntervalLog(final Context context, final String filename, final PayloadData payloadData, final EventType eventType) {
this.textFile = new TextFile(context, filename);
this.payloadData = payloadData;
this.eventType = eventType;
}

private String csv(String value) {
Expand All @@ -50,8 +58,10 @@ private void add(String payload) {
}

private void write() {
final StringBuilder content = new StringBuilder("payload,count,mean,sd,min,max\n");
final StringBuilder content = new StringBuilder("event,central,peripheral,count,mean,sd,min,max\n");
final List<String> payloadList = new ArrayList<>();
final String event = csv(eventType.name());
final String centralPayload = csv(payloadData.shortName());
for (String payload : payloadToSample.keySet()) {
if (payload.equals(payloadData.shortName())) {
continue;
Expand All @@ -67,6 +77,10 @@ private void write() {
if (sample.mean() == null || sample.standardDeviation() == null || sample.min() == null || sample.max() == null) {
continue;
}
content.append(event);
content.append(',');
content.append(centralPayload);
content.append(',');
content.append(csv(payload));
content.append(',');
content.append(sample.count());
Expand All @@ -88,13 +102,62 @@ private void write() {

@Override
public void sensor(SensorType sensor, PayloadData didRead, TargetIdentifier fromTarget) {
add(didRead.shortName());
final String payload = didRead.shortName();
targetIdentifierToPayload.put(fromTarget, payload);
if (eventType == EventType.read) {
add(payload);
}
}

@Override
public void sensor(SensorType sensor, TargetIdentifier didDetect) {
if (eventType == EventType.detect) {
final String payload = targetIdentifierToPayload.get(didDetect);
if (payload == null) {
return;
}
add(payload);
}
}

@Override
public void sensor(SensorType sensor, Proximity didMeasure, TargetIdentifier fromTarget) {
if (eventType == EventType.measure) {
final String payload = targetIdentifierToPayload.get(fromTarget);
if (payload == null) {
return;
}
add(payload);
}
}

@Override
public void sensor(SensorType sensor, List<PayloadData> didShare, TargetIdentifier fromTarget) {
for (PayloadData payload : didShare) {
add(payload.shortName());
if (eventType == EventType.share) {
final String payload = targetIdentifierToPayload.get(fromTarget);
if (payload == null) {
return;
}
add(payload);
} else if (eventType == EventType.sharedPeer) {
for (final PayloadData sharedPeer : didShare) {
final String payload = sharedPeer.shortName();
if (payload == null) {
return;
}
add(payload);
}
}
}

@Override
public void sensor(SensorType sensor, Location didVisit) {
if (eventType == EventType.visit) {
final String payload = payloadData.shortName();
if (payload == null) {
return;
}
add(payload);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,7 @@ public class Base64 {
3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30,
31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
48, 49, 50, 51};

public static String encode(byte[] data) {
final StringBuilder buffer = new StringBuilder();
Expand Down Expand Up @@ -63,7 +56,7 @@ public static byte[] decode(String data) {
final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
for (int i = 0; i < bytes.length; ) {
int b = 0;
if (decodeTable[bytes[i]] != -1) {
if (bytes[i] >=0 && bytes[i] < decodeTable.length && decodeTable[bytes[i]] != -1) {
b = (decodeTable[bytes[i]] & 0xFF) << 18;
} else {
// skip unknown characters
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

import androidx.annotation.NonNull;

import java.util.Objects;

/// Calibration data for interpreting proximity value between sensor and target, e.g. Transmit power for BLE.
public class Calibration {
/// Unit of measurement, e.g. transmit power
Expand All @@ -23,6 +25,20 @@ public String description() {
return unit + ":" + value;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Calibration that = (Calibration) o;
return unit == that.unit &&
Objects.equals(value, that.value);
}

@Override
public int hashCode() {
return Objects.hash(unit, value);
}

@NonNull
@Override
public String toString() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public String description() {

/// Get subdata from offset to end
public Data subdata(int offset) {
if (offset < value.length) {
if (offset >=0 && offset < value.length) {
final byte[] offsetValue = new byte[value.length - offset];
System.arraycopy(value, offset, offsetValue, 0, offsetValue.length);
return new Data(offsetValue);
Expand All @@ -80,7 +80,7 @@ public Data subdata(int offset) {

/// Get subdata from offset to offset + length
public Data subdata(int offset, int length) {
if (offset + length <= value.length) {
if (offset >= 0 && offset < value.length && offset + length <= value.length) {
final byte[] offsetValue = new byte[length];
System.arraycopy(value, offset, offsetValue, 0, length);
return new Data(offsetValue);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,37 +25,45 @@ public Encounter(Proximity didMeasure, PayloadData withPayload) {
}

public Encounter(String row) {
final String[] fields = row.split(",");
final String[] fields = row.split(",", -1);
if (!(fields.length >= 6)) {
return;
}
try {
this.timestamp = dateFormatter.parse(fields[0]);
if (fields[0] != null && !fields[0].isEmpty()) {
this.timestamp = dateFormatter.parse(fields[0]);
}
} catch (Throwable e) {
}
Calibration calibration = null;
try {
final double calibrationValue = Double.parseDouble(fields[3]);
final CalibrationMeasurementUnit calibrationUnit = CalibrationMeasurementUnit.valueOf(fields[4]);
calibration = new Calibration(calibrationUnit, calibrationValue);
if (fields[3] != null && fields[4] != null && !fields[3].isEmpty() && !fields[4].isEmpty()) {
final double calibrationValue = Double.parseDouble(fields[3]);
final CalibrationMeasurementUnit calibrationUnit = CalibrationMeasurementUnit.valueOf(fields[4]);
calibration = new Calibration(calibrationUnit, calibrationValue);
}
} catch (Throwable e) {
}
try {
final double proximityValue = Double.parseDouble(fields[1]);
final ProximityMeasurementUnit proximityUnit = ProximityMeasurementUnit.valueOf(fields[2]);
this.proximity = new Proximity(proximityUnit, proximityValue, calibration);
if (fields[1] != null && fields[2] != null && !fields[1].isEmpty() && !fields[2].isEmpty()) {
final double proximityValue = Double.parseDouble(fields[1]);
final ProximityMeasurementUnit proximityUnit = ProximityMeasurementUnit.valueOf(fields[2]);
this.proximity = new Proximity(proximityUnit, proximityValue, calibration);
}
} catch (Throwable e) {
}
this.payload = new PayloadData(fields[5]);
if (fields[5] != null) {
this.payload = new PayloadData(fields[5]);
}
}

public String csvString() {
final String f0 = dateFormatter.format(timestamp);
final String f1 = proximity.value.toString();
final String f2 = proximity.unit.name();
final String f3 = (proximity.calibration == null || proximity.calibration.value == null ? "" : proximity.calibration.value.toString());
final String f4 = (proximity.calibration == null || proximity.calibration.unit == null ? "" : proximity.calibration.unit.name());
final String f5 = payload.base64EncodedString();
final String f0 = (timestamp == null ? "" : dateFormatter.format(timestamp));
final String f1 = (proximity == null || proximity.value == null ? "" : proximity.value.toString());
final String f2 = (proximity == null || proximity.unit == null ? "" : proximity.unit.name());
final String f3 = (proximity == null || proximity.calibration == null || proximity.calibration.value == null ? "" : proximity.calibration.value.toString());
final String f4 = (proximity == null || proximity.calibration == null || proximity.calibration.unit == null ? "" : proximity.calibration.unit.name());
final String f5 = (payload == null ? "" : payload.base64EncodedString());
return f0 + "," + f1 + "," + f2 + "," + f3 + "," + f4 + "," + f5;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ public Float16(float value) {
this.bigEndian = new Data(byteBuffer.array());
}

public Float16(Data bigEndian) {
this.bigEndian = bigEndian;
final ByteBuffer byteBuffer = ByteBuffer.wrap(bigEndian.value);
byteBuffer.order(ByteOrder.BIG_ENDIAN);
final int value = byteBuffer.getShort(0);
this.value = valueOf(value);
}

/// Float value to Float16 where the last 16 bits of integer value contains Float16
private static int float16(float values) {
final int bits = Float.floatToIntBits(values);
Expand All @@ -43,4 +51,25 @@ private static int float16(float values) {
val = (bits & 0x7fffffff) >>> 23;
return sign | ((bits & 0x7fffff | 0x800000) + (0x800000 >>> val - 102) >>> 126 - val);
}

private static float valueOf(int hbits) {
int mant = hbits & 0x03ff;
int exp = hbits & 0x7c00;
if (exp == 0x7c00) {
exp = 0x3fc00;
} else if (exp != 0) {
exp += 0x1c000;
if (mant == 0 && exp > 0x1c400) {
return Float.intBitsToFloat((hbits & 0x8000) << 16 | exp << 13 | 0x3ff);
}
} else if (mant != 0) {
exp = 0x1c400;
do {
mant <<= 1;
exp -= 0x400;
} while ((mant & 0x400) == 0);
mant &= 0x3ff;
}
return Float.intBitsToFloat((hbits & 0x8000) << 16 | (exp | mant) << 13);
}
}
Loading

0 comments on commit 41089ef

Please sign in to comment.