J'ai une application iOS simple qui affiche la proximité des balises Bluetooth LE qu'elle détecte en utilisant des expressions telles que "immédiate", "proche", etc. et j'ai besoin d'écrire quelque chose de similaire sur Android.
J'ai suivi le tutoriel sur développeur Android et je suis capable de lister les appareils détectés et je veux maintenant estimer la distance/proximité - c'est là que c'est devenu un problème. Selon this SO thread c'est juste une poignée de calculs mathématiques. Cependant, ils me demandent de fournir une valeur txPower.
Selon ce tutoriel de Dave Smith (et les références croisées avec cela instruction Bluetooth SIG ), il devrait être diffusé par les dispositifs de balise comme une "structure AD" de type 0x0A
. Donc, ce que je fais, c'est analyser les structures AD et rechercher la charge utile de celle qui correspond au type.
Problème: J'ai 4 balises - 2 estimotes et 2 appflares. Les estimations ne diffusent pas du tout le txPower et les appflares diffusent le leur comme 0.
Y a-t-il quelque chose qui me manque ici? L'application iOS semble gérer tout cela sans aucun problème, mais en utilisant le SDK iOS, elle le fait en arrière-plan, donc je ne sais pas comment produire exactement le même comportement ou un comportement similaire. Existe-t-il un autre moyen de résoudre mon problème?
Au cas où vous voudriez jeter un œil au code que j'utilise pour analyser les structures AD, il est tiré du github de Dave Smith susmentionné et peut être trouvé ici . La seule modification que j'ai apportée à cette classe a été l'ajout de la méthode suivante:
public byte[] getData() {
return mData;
}
Et voici comment je gère le rappel des analyses:
// Prepare the callback for BLE device scan
this.leScanCallback = new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
if (!deviceList.contains(device)) {
MyService.this.deviceList.add(device);
Log.e("Test", "Device: " + device.getName());
List<AdRecord> adRecords = AdRecord.parseScanRecord(scanRecord);
for (AdRecord adRecord : adRecords) {
if (adRecord.getType() == AdRecord.TYPE_TRANSMITPOWER) {
Log.e("Test", "size of payload: " + adRecord.getData().length);
Log.e("Test", "payload: " + Byte.toString(adRecord.getData()[0]));
}
}
}
}
};
Et ce que je vois dans la console est:
04-01 11:33:35.864: E/Test(15061): Device: estimote
04-01 11:33:36.304: E/Test(15061): Device: estimote
04-01 11:33:36.475: E/Test(15061): Device: n86
04-01 11:33:36.475: E/Test(15061): size of payload: 1
04-01 11:33:36.475: E/Test(15061): payload: 0
04-01 11:33:36.525: E/Test(15061): Device: f79
04-01 11:33:36.525: E/Test(15061): size of payload: 1
04-01 11:33:36.525: E/Test(15061): payload: 0
Il est difficile de savoir si votre incapacité à lire la constante d'étalonnage "txPower" ou "mesuréePower" est due à la classe AdRecord
ou aux informations manquantes dans les publicités que vous essayez d'analyser. Il ne me semble pas que cette classe analysera une publicité iBeacon standard. Dans les deux cas, il existe une solution:
SOLUTION 1: Si vos balises envoient une publicité iBeacon standard qui inclut la constante d'étalonnage, vous pouvez l'analyser en utilisant du code dans l'open source Android iBeacon Library la classe IBeacon ici.
SOLUTION 2: Si vos balises N'ENVOYENT PAS de publicité iBeacon standard ou n'incluent pas de constante d'étalonnage:
Vous devez coder en dur une constante d'étalonnage dans votre application pour chaque type d'appareil que vous pourriez utiliser. Tout ce dont vous avez vraiment besoin de la publicité pour estimer la distance est la mesure RSSI. L'intérêt de l'intégration d'une constante d'étalonnage dans la transmission est de permettre à une grande variété de balises avec une puissance de sortie d'émetteur assez différente de fonctionner avec le même algorithme d'estimation de distance.
La constante d'étalonnage, telle que définie par Apple, indique essentiellement ce que devrait être le RSSI si votre appareil est exactement à un mètre de la balise. Si le signal est plus fort (RSSI moins négatif), alors l'appareil est à moins d'un mètre. Si le signal est plus faible (RSSI plus négatif), alors l'appareil est à plus d'un mètre. Vous pouvez utiliser une formule pour faire une estimation numérique de la distance. Voir ici.
Si vous ne traitez pas avec des publicités contenant une constante d'étalonnage "txPower" ou " Vous devrez d'abord mesurer le RSSI moyen de chaque émetteur à un mètre de distance. Vous aurez ensuite besoin d'une sorte de clé pour rechercher ces constantes d'étalonnage dans le tableau. (Peut-être pouvez-vous utiliser une partie de la chaîne de la structure AD ou l'adresse mac?) Ainsi, votre table pourrait ressembler à ceci:
HashMap<String,Integer> txPowerLookupTable = new HashMap<String,Integer>();
txPowerLookupTable.put("a5:09:37:78:c3:22", new Integer(-65));
txPowerLookupTable.put("d2:32:33:5c:87:09", new Integer(-78));
Ensuite, après avoir analysé une annonce, vous pouvez rechercher la constante d'étalonnage dans votre méthode onLeScan
comme ceci:
String macAddress = device.getAddress();
Integer txPower = txPowerLookupTable.get(macAddress);
Le txPower
mentionné par @davidgyoung est donné par la formule:
RSSI = -10 n log d + A
où
d
= distanceA
= txPowern
= constante de propagation du signalRSSI
= dBmDans l'espace libre n = 2
, mais il variera en fonction de la géométrie locale - par exemple, un mur réduira RSSI
de ~3dBm
et affectera n
en conséquence.
Si vous souhaitez la précision la plus élevée possible, il peut être utile de déterminer expérimentalement ces valeurs pour votre système particulier.
Référence: voir l'article Évaluation de la fiabilité du RSSI pour la localisation en intérieur par Qian Dong et Waltenegus Dargie pour une explication plus détaillée de la dérivation et de l'étalonnage.
double getDistance(int rssi, int txPower) {
/*
* RSSI = TxPower - 10 * n * lg(d)
* n = 2 (in free space)
*
* d = 10 ^ ((TxPower - RSSI) / (10 * n))
*/
return Math.pow(10d, ((double) txPower - rssi) / (10 * 2));
}