Bluetooth с низким энергопотреблением, как проанализировать значение интервала R-R?

Мое приложение получает информацию от умного сердечного устройства. Теперь я могу видеть значение пульса. Не могли бы вы помочь мне проанализировать значение интервала R-R? Как я могу проверить, поддерживает ли устройство значение интервала R-R или нет?

Любой совет от вас

Спасибо


person Thuong Tran    schedule 02.07.2013    source источник


Ответы (5)


Вы проверили спецификацию Bluetooth? Приведенный ниже пример кода написан на C#, но я думаю, что он показывает, как анализировать данные в каждом пакете частоты сердечных сокращений.

//first byte of heart rate record denotes flags
byte flags = heartRateRecord[0];

ushort offset = 1;

bool HRC2 = (flags & 1) == 1;
if (HRC2) //this means the BPM is un uint16
{
    short hr = BitConverter.ToInt16(heartRateRecord, offset);
    offset += 2;
}
else //BPM is uint8
{
    byte hr = heartRateRecord[offset];
    offset += 1;
}

//see if EE is available
//if so, pull 2 bytes
bool ee = (flags & (1 << 3)) != 0;
if (ee)
    offset += 2;

//see if RR is present
//if so, the number of RR values is total bytes left / 2 (size of uint16)
bool rr = (flags & (1 << 4)) != 0;
if (rr)
{
    int count = (heartRateRecord.Length - offset)/2;
    for (int i = 0; i < count; i++)
    {
        //each existence of these values means an R-Wave was already detected
        //the ushort means the time (1/1024 seconds) since last r-wave
        ushort value = BitConverter.ToUInt16(heartRateRecord, offset);

        double intervalLengthInSeconds = value/1024.0;
        offset += 2;
    }
}
person Daniel Judge    schedule 30.10.2013
comment
Спасибо за подробный пример кода, +1 вам. Я перевел и использовал все в Objective-C. Смотрите мой ответ на этот пост. - person Brabbeldas; 23.10.2014
comment
Поведение моего датчика Wahoo Tickr в отношении количества обнаруженных зубцов r довольно странное. В основном я получаю только одно показание RR, которое кажется правильным. Но когда я получаю, например. 2 показания, эти два показания должны быть суммированы, чтобы получить правильное значение RR (уд/мин: 48, rr[0]: 0,58, rr[1]: 0,62) ... но иногда это почти два отдельных показания, но с каким-то маловероятным значением. слишком большой или слишком низкий.. - person Chriz; 21.04.2016

Этот пост немного устарел, но полный ответ не был дан. Поскольку я столкнулся с этим постом, и он действительно помог мне в конце, я хотел бы поделиться своим окончательным кодом. Надеюсь, это поможет другим.

Код, предоставленный Daniel Judge, на самом деле правильный, но, как он уже писал, это C#. Код HI немного лучше по сравнению с тем, что Саймон М. придумал в конце, поскольку код Дэниела Джаджа учитывает, что в одном сообщении может быть более двух значений RR. Вот актуальная спецификация Характеристика Heart_rate_measurement

Я перевел код Daniel Judge на Objective-C:

// Instance method to get the heart rate BPM information
- (void) getHeartBPMData:(CBCharacteristic *)characteristic error:(NSError *)error
{
    // Get the BPM //
    // https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml //

    // Convert the contents of the characteristic value to a data-object //
    NSData *data = [characteristic value];

    // Get the byte sequence of the data-object //
    const uint8_t *reportData = [data bytes];

    // Initialise the offset variable //
    NSUInteger offset = 1;
    // Initialise the bpm variable //
    uint16_t bpm = 0;


    // Next, obtain the first byte at index 0 in the array as defined by reportData[0] and mask out all but the 1st bit //
    // The result returned will either be 0, which means that the 2nd bit is not set, or 1 if it is set //
    // If the 2nd bit is not set, retrieve the BPM value at the second byte location at index 1 in the array //
    if ((reportData[0] & 0x01) == 0) {
        // Retrieve the BPM value for the Heart Rate Monitor
        bpm = reportData[1];

        offset = offset + 1; // Plus 1 byte //
    }
    else {
        // If the second bit is set, retrieve the BPM value at second byte location at index 1 in the array and //
        // convert this to a 16-bit value based on the host’s native byte order //
        bpm = CFSwapInt16LittleToHost(*(uint16_t *)(&reportData[1]));

        offset =  offset + 2; // Plus 2 bytes //
    }
    NSLog(@"bpm: %i", bpm);



    // Determine if EE data is present //
    // If the 3rd bit of the first byte is 1 this means there is EE data //
    // If so, increase offset with 2 bytes //
    if ((reportData[0] & 0x03) == 1) {
        offset =  offset + 2; // Plus 2 bytes //
    }



    // Determine if RR-interval data is present //
    // If the 4th bit of the first byte is 1 this means there is RR data //
    if ((reportData[0] & 0x04) == 0)
    {
        NSLog(@"%@", @"Data are not present");
    }
    else
    {
        // The number of RR-interval values is total bytes left / 2 (size of uint16) //

        NSUInteger length = [data length];
        NSUInteger count = (length - offset)/2;
        NSLog(@"RR count: %lu", (unsigned long)count);

        for (int i = 0; i < count; i++) {

            // The unit for RR interval is 1/1024 seconds //
            uint16_t value = CFSwapInt16LittleToHost(*(uint16_t *)(&reportData[offset]));
            value = ((double)value / 1024.0 ) * 1000.0;


            offset = offset + 2; // Plus 2 bytes //

            NSLog(@"RR value %lu: %u", (unsigned long)i, value);

        }

    }

}
person Brabbeldas    schedule 23.10.2014
comment
Привет! Не могли бы вы объяснить мне, откуда берется * 1000? Я понимаю, что это связано со спецификацией Разрешение 1/1024 секунды, но я не понимаю работу *1000 - person darksider; 09.11.2017

