Skip to content

Commit 54a8d02

Browse files
Mirlenkorlubos
authored andcommitted
samples: radio_test: add set_channel_sequence_hopping_mode shell command
Add the set_channel_sequence_hopping_mode command to control how TX sweep with sleep uses the configured channel list. * sequential: hopping follows the stored channel list in index order. The list is restored to the last saved baseline (same order as after the sequence was set or restored). random [seed]: optional seed from 1 to 511; if omitted, seed 1 is used. The hopping order is a Fisher–Yates shuffle of the list driven by that seed, applied immediately when switching to random mode. Signed-off-by: Aleksandr Mirlenko <aleksandr.mirlenko@nordicsemi.no>
1 parent 68cee5b commit 54a8d02

5 files changed

Lines changed: 189 additions & 30 deletions

File tree

doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -345,14 +345,11 @@ Peripheral samples
345345

346346
* Added:
347347

348-
* ``start_tx_sweep_with_sleep`` shell command.
349-
It allows for running a TX sweep with silent periods between each frequency.
350-
* ``start_tx_sweep_with_sleep_modulated`` shell command.
351-
It allows for running a modulated TX sweep with silent periods between each frequency.
352-
* ``set_channel_sequence`` shell command.
353-
It allows for setting a custom channel sequence for the ``start_tx_sweep_with_sleep`` and ``start_tx_sweep_with_sleep_modulated`` commands.
354-
* ``print_channel_sequence`` shell command.
355-
It prints the currently configured channel sequence.
348+
* ``start_tx_sweep_with_sleep`` shell command that allows for running a TX sweep with silent periods between each frequency.
349+
* ``start_tx_sweep_with_sleep_modulated`` shell command that allows for running a modulated TX sweep with silent periods between each frequency.
350+
* ``set_channel_sequence`` shell command that allows for setting a custom channel sequence for the ``start_tx_sweep_with_sleep`` and ``start_tx_sweep_with_sleep_modulated`` commands.
351+
* ``print_channel_sequence`` shell command that prints the currently configured channel sequence.
352+
* ``set_channel_sequence_hopping_mode`` shell command that allows for setting the hopping mode for the channel sequence.
356353
* Support for pin debugging of radio events on nRF54L Series devices.
357354

358355
PMIC samples

samples/peripheral/radio_test/README.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,10 @@ User interface
111111
* - set_channel_sequence
112112
- <sequence of up to 80 channels>
113113
- Set a custom channel sequence for the start_tx_with_sleep command
114+
* - set_channel_sequence_hopping_mode
115+
- <hopping_mode>
116+
- Set the hopping mode for the channel sequence.
117+
Sequential (default order) | random <seed>
114118
* - start_channel
115119
- <channel>
116120
- Start channel for the sweep or the channel for the constant carrier (in MHz, as difference from 2400 MHz).

