Оценка близости/расстояния маяка на основе RSSI - Bluetooth LE

У меня есть простое приложение для iOS, которое отображает близость маяков Bluetooth LE, которые он обнаруживает, используя такие выражения, как "немедленный", "близкий" и т.д., и мне нужно написать что-то подобное на Android.

Я следил за учебником в разработчике Android, и я могу перечислить обнаруженные устройства и теперь хочу оценить расстояние/близость - вот где это становится проблемой. Согласно эта SO-нить, это всего лишь несколько математических вычислений. Однако они требуют, чтобы я предоставлял значение txPower.

В соответствии с этот учебник от Dave Smith (и перекрестная ссылка с этим Инструкция Bluetooth SIG), он должен быть передан маяковыми устройствами как "структура AD" типа 0x0A. Итак, что я делаю, это проанализировать структуры AD и искать полезную нагрузку той, которая соответствует типу.

Проблема: У меня есть 4 маяка - 2 оценки и 2 приложения. Оценки вообще не транслируют txPower, и приложени передают их как 0.

Есть ли что-то, что мне не хватает здесь? Кажется, что приложение iOS обрабатывает все без проблем, но с помощью SDK iOS он делает это за кулисами, поэтому я не уверен, как производить то же самое или подобное поведение. Есть ли другой способ решить мою проблему?

Если вы хотите взглянуть на код, который я использую для анализа структур AD, он взят из вышеупомянутого гитаба Dave Smith и может быть найден . Единственное изменение, которое я сделал для этого класса, это добавить следующий метод:

public byte[] getData() {

    return mData;
}

И вот как я обрабатываю обратный вызов из сканирования:

// 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]));
                }
            }
        }
    }
};

И что я вижу в консоли:

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

Ответы

Ответ 1

Неясно, является ли ваша неспособность прочитать константу калибровки "txPower" или "измеряемая мощность" из-за класса AdRecord или из-за отсутствия информации из рекламных объявлений, которые вы пытаетесь проанализировать. Мне не кажется, что этот класс будет анализировать стандартную рекламу iBeacon. В любом случае, есть решение:

РЕШЕНИЕ 1: Если ваши маяки отправляют стандартную рекламу iBeacon, которая включает в себя константу калибровки, вы можете проанализировать ее с помощью кода в открытом источнике Android iBeacon Library Класс IBeacon здесь.

РЕШЕНИЕ 2: Если ваши маяки НЕ отправляют стандартную рекламу iBeacon или не включают константу калибровки:

Вы должны жестко закодировать константу калибровки в своем приложении для каждого типа устройства, которое вы можете использовать. Все, что вам действительно нужно от рекламы для оценки расстояния, - это измерение RSSI. Вся цель встраивания калибровочной константы в передачу заключается в том, чтобы позволить широкому спектру маяков с совершенно другой выходной мощностью передатчика работать с тем же алгоритмом оценки расстояния.

Калибровочная константа, определенная Apple, в основном говорит, что должен быть RSSI, если ваше устройство находится на расстоянии одного метра от маяка. Если сигнал сильнее (менее отрицательный RSSI), то устройство находится на расстоянии менее одного метра. Если сигнал слабее (более отрицательный RSSI), то устройство находится на расстоянии более одного метра. Вы можете использовать формулу для вычисления числовой оценки расстояния. Смотрите здесь.

Если вы не имеете рекламы, содержащей константу калибровки "txPower" или "measuredPower", вы можете жестко закодировать таблицу поиска в своем приложении, в которой хранятся известные константы калибровки для разных передатчиков. Сначала вам нужно будет измерить средний RSSI каждого передатчика на расстоянии одного метра. Затем вам понадобится какой-то ключ для поиска этих калибровочных констант в таблице. (Возможно, вы можете использовать некоторую часть строки из структуры AD или MAC-адрес?) Таким образом, ваша таблица может выглядеть так:

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));

Затем после разбора рекламы вы можете найти калибровочную константу в методе onLeScan следующим образом:

String macAddress = device.getAddress();
Integer txPower = txPowerLookupTable.get(macAddress);

Ответ 2

Упомянутая txPower txPower определяется по формуле:

RSSI = -10 n log d + A

где

  • d= расстояние
  • A= txPower
  • n= постоянная распространения сигнала
  • RSSI= дБм

В свободном пространстве n = 2, но оно будет варьироваться в зависимости от локальной геометрии - например, стена уменьшит RSSI на ~3dBm и соответственно повлияет на n.

Если вы хотите максимально возможной точности, возможно, стоит экспериментально определить эти значения для вашей конкретной системы.

Для справки: см. Статью " Оценка надежности RSSI для локализации в помещении " Цянь Донга и Вальтенегуса Дарги для более подробного объяснения происхождения и калибровки.

Ответ 3

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));
}

Ответ 4

Ответить на ответ sergey.n,

в строке 9: вернуть Math.pow(10d, ((double) txPower - rssi)/(10 * 2));

Для чего нужен 10d?

Это должно быть

return Math.pow(10, ((double) txPower - rssi)/(10 * 2)));

Учитывая формулу: Расстояние = 10 ^ ((Измеренная мощность - RSSI)/(10 * N))

Поправь меня, если я ошибаюсь.

Ответ 5

используйте метод getAccuracy() в библиотеке, он дает расстояние от маяка