РЕДАКТИРОВАТЬ: это работает для меня, я получаю правильные значения rr: в некоторых случаях вы можете одновременно найти два значения для rr.

- (void) updateWithHRMData:(NSData *)datas {

    const uint8_t *reportData = [datas bytes];

    uint16_t bpm = 0;
    uint16_t bpm2 = 0;

    if ((reportData[0] & 0x04) == 0)
    {
        NSLog(@"%@", @"Data are not present");
    }
    else
    {

        bpm = CFSwapInt16LittleToHost(*(uint16_t *)(&reportData[2]));

        bpm2 = CFSwapInt16LittleToHost(*(uint16_t *)(&reportData[4]));

        if (bpm != 0 || bpm2 != 0) {

                NSLog(@"%u", bpm);

                if (bpm2 != 0) {
                    NSLog(@"%u", bpm2);
                }

        }

    }

}
person Simone M    schedule 07.11.2013

в решении @Brabbeldas мне пришлось использовать другой флаг для получения значений rri. но может зависеть от используемого устройства.

if ((reportData[0] & 0x10) == 0)

вместо

if ((reportData[0] & 0x04) == 0)
person bnomei    schedule 19.07.2016

Разобрать параметры ЧСС в «С»

Я загрузил пример приложения на GitHub Heart-Rate-Bluegiga.

void ble_evt_attclient_attribute_value(const struct ble_msg_attclient_attribute_value_evt_t *msg)
{
    if (msg->value.len < 2) {
        printf("Not enough fields in Heart Rate Measurement value");
        change_state(state_finish);
    }


    // Heart Rate Profile defined flags 
    const unsigned char HEART_RATE_VALUE_FORMAT = 0x01;
    const unsigned char ENERGY_EXPENDED_STATUS = 0x08;
    const unsigned char RR_INTERVAL = 0x10;


    unsigned char current_offset = 0;
    unsigned char flags = msg->value.data[current_offset];
    int is_heart_rate_value_size_long = ((flags & HEART_RATE_VALUE_FORMAT) != 0);
    int has_expended_energy = ((flags & ENERGY_EXPENDED_STATUS) != 0);
    int has_rr_intervals = ((flags & RR_INTERVAL) != 0);


    current_offset++;


    uint16 heart_rate_measurement_value = 0;


    if (is_heart_rate_value_size_long)
    {
        heart_rate_measurement_value = (uint16)((msg->value.data[current_offset + 1] << 8) +
            msg->value.data[current_offset]);
        current_offset += 2;
    }
    else
    {
        heart_rate_measurement_value = msg->value.data[current_offset];
        current_offset++;
    }


    printf("Heart rate measurment value: %d ", heart_rate_measurement_value);


    uint16 expended_energy_value = 0;


    if (has_expended_energy)
    {
        expended_energy_value = (uint16)((msg->value.data[current_offset + 1] << 8) +
            msg->value.data[current_offset]);
        current_offset += 2;


        printf(" Expended energy value: %d ", expended_energy_value);
    }


    uint16 rr_intervals[10] = {0};


    if (has_rr_intervals)
    {
        printf(" Rr intervals: ");


        int rr_intervals_count = (msg->value.len - current_offset) / 2;


        for (int i = 0; i < rr_intervals_count; i++)
        {
            int raw_rr_interval = (uint16)((msg->value.data[current_offset + 1] << 8) +
                msg->value.data[current_offset]);
            rr_intervals[i] = ((double)raw_rr_interval / 1024) * 1000;
            current_offset += 2;


            printf("%d ", rr_intervals[i]);
        }
        printf("\n");
    }
}
person ozapara    schedule 20.11.2016