samples/peripheral/radio_test/src/radio_cmd.c

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -749,6 +749,7 @@ static int cmd_set_channel_sequence(const struct shell *shell, size_t argc,
749749
if (sequence_length > 80) {
750750
shell_error(shell, "Too many channels in array, %i is larger than 80",
751751
sequence_length);
752+
return -EINVAL;
752753
}
753754

754755
for (uint8_t i = 0; i < sequence_length; i++) {
@@ -764,7 +765,7 @@ static int cmd_set_channel_sequence(const struct shell *shell, size_t argc,
764765

765766
struct radio_test_channel_sequence *channel_sequence = radio_test_channel_sequence_get();
766767

767-
channel_sequence->sequence_length = sequence_length;
768+
channel_sequence->length = sequence_length;
768769
for (uint8_t i = 0; i < sequence_length; i++) {
769770
uint8_t channel = atoi(argv[i + 1]);
770771

@@ -773,14 +774,65 @@ static int cmd_set_channel_sequence(const struct shell *shell, size_t argc,
773774
return 0;
774775
}
775776

777+
static int cmd_sequential(const struct shell *shell, size_t argc,
778+
char **argv)
779+
{
780+
(void)argc;
781+
(void)argv;
782+
783+
struct radio_test_channel_sequence *seq = radio_test_channel_sequence_get();
784+
785+
seq->hopping_mode = CHANNEL_HOP_SEQUENTIAL;
786+
shell_print(shell, "Channel hopping: sequential (default order)");
787+
788+
return 0;
789+
}
790+
791+
static int cmd_random(const struct shell *shell, size_t argc,
792+
char **argv)
793+
{
794+
struct radio_test_channel_sequence *seq = radio_test_channel_sequence_get();
795+
796+
seq->hopping_mode = CHANNEL_HOP_RANDOM_FISHER_YATES;
797+
798+
if (argc >= 2) {
799+
seq->shuffled.seed = (uint32_t)atoi(argv[1]);
800+
}
801+
802+
memcpy(seq->shuffled.sequence, seq->sequence_array, seq->length);
803+
804+
srand((unsigned int)seq->shuffled.seed);
805+
shuffle_channel_sequence();
806+
807+
shell_print(shell, "Channel hopping: random (seed %u)", seq->shuffled.seed);
808+
return 0;
809+
}
810+
811+
static int cmd_set_channel_sequence_hopping_mode(const struct shell *shell, size_t argc,
812+
char **argv)
813+
{
814+
if (argc == 1) {
815+
shell_help(shell);
816+
return SHELL_CMD_HELP_PRINTED;
817+
}
818+
819+
if (argc > 2) {
820+
shell_error(shell, "%s: bad parameters count", argv[0]);
821+
return -EINVAL;
822+
}
823+
824+
shell_error(shell, "Unknown argument: %s", argv[1]);
825+
return -EINVAL;
826+
}
827+
776828
static int cmd_print_channel_sequence(const struct shell *shell, size_t argc, char **argv)
777829
{
778830
struct radio_test_channel_sequence *channel_sequence = radio_test_channel_sequence_get();
779831

780-
shell_print(shell, "Channel Sequence length: %i", channel_sequence->sequence_length);
832+
shell_print(shell, "Channel Sequence length: %i", channel_sequence->length);
781833
shell_fprintf_normal(shell, "Channel Sequence: [%i", channel_sequence->sequence_array[0]);
782834

783-
for (uint8_t i = 1; i < channel_sequence->sequence_length; i++) {
835+
for (uint8_t i = 1; i < channel_sequence->length; i++) {
784836
shell_fprintf_normal(shell, ", %i", channel_sequence->sequence_array[i]);
785837
}
786838

@@ -1631,6 +1683,16 @@ SHELL_CMD_REGISTER(total_output_power, NULL,
16311683
cmd_total_output_power_set);
16321684
#endif /* CONFIG_RADIO_TEST_POWER_CONTROL_AUTOMATIC */
16331685

1686+
SHELL_STATIC_SUBCMD_SET_CREATE(sub_set_channel_sequence_hopping_mode,
1687+
SHELL_CMD(sequential, NULL,
1688+
"Set the channel sequence hopping mode to sequential",
1689+
cmd_sequential),
1690+
SHELL_CMD(random, NULL,
1691+
"Set the channel sequence hopping mode to random with <seed>",
1692+
cmd_random),
1693+
SHELL_SUBCMD_SET_END
1694+
);
1695+
16341696
SHELL_CMD_REGISTER(transmit_pattern,
16351697
&sub_transmit_pattern,
16361698
"Set the transmission pattern",
@@ -1651,6 +1713,9 @@ SHELL_CMD_REGISTER(set_channel_sequence, NULL,
16511713
"Set a custom channel sequence for TX "
16521714
"<sequence_of_up_to_80_channels>",
16531715
cmd_set_channel_sequence);
1716+
SHELL_CMD_REGISTER(set_channel_sequence_hopping_mode, &sub_set_channel_sequence_hopping_mode,
1717+
"TX hop: sequential (default order) | random <seed>",
1718+
cmd_set_channel_sequence_hopping_mode);
16541719
SHELL_CMD_REGISTER(print_channel_sequence, NULL,
16551720
"Print the custom channel sequence for TX.",
16561721
cmd_print_channel_sequence);

samples/peripheral/radio_test/src/radio_test.c

Lines changed: 75 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "radio_test.h"
88

99
#include <string.h>
10+
#include <stdlib.h>
1011
#include <inttypes.h>
1112

1213
#if !(defined(CONFIG_SOC_SERIES_NRF54H) || defined(CONFIG_SOC_SERIES_NRF54L))
@@ -123,18 +124,70 @@ static uint32_t rx_packet_cnt;
123124
/* Radio current channel (frequency). */
124125
static uint8_t current_channel;
125126

126-
/* Modulated TX sweep with sleep: sequential index into channel_array. */
127-
static uint8_t mod_tx_sweep_ch_idx;
127+
#define DEFAULT_CHANNEL_VALUES \
128+
4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, \
129+
17, 18, 19, 20, 21, 22, 23, 24, 28, 29, 30, 31, 32, 33, \
130+
34, 35, 36, 37, 38, 39, 40, 41, \
131+
42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, \
132+
56, 57, 58, 59, 60, 61, 62, 63, \
133+
64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78
134+
135+
#define DEFAULT_CHANNEL_SEQUENCE {DEFAULT_CHANNEL_VALUES}
136+
137+
#define DEFAULT_CHANNEL_COUNT \
138+
((uint8_t)(sizeof((uint8_t[]){ DEFAULT_CHANNEL_VALUES }) / sizeof(uint8_t)))
128139

129140
/* Radio TX Sweep with sleep channel array */
130141
static struct radio_test_channel_sequence channel_sequence = {
131-
.sequence_length = 72,
132-
.sequence_array = {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
133-
17, 18, 19, 20, 21, 22, 23, 24, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41,
134-
42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
135-
64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78}
142+
.length = DEFAULT_CHANNEL_COUNT,
143+
.sequence_array = DEFAULT_CHANNEL_SEQUENCE,
144+
.hopping_mode = CHANNEL_HOP_SEQUENTIAL,
145+
.shuffled = {
146+
.seed = 1,
147+
}
136148
};
137149

150+
static const uint8_t *active_channel_sequence(void)
151+
{
152+
if (channel_sequence.hopping_mode == CHANNEL_HOP_RANDOM_FISHER_YATES) {
153+
return channel_sequence.shuffled.sequence;
154+
}
155+
return channel_sequence.sequence_array;
156+
}
157+
158+
/**
159+
* @brief Random index in [0, n - 1] using rand() (n is exclusive upper bound).
160+
*/
161+
static uint8_t shuffle_rand_index(uint8_t n)
162+
{
163+
if (n <= 1) {
164+
return 0;
165+
}
166+
167+
return (uint8_t)((uint32_t)rand() % (uint32_t)n);
168+
}
169+
170+
/**
171+
* @brief Shuffle hop order on shuffled.sequence (Fisher–Yates) using C library rand().
172+
*
173+
*
174+
*/
175+
void shuffle_channel_sequence(void)
176+
{
177+
if (channel_sequence.length <= 1) {
178+
return;
179+
}
180+
181+
/* Fisher-Yates shuffle */
182+
for (uint8_t i = channel_sequence.length - 1; i > 0; i--) {
183+
uint8_t j = shuffle_rand_index(i + 1);
184+
uint8_t tmp = channel_sequence.shuffled.sequence[i];
185+
186+
channel_sequence.shuffled.sequence[i] = channel_sequence.shuffled.sequence[j];
187+
channel_sequence.shuffled.sequence[j] = tmp;
188+
}
189+
}
190+
138191
/* Timer used for channel sweeps and tx with duty cycle. */
139192
static nrfx_timer_t timer =
140193
NRFX_TIMER_INSTANCE(NRF_TIMER_INST_GET(RADIO_TEST_TIMER_INSTANCE));
@@ -1124,15 +1177,15 @@ static void tx_sweep_with_sleep_modulated_timer_setup(const struct radio_test_co
11241177

11251178
static void radio_tx_sweep_with_sleep_modulated(const struct radio_test_config *cfg)
11261179
{
1127-
mod_tx_sweep_ch_idx = 0;
1180+
channel_sequence.current_index = 0;
11281181

11291182
nrfx_timer_disable(&timer);
11301183
tx_sweep_with_sleep_modulated_timer_setup(cfg);
11311184

11321185
sweep_processing = true;
11331186
radio_modulated_tx_carrier(cfg->mode,
11341187
cfg->params.tx_sweep_with_sleep_modulated.txpower,
1135-
channel_sequence.sequence_array[mod_tx_sweep_ch_idx],
1188+
active_channel_sequence()[channel_sequence.current_index],
11361189
cfg->params.tx_sweep_with_sleep_modulated.pattern,
11371190
0);
11381191
sweep_processing = false;
@@ -1350,11 +1403,11 @@ static void timer_handler(nrf_timer_event_t event_type, void *context)
13501403
radio_unmodulated_tx_carrier_radio_setup(
13511404
NRF_RADIO_MODE_BLE_1MBIT,
13521405
config->params.tx_sweep_with_sleep.txpower,
1353-
channel_sequence.sequence_array[current_channel], false);
1406+
active_channel_sequence()[current_channel], false);
13541407

13551408
/* set up next channel */
13561409
channel_start = 0;
1357-
channel_end = channel_sequence.sequence_length - 1;
1410+
channel_end = channel_sequence.length - 1;
13581411
} else if (config->type == TX_SWEEP_WITH_SLEEP_MODULATED) {
13591412

13601413
nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_DISABLE);
@@ -1363,17 +1416,18 @@ static void timer_handler(nrf_timer_event_t event_type, void *context)
13631416
}
13641417
nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_DISABLED);
13651418
nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_READY);
1366-
mod_tx_sweep_ch_idx++;
1419+
channel_sequence.current_index =
1420+
(channel_sequence.current_index + 1) % channel_sequence.length;
13671421

1368-
if (mod_tx_sweep_ch_idx >
1369-
channel_sequence.sequence_length - 1) {
1370-
mod_tx_sweep_ch_idx = 0;
1422+
if (channel_sequence.hopping_mode == CHANNEL_HOP_RANDOM_FISHER_YATES &&
1423+
channel_sequence.current_index == 0) {
1424+
shuffle_channel_sequence();
13711425
}
13721426

13731427
nrf_radio_event_clear(NRF_RADIO, RADIO_TEST_EVENT_END);
13741428
radio_channel_set(
13751429
config->mode,
1376-
channel_sequence.sequence_array[mod_tx_sweep_ch_idx]);
1430+
active_channel_sequence()[channel_sequence.current_index]);
13771431
} else {
13781432
printk("Unexpected test type: %d\n", config->type);
13791433
return;
@@ -1384,6 +1438,11 @@ static void timer_handler(nrf_timer_event_t event_type, void *context)
13841438
current_channel++;
13851439
if (current_channel > channel_end) {
13861440
current_channel = channel_start;
1441+
1442+
if (channel_sequence.hopping_mode == CHANNEL_HOP_RANDOM_FISHER_YATES &&
1443+
config->type == TX_SWEEP_WITH_SLEEP) {
1444+
shuffle_channel_sequence();
1445+
}
13871446
}
13881447
#if NRF_ERRATA_STATIC_CHECK(54H, 216)
13891448
} else if (event_type == NRF_TIMER_EVENT_COMPARE7) { /* HMPAN-216 errata */

samples/peripheral/radio_test/src/radio_test.h

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -228,13 +228,41 @@ struct radio_rx_stats {
228228
uint32_t packet_cnt;
229229
};
230230

231-
/**@brief Configurable channel sequence for TX with sleep. */
231+
/**
232+
* @brief How the TX sweep walks the configured channel list.
233+
*
234+
* Sequential: index order. Random: list order is shuffled before use.
235+
*/
236+
typedef enum {
237+
CHANNEL_HOP_SEQUENTIAL,
238+
CHANNEL_HOP_RANDOM_FISHER_YATES,
239+
} channel_hopping_mode_t;
240+
241+
/**
242+
* @brief Configurable channel sequence.
243+
*
244+
* sequence_array holds the configured channel order (never permuted here).
245+
* When hopping_mode is CHANNEL_HOP_RANDOM_FISHER_YATES,
246+
* the sweep uses shuffled.sequence, a Fisher–Yates permutation of sequence_array.
247+
*/
232248
struct radio_test_channel_sequence {
233249
/** Length of configurable channel sequence. */
234-
uint8_t sequence_length;
250+
uint8_t length;
235251

236-
/** Configurable channel sequence contents. */
252+
/** Configured channel sequence (canonical order). */
237253
uint8_t sequence_array[80];
254+
255+
/* Index of the next channel in the sequence. */
256+
uint8_t current_index;
257+
258+
/** Channel hopping mode. */
259+
channel_hopping_mode_t hopping_mode;
260+
261+
/** Shuffled channel sequence. */
262+
struct {
263+
uint32_t seed;
264+
uint8_t sequence[80];
265+
} shuffled;
238266
};
239267

240268
/**
@@ -282,4 +310,10 @@ void toggle_dcdc_state(uint8_t dcdc_state);
282310
*/
283311
struct radio_test_channel_sequence *radio_test_channel_sequence_get(void);
284312

313+
/**
314+
* @brief Shuffle channel sequence using Fisher–Yates shuffling algorithm.
315+
* Randomness provided by standard library rand().
316+
*/
317+
void shuffle_channel_sequence(void);
318+
285319
#endif /* RADIO_TEST_H_ */

0 commit comments

Comments
 (0)