Skip to content

Commit

Permalink
LL: Allow Channel Map for Advertising #113
Browse files Browse the repository at this point in the history
  • Loading branch information
TorstenRobitzki committed Sep 1, 2023
1 parent 2d9a807 commit f71e94a
Show file tree
Hide file tree
Showing 2 changed files with 276 additions and 20 deletions.
158 changes: 138 additions & 20 deletions bluetoe/link_layer/include/bluetoe/advertising.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ namespace link_layer {
struct advertising_type_meta_type {};
struct advertising_startup_meta_type {};
struct advertising_interval_meta_type {};
struct advertising_channel_map_meta_type {};

template < unsigned long long AdvertisingIntervalMilliSeconds >
struct check_advertising_interval_parameter {
Expand Down Expand Up @@ -941,6 +942,137 @@ namespace link_layer {
/** @endcond */
};

namespace details {
struct advertising_channel_map_base {
static constexpr unsigned first_advertising_channel = 37;
static constexpr unsigned last_advertising_channel = 39;
};
}

/**
* @brief adds the abillity to set a channel map for advertising
*
* Using this type as an option to the link_layer, adds the documented
* functions to the link_layer.
*
* By default, all 3 channels are enabled. Setting the channel map to
* empty is not supported.
*
* It is not supported to change the channel map during advertising.
*/
struct variable_advertising_channel_map : details::advertising_channel_map_base
{
void add_channel_to_advertising_channel_map( unsigned channel )
{
assert( channel >= first_advertising_channel );
assert( channel <= last_advertising_channel );

channel -= first_advertising_channel;
map_ = map_ | ( 1 << channel );

current_channel_index_ = first_channel_index();
}

void remove_channel_from_advertsing_channel_map( unsigned channel )
{
assert( channel >= first_advertising_channel );
assert( channel <= last_advertising_channel );

channel -= first_advertising_channel;
map_ = map_ & ~( 1 << channel );

if ( map_ )
current_channel_index_ = first_channel_index();
}

/** @cond HIDDEN_SYMBOLS */
struct meta_type :
details::advertising_channel_map_meta_type,
details::valid_link_layer_option_meta_type {};
protected:
variable_advertising_channel_map()
: current_channel_index_( 0 )
, map_( 0x7 )
{}

unsigned current_channel() const
{
return current_channel_index_ + first_advertising_channel;
}

void next_channel()
{
assert( map_ != 0 );

++current_channel_index_;

// seek next position in map that is set to 1
for ( ; ( 1 << current_channel_index_ ) == 0 && ( 1 << current_channel_index_ ) <= map_; ++current_channel_index_ )
;

if ( ( 1 << current_channel_index_ ) > map_ )
current_channel_index_ = first_channel_index();
}

bool first_channel_selected() const
{
return current_channel_index_ == first_channel_index();
}

private:
unsigned first_channel_index() const
{
unsigned result = 0;
for ( ; ( map_ & ( 1 << result ) ) == 0; ++result )
;

return result;
}

unsigned current_channel_index_;
unsigned map_;
/** @endcond */
};

/**
* @brief channel map that contains all three advertising channels
*
* This is the default behaviour
*/
struct all_advertising_channel_map : details::advertising_channel_map_base
{
/** @cond HIDDEN_SYMBOLS */
struct meta_type :
details::advertising_channel_map_meta_type,
details::valid_link_layer_option_meta_type {};

protected:
all_advertising_channel_map()
: current_channel_index_( first_advertising_channel )
{}

unsigned current_channel() const
{
return current_channel_index_;
}

void next_channel()
{
current_channel_index_ = current_channel_index_ == last_advertising_channel
? first_advertising_channel
: current_channel_index_ + 1;
}

bool first_channel_selected() const
{
return current_channel_index_ == this->first_advertising_channel;
}

private:
unsigned current_channel_index_;
/** @endcond */
};

namespace details {
/*
* Type to implement the single and multiple adverting type advertisings
Expand All @@ -952,51 +1084,37 @@ namespace link_layer {
{
static constexpr std::uint32_t advertising_radio_access_address = 0x8E89BED6;
static constexpr std::uint32_t advertising_crc_init = 0x555555;

static constexpr unsigned first_advertising_channel = 37;
static constexpr unsigned last_advertising_channel = 39;
};

template < typename ... Options >
class advertiser_base :
public advertiser_base_base,
public bluetoe::details::find_by_meta_type<
details::advertising_interval_meta_type,
Options..., advertising_interval< 100 > >::type
Options..., advertising_interval< 100 > >::type,
public bluetoe::details::find_by_meta_type<
details::advertising_channel_map_meta_type,
Options..., all_advertising_channel_map >::type
{
protected:
advertiser_base()
: current_channel_index_( first_advertising_channel )
, adv_perturbation_( 0 )
: adv_perturbation_( 0 )
{
}

delta_time next_adv_event()
{
if ( current_channel_index_ != this->first_advertising_channel )
if ( !this->first_channel_selected() )
return delta_time::now();

adv_perturbation_ = ( adv_perturbation_ + 7 ) % ( max_adv_perturbation_ + 1 );

return this->current_advertising_interval() + delta_time::msec( adv_perturbation_ );
}

unsigned current_channel() const
{
return current_channel_index_;
}

void next_channel()
{
current_channel_index_ = current_channel_index_ == last_advertising_channel
? first_advertising_channel
: current_channel_index_ + 1;
}

private:
static constexpr unsigned max_adv_perturbation_ = 10;

unsigned current_channel_index_;
unsigned adv_perturbation_;
};

Expand Down
138 changes: 138 additions & 0 deletions tests/link_layer/ll_advertising_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1134,3 +1134,141 @@ BOOST_AUTO_TEST_SUITE( advertising_custom_data )
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_SUITE( channel_map )

using variable_channel_map = advertising_base<
bluetoe::link_layer::variable_advertising_channel_map,
bluetoe::link_layer::no_auto_start_advertising >;

BOOST_FIXTURE_TEST_CASE( advertising_uses_all_three_adv_channels, variable_channel_map )
{
std::map< unsigned, unsigned > channel;
start_advertising();

run();

all_data( [&]( const test::advertising_data& d ) { ++channel[ d.channel ]; } );

BOOST_CHECK_EQUAL( channel.size(), 3u );
BOOST_CHECK_GT( channel[ 37 ], 0u );
BOOST_CHECK_GT( channel[ 38 ], 0u );
BOOST_CHECK_GT( channel[ 39 ], 0u );
}

BOOST_FIXTURE_TEST_CASE( advertising_uses_just_2_adv_channels, variable_channel_map )
{
std::map< unsigned, unsigned > channel;
remove_channel_from_advertsing_channel_map( 37 );
start_advertising();

run();

all_data( [&]( const test::advertising_data& d ) { ++channel[ d.channel ]; } );

BOOST_CHECK_EQUAL( channel.size(), 2u );
BOOST_CHECK_EQUAL( channel[ 37 ], 0u );
BOOST_CHECK_GT( channel[ 38 ], 0u );
BOOST_CHECK_GT( channel[ 39 ], 0u );
}

BOOST_FIXTURE_TEST_CASE( advertising_uses_just_1_adv_channels, variable_channel_map )
{
std::map< unsigned, unsigned > channel;
remove_channel_from_advertsing_channel_map( 37 );
remove_channel_from_advertsing_channel_map( 39 );
start_advertising();

run();

all_data( [&]( const test::advertising_data& d ) { ++channel[ d.channel ]; } );

BOOST_CHECK_EQUAL( channel.size(), 1u );
BOOST_CHECK_EQUAL( channel[ 37 ], 0u );
BOOST_CHECK_GT( channel[ 38 ], 0u );
BOOST_CHECK_EQUAL( channel[ 39 ], 0u );
}


BOOST_FIXTURE_TEST_CASE( advertising_uses_just_1_adv_channels_remove_all_and_add, variable_channel_map )
{
std::map< unsigned, unsigned > channel;
remove_channel_from_advertsing_channel_map( 37 );
remove_channel_from_advertsing_channel_map( 38 );
remove_channel_from_advertsing_channel_map( 39 );

add_channel_to_advertising_channel_map( 37 );

start_advertising();

run();

all_data( [&]( const test::advertising_data& d ) { ++channel[ d.channel ]; } );

BOOST_CHECK_EQUAL( channel.size(), 1u );
BOOST_CHECK_GT( channel[ 37 ], 0u );
BOOST_CHECK_EQUAL( channel[ 38 ], 0u );
BOOST_CHECK_EQUAL( channel[ 39 ], 0u );
}

BOOST_FIXTURE_TEST_CASE( advertising_uses_all_three_adv_channels_observice_time, variable_channel_map )
{
std::vector< bluetoe::link_layer::delta_time > on_air_times;
start_advertising();

run();

all_data( [&]( const test::advertising_data& d ) { on_air_times.push_back( d.on_air_time ); } );

static const bluetoe::link_layer::delta_time expected[] = {
bluetoe::link_layer::delta_time::msec( 0 ),
bluetoe::link_layer::delta_time::msec( 0 ),
bluetoe::link_layer::delta_time::msec( 0 ),
bluetoe::link_layer::delta_time::msec( 107 ),
bluetoe::link_layer::delta_time::msec( 107 ),
bluetoe::link_layer::delta_time::msec( 107 ),
bluetoe::link_layer::delta_time::msec( 210 ),
bluetoe::link_layer::delta_time::msec( 210 ),
bluetoe::link_layer::delta_time::msec( 210 ),
};

static const auto expected_size = std::distance( std::begin(expected), std::end(expected) );

BOOST_REQUIRE_LE( expected_size, on_air_times.size() );
on_air_times.erase(
std::next( on_air_times.begin(), expected_size ), on_air_times.end() );

BOOST_CHECK_EQUAL_COLLECTIONS(
on_air_times.begin(), on_air_times.end(),
std::begin(expected), std::end(expected) );
}

BOOST_FIXTURE_TEST_CASE( advertising_uses_single_adv_channels_observice_time, variable_channel_map )
{
std::vector< bluetoe::link_layer::delta_time > on_air_times;
remove_channel_from_advertsing_channel_map( 38 );
remove_channel_from_advertsing_channel_map( 39 );
start_advertising();

run();

all_data( [&]( const test::advertising_data& d ) { on_air_times.push_back( d.on_air_time ); } );

static const bluetoe::link_layer::delta_time expected[] = {
bluetoe::link_layer::delta_time::msec( 0 ),
bluetoe::link_layer::delta_time::msec( 107 ),
bluetoe::link_layer::delta_time::msec( 210 ),
};

static const auto expected_size = std::distance( std::begin(expected), std::end(expected) );

BOOST_REQUIRE_LE( expected_size, on_air_times.size() );
on_air_times.erase(
std::next( on_air_times.begin(), expected_size ), on_air_times.end() );

BOOST_CHECK_EQUAL_COLLECTIONS(
on_air_times.begin(), on_air_times.end(),
std::begin(expected), std::end(expected) );
}

BOOST_AUTO_TEST_SUITE_END()

0 comments on commit f71e94a

Please sign in to comment.