package com.radiusnetworks.ibeacon.client;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import android.util.Log;
import com.radiusnetworks.ibeacon.IBeacon;
import com.radiusnetworks.ibeacon.IBeaconManager;
public class RangedIBeacon extends IBeacon{
private static String TAG = "RangedIBeacon";
public static long DEFAULT_SAMPLE_EXPIRATION_MILLISECONDS = 30000; /* 30 seconds */
private long sampleExpirationMilliseconds = DEFAULT_SAMPLE_EXPIRATION_MILLISECONDS;
public RangedIBeacon(IBeacon ibeacon) {
super(ibeacon);
addMeasurement(this.rssi);
}
public void setSampleExpirationMilliseconds(long milliseconds) {
sampleExpirationMilliseconds = milliseconds;
}
public void addMeasurement(Integer rssi) {
Measurement measurement = new Measurement();
measurement.rssi = rssi;
measurement.timestamp = new Date().getTime();
measurements.add(measurement);
}
private ArrayList<Measurement> measurements = new ArrayList<Measurement>();
public boolean allMeasurementsExpired() {
refreshMeasurements();
return measurements.size() == 0;
}
private class Measurement implements Comparable<Measurement> {
Integer rssi;
long timestamp;
@Override
public int compareTo(Measurement arg0) {
return rssi.compareTo(arg0.rssi);
}
}
private synchronized void refreshMeasurements() {
Date now = new Date();
ArrayList<Measurement> newMeasurements = new ArrayList<Measurement>();
Iterator<Measurement> iterator = measurements.iterator();
while (iterator.hasNext()) {
Measurement measurement = iterator.next();
if (now.getTime() - measurement.timestamp < sampleExpirationMilliseconds ) {
newMeasurements.add(measurement);
}
}
measurements = newMeasurements;
Collections.sort(measurements);
}
private double calculateRunningAverage() {
refreshMeasurements();
int size = measurements.size();
int startIndex = 0;
int endIndex = size -1;
if (size > 2) {
startIndex = size/10+1;
endIndex = size-size/10-2;
}
int sum = 0;
for (int i = startIndex; i <= endIndex; i++) {
sum += measurements.get(i).rssi;
}
double runningAverage = sum/(endIndex-startIndex+1);
if (IBeaconManager.debug) Log.d(TAG, "Running average rssi based on "+size+" measurements: "+runningAverage);
return runningAverage;
}
protected void addRangeMeasurement(Integer rssi) {
this.rssi = rssi;
addMeasurement(rssi);
if (IBeaconManager.debug) Log.d(TAG, "calculating new range measurement with new rssi measurement:"+rssi);
runningAverageRssi = calculateRunningAverage();
accuracy = null; // force calculation of accuracy and proximity next time they are requested
proximity = null;
}
}