Skip to content

Commit 35328c4

Browse files
committed
make distance calculations specific to Android model
1 parent c05082b commit 35328c4

File tree

7 files changed

+299
-0
lines changed

7 files changed

+299
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package org.altbeacon.beacon.distance;
2+
3+
import android.os.Build;
4+
5+
/**
6+
* Created by dyoung on 8/28/14.
7+
*/
8+
public class AndroidModel {
9+
String mVersion;
10+
String mBuildNumber;
11+
String mModel;
12+
String mManufacturer;
13+
14+
public AndroidModel(String version, String buildNumber,
15+
String model,
16+
String manufacturer) {
17+
mVersion = version;
18+
mBuildNumber = buildNumber;
19+
mModel = model;
20+
mManufacturer = manufacturer;
21+
22+
}
23+
public static AndroidModel forThisDevice() {
24+
return new AndroidModel(
25+
Build.VERSION.RELEASE,
26+
Build.ID,
27+
Build.MODEL,
28+
Build.MANUFACTURER);
29+
}
30+
31+
public String getVersion() {
32+
return mVersion;
33+
}
34+
35+
public void setVersion(String mVersion) {
36+
this.mVersion = mVersion;
37+
}
38+
39+
public String getBuildNumber() {
40+
return mBuildNumber;
41+
}
42+
43+
public String getModel() {
44+
return mModel;
45+
}
46+
47+
48+
public String getManufacturer() {
49+
return mManufacturer;
50+
}
51+
52+
public int matchScore(AndroidModel otherModel) {
53+
int score = 0;
54+
if (this.mManufacturer.equals(otherModel.mManufacturer)) {
55+
score = 1;
56+
}
57+
if (this.mModel.equals(otherModel.mModel)) {
58+
score = 2;
59+
}
60+
if (this.mBuildNumber.equals(otherModel.mBuildNumber)) {
61+
score = 3;
62+
}
63+
if (this.mVersion.equals(otherModel.mVersion)) {
64+
score = 4;
65+
}
66+
return score;
67+
}
68+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package org.altbeacon.beacon.distance;
2+
3+
import org.altbeacon.beacon.BeaconManager;
4+
5+
/**
6+
* Created by dyoung on 8/28/14.
7+
*/
8+
public class CurveFittedDistanceCalculator implements DistanceCalculator {
9+
10+
public static final String TAG = "CurveFittedDistanceCalculator";
11+
private double mCoefficient1;
12+
private double mCoefficient2;
13+
private double mCoefficient3;
14+
15+
public CurveFittedDistanceCalculator(double coefficient1, double coefficient2, double coefficient3) {
16+
mCoefficient1 = coefficient1;
17+
mCoefficient2 = coefficient2;
18+
mCoefficient3 = coefficient3;
19+
}
20+
21+
/**
22+
* Calculated the estimated distance in meters to the beacon based on a reference rssi at 1m
23+
* and the known actual rssi at the current location
24+
* @param txPower
25+
* @param rssi
26+
* @return estimated distance
27+
*/
28+
@Override
29+
public double calculateDistance(int txPower, double rssi) {
30+
if (rssi == 0) {
31+
return -1.0; // if we cannot determine accuracy, return -1.
32+
}
33+
34+
BeaconManager.logDebug(TAG, "calculating distance based on mRssi of " + rssi + " and txPower of " + txPower);
35+
36+
37+
double ratio = rssi*1.0/txPower;
38+
double distance;
39+
if (ratio < 1.0) {
40+
distance = Math.pow(ratio,10);
41+
}
42+
else {
43+
distance = (0.42093)*Math.pow(ratio,6.9476) + 0.54992;
44+
}
45+
BeaconManager.logDebug(TAG, " avg mRssi: "+rssi+" distance: "+distance);
46+
return distance;
47+
}
48+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package org.altbeacon.beacon.distance;
2+
3+
/**
4+
* Created by dyoung on 8/28/14.
5+
*/
6+
public interface DistanceCalculator {
7+
public double calculateDistance(int txPower, double rssi);
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package org.altbeacon.beacon.distance;
2+
3+
import android.os.Build;
4+
import android.util.Log;
5+
6+
import org.json.JSONArray;
7+
import org.json.JSONObject;
8+
9+
import java.io.BufferedReader;
10+
import java.io.IOException;
11+
import java.io.InputStream;
12+
import java.io.InputStreamReader;
13+
import java.util.HashMap;
14+
import java.util.Map;
15+
import java.util.Properties;
16+
17+
/**
18+
* Created by dyoung on 8/28/14.
19+
*/
20+
public class ModelSpecificDistanceCalculator implements DistanceCalculator {
21+
Map<AndroidModel,DistanceCalculator> mModelMap;
22+
//private static final String CONFIG_FILE = "/model-distance-calculations.json";
23+
private static final String CONFIG_FILE = "test.properties";
24+
private static final String TAG = "ModelSpecificDistanceCalculator";
25+
private DistanceCalculator mDefaultDistanceCalculator;
26+
private DistanceCalculator mDistanceCalculator;
27+
28+
public ModelSpecificDistanceCalculator() {
29+
this(AndroidModel.forThisDevice());
30+
}
31+
public ModelSpecificDistanceCalculator(AndroidModel model) {
32+
loadModelMap();
33+
mDistanceCalculator = findCalculatorForModel(model);
34+
}
35+
36+
private DistanceCalculator findCalculatorForModel(AndroidModel model) {
37+
Log.d(TAG, "Finding best distance calculator for "+model.getVersion()+","+
38+
model.getBuildNumber()+","+model.getModel()+"," +
39+
""+model.getManufacturer());
40+
41+
int highestScore = 0;
42+
AndroidModel bestMatchingModel = null;
43+
for (AndroidModel candidateModel : mModelMap.keySet()) {
44+
if (candidateModel.matchScore(model) > highestScore) {
45+
highestScore = candidateModel.matchScore(model);
46+
bestMatchingModel = candidateModel;
47+
}
48+
}
49+
if (bestMatchingModel != null) {
50+
Log.d(TAG, "found a match with score "+highestScore);
51+
Log.d(TAG, "Finding best distance calculator for "+bestMatchingModel.getVersion()+","+
52+
bestMatchingModel.getBuildNumber()+","+bestMatchingModel.getModel()+"," +
53+
""+bestMatchingModel.getManufacturer());
54+
return mModelMap.get(bestMatchingModel);
55+
}
56+
57+
Log.d(TAG, "Cannot find match for this device. Using default");
58+
return mDefaultDistanceCalculator;
59+
}
60+
61+
private void loadModelMap() {
62+
mModelMap = new HashMap<AndroidModel, DistanceCalculator>();
63+
try {
64+
JSONObject jsonObject = new JSONObject(stringFromFilePath(CONFIG_FILE));
65+
JSONArray array = jsonObject.getJSONArray("models");
66+
for (int i = 0; i < array.length(); i++) {
67+
JSONObject modelObject = array.getJSONObject(i);
68+
boolean defaultFlag = modelObject.getBoolean("default");
69+
Double coefficient1 = modelObject.getDouble("coefficient1");
70+
Double coefficient2 = modelObject.getDouble("coefficient2");
71+
Double coefficient3 = modelObject.getDouble("coefficient3");
72+
String version = modelObject.getString("version");
73+
String buildNumber = modelObject.getString("build_number");
74+
String model = modelObject.getString("model");
75+
String manufacturer = modelObject.getString("manufacturer");
76+
77+
CurveFittedDistanceCalculator distanceCalculator =
78+
new CurveFittedDistanceCalculator(coefficient1,coefficient2,coefficient3);
79+
80+
AndroidModel androidModel = new AndroidModel(version, buildNumber, model, manufacturer);
81+
mModelMap.put(androidModel, distanceCalculator);
82+
if (defaultFlag) {
83+
mDefaultDistanceCalculator = distanceCalculator;
84+
}
85+
}
86+
}
87+
catch (Exception e) {
88+
throw new RuntimeException("Cannot build model distance calculations", e);
89+
}
90+
}
91+
92+
private String stringFromFilePath(String path) throws IOException {
93+
InputStream stream = ModelSpecificDistanceCalculator.class.getResourceAsStream(path);
94+
if (stream == null) {
95+
throw new RuntimeException("Cannot load resource at "+path);
96+
}
97+
StringBuilder inputStringBuilder = new StringBuilder();
98+
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(stream, "UTF-8"));
99+
String line = bufferedReader.readLine();
100+
while(line != null){
101+
inputStringBuilder.append(line);inputStringBuilder.append('\n');
102+
line = bufferedReader.readLine();
103+
}
104+
return inputStringBuilder.toString();
105+
}
106+
107+
@Override
108+
public double calculateDistance(int txPower, double rssi) {
109+
return mDistanceCalculator.calculateDistance(txPower, rssi);
110+
}
111+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"models":
3+
[
4+
{
5+
"coefficient1": 0.42093,
6+
"coefficient2": 6.9476,
7+
"coefficient3": 0.54992,
8+
"version":"4.4.2",
9+
"build_number":"KOT49H",
10+
"model":"Nexus 4",
11+
"manufacturer":"LGE",
12+
"default": true
13+
},
14+
{
15+
"coefficient1": 0.42093,
16+
"coefficient2": 6.9476,
17+
"coefficient3": 0.54992,
18+
"version":"4.4.2",
19+
"build_number":"LPV79",
20+
"model":"Nexus 5",
21+
"manufacturer":"LGE",
22+
}
23+
]
24+
}

src/main/resources/test.properties

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package org.altbeacon.beacon.distance;
2+
3+
import android.os.Parcel;
4+
5+
import static android.test.MoreAsserts.assertNotEqual;
6+
import static org.junit.Assert.assertEquals;
7+
import static org.junit.Assert.assertNotNull;
8+
import static org.junit.Assert.assertTrue;
9+
10+
import org.altbeacon.beacon.AltBeacon;
11+
import org.altbeacon.beacon.AltBeaconParser;
12+
import org.altbeacon.beacon.Beacon;
13+
import org.robolectric.RobolectricTestRunner;
14+
15+
import org.junit.runner.RunWith;
16+
import org.junit.Test;
17+
import org.robolectric.annotation.Config;
18+
19+
@Config(emulateSdk = 18)
20+
@RunWith(RobolectricTestRunner.class)
21+
/*
22+
HOW TO SEE DEBUG LINES FROM YOUR UNIT TESTS:
23+
24+
1. set a line like this at the start of your test:
25+
org.robolectric.shadows.ShadowLog.stream = System.err;
26+
2. run the tests from the command line
27+
3. Look at the test report file in your web browser, e.g.
28+
file:///Users/dyoung/workspace/AndroidProximityLibrary/build/reports/tests/index.html
29+
4. Expand the System.err section
30+
/**
31+
* Created by dyoung on 8/28/14.
32+
*/
33+
public class ModelSpecificDistanceCalculatorTest {
34+
@Test
35+
public void testRecognizeBeacon() {
36+
ModelSpecificDistanceCalculator distanceCalculator = new ModelSpecificDistanceCalculator();
37+
Double distance = distanceCalculator.calculateDistance(-59, -59);
38+
assertEquals("Distance should be 1.0 for same power and rssi", 1.0, (double) distance, 0.001);
39+
}
40+
}

0 commit comments

Comments
 (0)