Skip to content

Commit 7e72d46

Browse files
olivier-le-sageaescolar
authored andcommitted
bluetooth: host: Add helper function for parsing PCTs
The 12-bit signed values for the results of PBR are a bit cumbersome, so this adds a helper function to make it easier to work with the HCI formatted steps. Signed-off-by: Olivier Lesage <[email protected]>
1 parent 635d03b commit 7e72d46

File tree

7 files changed

+186
-1
lines changed

7 files changed

+186
-1
lines changed

include/zephyr/bluetooth/cs.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,24 @@ struct bt_le_cs_subevent_step {
551551
const uint8_t *data;
552552
};
553553

554+
/** Sign-extended IQ value extracted from step data. */
555+
struct bt_le_cs_iq_sample {
556+
int16_t i;
557+
int16_t q;
558+
};
559+
560+
/** @brief Extract in-phase and quadrature terms from HCI-formatted PCT.
561+
*
562+
* Convenience function for processing 24-bit phase correction terms found
563+
* in CS step data. The 12-bit signed real and imaginary components are
564+
* converted to host endianness and sign-extended.
565+
*
566+
* @param pct 24-bit little-endian phase correction term.
567+
*
568+
* @return struct bt_le_cs_iq_sample containing real and imaginary terms as int16_t
569+
*/
570+
struct bt_le_cs_iq_sample bt_le_cs_parse_pct(const uint8_t pct[3]);
571+
554572
/** @brief Set all valid channel map bits
555573
*
556574
* This command is used to enable all valid channels in a

include/zephyr/bluetooth/hci_types.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3620,7 +3620,8 @@ struct bt_hci_evt_le_cs_config_complete {
36203620

36213621
#define BT_HCI_LE_CS_REF_POWER_LEVEL_UNAVAILABLE 0x7F
36223622

3623-
#define BT_HCI_LE_CS_PCT_MASK 0xFFF
3623+
#define BT_HCI_LE_CS_PCT_I_MASK 0x000FFF
3624+
#define BT_HCI_LE_CS_PCT_Q_MASK 0xFFF000
36243625

36253626
#define BT_HCI_LE_CS_TONE_QUALITY_HIGH 0x0
36263627
#define BT_HCI_LE_CS_TONE_QUALITY_MED 0x1

subsys/bluetooth/host/cs.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,24 @@ static void reset_reassembly_results(void)
240240
memset(&reassembled_result, 0, sizeof(struct bt_conn_le_cs_subevent_result));
241241
}
242242

243+
/** @brief Converts PCT to a pair of int16_t
244+
*
245+
*/
246+
struct bt_le_cs_iq_sample bt_le_cs_parse_pct(const uint8_t pct[3])
247+
{
248+
uint32_t pct_u32 = sys_get_le24(pct);
249+
250+
/* Extract I and Q. */
251+
uint16_t i_u16 = pct_u32 & BT_HCI_LE_CS_PCT_I_MASK;
252+
uint16_t q_u16 = (pct_u32 & BT_HCI_LE_CS_PCT_Q_MASK) >> 12;
253+
254+
/* Convert from 12-bit 2's complement to int16_t */
255+
int16_t i = (i_u16 ^ BIT(11)) - BIT(11);
256+
int16_t q = (q_u16 ^ BIT(11)) - BIT(11);
257+
258+
return (struct bt_le_cs_iq_sample){.i = i, .q = q};
259+
}
260+
243261
void bt_le_cs_set_valid_chmap_bits(uint8_t channel_map[10])
244262
{
245263
memset(channel_map, 0xFF, 10);
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
cmake_minimum_required(VERSION 3.20.0)
4+
5+
find_package(Zephyr COMPONENTS unittest REQUIRED HINTS $ENV{ZEPHYR_BASE})
6+
7+
project(bt_step_data_parse)
8+
9+
include_directories(BEFORE
10+
${ZEPHYR_BASE}/tests/bluetooth/host/cs/mocks
11+
)
12+
13+
add_subdirectory(${ZEPHYR_BASE}/tests/bluetooth/host host_mocks)
14+
add_subdirectory(${ZEPHYR_BASE}/tests/bluetooth/host/cs/mocks mocks)
15+
16+
target_link_libraries(testbinary PRIVATE mocks host_mocks)
17+
18+
target_sources(testbinary
19+
PRIVATE
20+
src/main.c
21+
22+
${ZEPHYR_BASE}/subsys/bluetooth/host/cs.c
23+
${ZEPHYR_BASE}/lib/net_buf/buf_simple.c
24+
${ZEPHYR_BASE}/subsys/logging/log_minimal.c
25+
)
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CONFIG_ZTEST=y
2+
CONFIG_BT=y
3+
CONFIG_BT_HCI=y
4+
CONFIG_BT_CENTRAL=y
5+
CONFIG_BT_CHANNEL_SOUNDING=y
6+
CONFIG_ASSERT=y
7+
CONFIG_ASSERT_LEVEL=2
8+
CONFIG_ASSERT_VERBOSE=y
9+
CONFIG_ASSERT_ON_ERRORS=y
10+
CONFIG_NET_BUF=y
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/*
2+
* Copyright (c) 2024 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <zephyr/kernel.h>
8+
#include <zephyr/bluetooth/cs.h>
9+
#include <zephyr/fff.h>
10+
11+
DEFINE_FFF_GLOBALS;
12+
13+
ZTEST_SUITE(bt_le_cs_parse_pct, NULL, NULL, NULL, NULL, NULL);
14+
15+
/*
16+
* Test success case
17+
*
18+
* Constraints:
19+
* - Valid PCT is passed in
20+
*
21+
* Expected behaviour:
22+
* - IQ term matches expected values
23+
*/
24+
ZTEST(bt_le_cs_parse_pct, test_parsing_success)
25+
{
26+
struct bt_le_cs_iq_sample iq;
27+
28+
struct {
29+
uint8_t input[3];
30+
struct bt_le_cs_iq_sample output;
31+
} test_vector[] = {
32+
/* Edge cases */
33+
{.input = {0x00, 0x00, 0x00}, .output = {.i = 0, .q = 0}},
34+
{.input = {0xFF, 0xFF, 0xFF}, .output = {.i = -1, .q = -1}},
35+
{.input = {0xFF, 0x00, 0xFF}, .output = {.i = 255, .q = -16}},
36+
{.input = {0xFF, 0x00, 0x00}, .output = {.i = 255, .q = 0}},
37+
{.input = {0x00, 0xFF, 0x00}, .output = {.i = -256, .q = 15}},
38+
{.input = {0x00, 0x00, 0xFF}, .output = {.i = 0, .q = -16}},
39+
{.input = {0x00, 0x08, 0x80}, .output = {.i = -2048, .q = -2048}},
40+
{.input = {0xFF, 0xF7, 0x7F}, .output = {.i = 2047, .q = 2047}},
41+
42+
/* Randomly generated using python */
43+
{.input = {0xEF, 0xCD, 0xAB}, .output = {.i = -529, .q = -1348}},
44+
{.input = {0x30, 0x75, 0x44}, .output = {.i = 1328, .q = 1095}},
45+
{.input = {0x46, 0x5D, 0xEB}, .output = {.i = -698, .q = -331}},
46+
{.input = {0xE8, 0x14, 0x45}, .output = {.i = 1256, .q = 1105}},
47+
{.input = {0x23, 0xCA, 0x5C}, .output = {.i = -1501, .q = 1484}},
48+
{.input = {0x68, 0xA0, 0x15}, .output = {.i = 104, .q = 346}},
49+
{.input = {0x39, 0x73, 0x1B}, .output = {.i = 825, .q = 439}},
50+
{.input = {0x23, 0x72, 0x3D}, .output = {.i = 547, .q = 983}},
51+
{.input = {0xF5, 0xF8, 0x3D}, .output = {.i = -1803, .q = 991}},
52+
{.input = {0xF7, 0xB4, 0xB9}, .output = {.i = 1271, .q = -1125}},
53+
{.input = {0x61, 0x9F, 0xD5}, .output = {.i = -159, .q = -679}},
54+
{.input = {0x9B, 0x21, 0xC6}, .output = {.i = 411, .q = -926}},
55+
{.input = {0x14, 0x86, 0x0F}, .output = {.i = 1556, .q = 248}},
56+
{.input = {0x8E, 0xBB, 0xC6}, .output = {.i = -1138, .q = -917}},
57+
{.input = {0x5B, 0xD1, 0xC2}, .output = {.i = 347, .q = -979}},
58+
{.input = {0x99, 0x4A, 0x28}, .output = {.i = -1383, .q = 644}},
59+
{.input = {0x32, 0x16, 0x2B}, .output = {.i = 1586, .q = 689}},
60+
{.input = {0x3E, 0x8C, 0xD4}, .output = {.i = -962, .q = -696}},
61+
{.input = {0x2B, 0x1F, 0x95}, .output = {.i = -213, .q = -1711}},
62+
{.input = {0x22, 0xE6, 0xD6}, .output = {.i = 1570, .q = -658}},
63+
{.input = {0x0B, 0x31, 0xD6}, .output = {.i = 267, .q = -669}},
64+
{.input = {0x1B, 0x98, 0x9D}, .output = {.i = -2021, .q = -1575}},
65+
{.input = {0x8E, 0x97, 0x63}, .output = {.i = 1934, .q = 1593}},
66+
{.input = {0x97, 0x91, 0x8D}, .output = {.i = 407, .q = -1831}},
67+
{.input = {0x67, 0xF7, 0x1F}, .output = {.i = 1895, .q = 511}},
68+
{.input = {0xD6, 0x5C, 0x23}, .output = {.i = -810, .q = 565}},
69+
{.input = {0x92, 0xD3, 0x0B}, .output = {.i = 914, .q = 189}},
70+
{.input = {0xE8, 0xF3, 0x23}, .output = {.i = 1000, .q = 575}},
71+
{.input = {0xE6, 0xE3, 0xAD}, .output = {.i = 998, .q = -1314}},
72+
{.input = {0x6E, 0x70, 0xA9}, .output = {.i = 110, .q = -1385}},
73+
{.input = {0x63, 0x65, 0x28}, .output = {.i = 1379, .q = 646}},
74+
{.input = {0x27, 0x0F, 0x32}, .output = {.i = -217, .q = 800}},
75+
{.input = {0x3F, 0x8C, 0xE1}, .output = {.i = -961, .q = -488}},
76+
{.input = {0x4E, 0x86, 0xAA}, .output = {.i = 1614, .q = -1368}},
77+
{.input = {0x9E, 0xD1, 0xF6}, .output = {.i = 414, .q = -147}},
78+
{.input = {0x86, 0x09, 0x56}, .output = {.i = -1658, .q = 1376}},
79+
{.input = {0xFF, 0x09, 0x41}, .output = {.i = -1537, .q = 1040}},
80+
{.input = {0x89, 0xC5, 0x1F}, .output = {.i = 1417, .q = 508}},
81+
{.input = {0x1A, 0xE2, 0x9A}, .output = {.i = 538, .q = -1618}},
82+
{.input = {0x7E, 0x03, 0xB8}, .output = {.i = 894, .q = -1152}},
83+
{.input = {0x5E, 0x28, 0xB3}, .output = {.i = -1954, .q = -1230}},
84+
{.input = {0xFF, 0x50, 0xF0}, .output = {.i = 255, .q = -251}},
85+
{.input = {0xB0, 0x07, 0x87}, .output = {.i = 1968, .q = -1936}},
86+
{.input = {0x7E, 0xD7, 0x0C}, .output = {.i = 1918, .q = 205}},
87+
{.input = {0x26, 0xA2, 0xC9}, .output = {.i = 550, .q = -870}},
88+
{.input = {0x97, 0x71, 0x72}, .output = {.i = 407, .q = 1831}},
89+
{.input = {0x73, 0x0E, 0xC1}, .output = {.i = -397, .q = -1008}},
90+
{.input = {0xAC, 0x20, 0x6B}, .output = {.i = 172, .q = 1714}},
91+
{.input = {0x85, 0x7D, 0xB4}, .output = {.i = -635, .q = -1209}},
92+
{.input = {0xCC, 0xE3, 0x1B}, .output = {.i = 972, .q = 446}},
93+
{.input = {0x88, 0x48, 0x65}, .output = {.i = -1912, .q = 1620}},
94+
};
95+
96+
for (uint16_t k = 0; k < ARRAY_SIZE(test_vector); k++) {
97+
iq = bt_le_cs_parse_pct(test_vector[k].input);
98+
99+
zassert_equal(iq.i, test_vector[k].output.i,
100+
"Failed for k = %u, expected %d, not %d", k, test_vector[k].output.i,
101+
iq.i);
102+
zassert_equal(iq.q, test_vector[k].output.q,
103+
"Failed for k = %u, expected %d, not %d", k, test_vector[k].output.q,
104+
iq.q);
105+
}
106+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
common:
2+
tags:
3+
- bluetooth
4+
- host
5+
tests:
6+
bluetooth.host.cs.bt_le_cs_parse_pct:
7+
type: unit

0 commit comments

Comments
 (0)