[simulation] simulate energy scan (#7393)

This commit implements energy scan in simulation platform.

Verified locally
```bash
> ifconfig up
Done
> scan energy 100
| Ch | RSSI |
+----+------+
| 11 |  -98 |
| 12 |  -98 |
| 13 |  -98 |
| 14 |  -98 |
| 15 |  -30 |
| 16 |  -30 |
| 17 |  -98 |
| 18 |  -98 |
| 19 |  -30 |
| 20 |  -98 |
| 21 |  -98 |
| 22 |  -98 |
| 23 |  -30 |
| 24 |  -30 |
| 25 |  -30 |
| 26 |  -98 |
Done
```
This commit is contained in:
Yakun Xu
2022-05-21 02:00:44 +08:00
committed by GitHub
parent 5fbf1bec88
commit 36b2a173f9
11 changed files with 126 additions and 20 deletions
@@ -163,10 +163,11 @@ void platformRadioReceive(otInstance *aInstance, uint8_t *aBuf, uint16_t aBufLen
*
* @param[in,out] aReadFdSet A pointer to the read file descriptors.
* @param[in,out] aWriteFdSet A pointer to the write file descriptors.
* @param[in,out] aTimeout A pointer to the timeout.
* @param[in,out] aMaxFd A pointer to the max file descriptor.
*
*/
void platformRadioUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, int *aMaxFd);
void platformRadioUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, struct timeval *aTimeout, int *aMaxFd);
/**
* This function performs radio driver processing.
+60 -9
View File
@@ -29,6 +29,7 @@
#include "platform-simulation.h"
#include <errno.h>
#include <sys/time.h>
#include <openthread/dataset.h>
#include <openthread/link.h>
@@ -47,6 +48,9 @@
// The IPv4 group for receiving packets of radio simulation
#define OT_RADIO_GROUP "224.0.0.116"
#define MS_PER_S 1000
#define US_PER_MS 1000
enum
{
IEEE802154_ACK_LENGTH = 5,
@@ -75,6 +79,10 @@ static uint16_t sPortOffset = 0;
static uint16_t sPort = 0;
#endif
static int8_t sEnergyScanResult = OT_RADIO_RSSI_INVALID;
static bool sEnergyScanning = false;
static uint32_t sEnergyScanEndTime = 0;
enum
{
SIM_RADIO_CHANNEL_MIN = OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MIN,
@@ -156,6 +164,13 @@ static otMacKeyMaterial sCurrKey;
static otMacKeyMaterial sNextKey;
static otRadioKeyType sKeyType;
static int8_t GetRssi(uint16_t aChannel);
static bool IsTimeAfterOrEqual(uint32_t aTimeA, uint32_t aTimeB)
{
return (aTimeA - aTimeB) < (1U << 31);
}
static void ReverseExtAddress(otExtAddress *aReversed, const otExtAddress *aOrigin)
{
for (size_t i = 0; i < sizeof(*aReversed); i++)
@@ -493,20 +508,23 @@ otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *aInstance)
int8_t otPlatRadioGetRssi(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
assert(aInstance != NULL);
int8_t rssi = SIM_LOW_RSSI_SAMPLE;
uint8_t channel = sReceiveFrame.mChannel;
return GetRssi(sReceiveFrame.mChannel);
}
static int8_t GetRssi(uint16_t aChannel)
{
int8_t rssi = SIM_LOW_RSSI_SAMPLE;
uint32_t probabilityThreshold;
otEXPECT((SIM_RADIO_CHANNEL_MIN <= channel) && channel <= (SIM_RADIO_CHANNEL_MAX));
otEXPECT((SIM_RADIO_CHANNEL_MIN <= aChannel) && aChannel <= (SIM_RADIO_CHANNEL_MAX));
// To emulate a simple interference model, we return either a high or
// a low RSSI value with a fixed probability per each channel. The
// probability is increased per channel by a constant.
probabilityThreshold = (channel - SIM_RADIO_CHANNEL_MIN) * SIM_HIGH_RSSI_PROB_INC_PER_CHANNEL;
probabilityThreshold = (aChannel - SIM_RADIO_CHANNEL_MIN) * SIM_HIGH_RSSI_PROB_INC_PER_CHANNEL;
if (otRandomNonCryptoGetUint16() < (probabilityThreshold * 0xffff / 100))
{
@@ -736,7 +754,7 @@ void platformRadioReceive(otInstance *aInstance, uint8_t *aBuf, uint16_t aBufLen
radioReceive(aInstance);
}
#else
void platformRadioUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, int *aMaxFd)
void platformRadioUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, struct timeval *aTimeout, int *aMaxFd)
{
if (aReadFdSet != NULL && (sState != OT_RADIO_STATE_TRANSMIT || sTxWait))
{
@@ -757,6 +775,25 @@ void platformRadioUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, int *aMax
*aMaxFd = sTxFd;
}
}
if (sEnergyScanning)
{
struct timeval tv = {0, 0};
uint32_t now = otPlatAlarmMilliGetNow();
if (IsTimeAfterOrEqual(sEnergyScanEndTime, now))
{
uint32_t remaining = sEnergyScanEndTime - now;
tv.tv_sec = remaining / MS_PER_S;
tv.tv_usec = (remaining % MS_PER_S) * US_PER_MS;
}
if (timercmp(&tv, aTimeout, <))
{
*aTimeout = tv;
}
}
}
// no need to close in virtual time mode.
@@ -811,11 +848,16 @@ void platformRadioProcess(otInstance *aInstance, const fd_set *aReadFdSet, const
}
}
#endif
if (platformRadioIsTransmitPending())
{
radioSendMessage(aInstance);
}
if (sEnergyScanning && IsTimeAfterOrEqual(otPlatAlarmMilliGetNow(), sEnergyScanEndTime))
{
sEnergyScanning = false;
otPlatRadioEnergyScanDone(aInstance, sEnergyScanResult);
}
}
void radioTransmit(struct RadioMessage *aMessage, const struct otRadioFrame *aFrame)
@@ -982,13 +1024,22 @@ otError otPlatRadioEnergyScan(otInstance *aInstance, uint8_t aScanChannel, uint1
{
OT_UNUSED_VARIABLE(aInstance);
OT_UNUSED_VARIABLE(aScanChannel);
OT_UNUSED_VARIABLE(aScanDuration);
otError error = OT_ERROR_NONE;
assert(aInstance != NULL);
assert(aScanChannel >= SIM_RADIO_CHANNEL_MIN && aScanChannel <= SIM_RADIO_CHANNEL_MAX);
assert(aScanDuration > 0);
return OT_ERROR_NOT_IMPLEMENTED;
otEXPECT_ACTION((gRadioCaps & OT_RADIO_CAPS_ENERGY_SCAN), error = OT_ERROR_NOT_IMPLEMENTED);
otEXPECT_ACTION(!sEnergyScanning, error = OT_ERROR_BUSY);
sEnergyScanResult = GetRssi(aScanChannel);
sEnergyScanning = true;
sEnergyScanEndTime = otPlatAlarmMilliGetNow() + aScanDuration;
exit:
return error;
}
otError otPlatRadioGetTransmitPower(otInstance *aInstance, int8_t *aPower)
+15 -9
View File
@@ -70,10 +70,11 @@ static void handleSignal(int aSignal)
*/
enum
{
OT_SIM_OPT_HELP = 'h',
OT_SIM_OPT_SLEEP_TO_TX = 't',
OT_SIM_OPT_TIME_SPEED = 's',
OT_SIM_OPT_UNKNOWN = '?',
OT_SIM_OPT_HELP = 'h',
OT_SIM_OPT_ENABLE_ENERGY_SCAN = 'E',
OT_SIM_OPT_SLEEP_TO_TX = 't',
OT_SIM_OPT_TIME_SPEED = 's',
OT_SIM_OPT_UNKNOWN = '?',
};
static void PrintUsage(const char *aProgramName, int aExitCode)
@@ -82,9 +83,10 @@ static void PrintUsage(const char *aProgramName, int aExitCode)
"Syntax:\n"
" %s [Options] NodeId\n"
"Options:\n"
" -h --help Display this usage information.\n"
" -t --sleep-to-tx Let radio support direct transition from sleep to TX with CSMA.\n"
" -s --time-speed=val Speed up the time in simulation.\n",
" -h --help Display this usage information.\n"
" -E --enable-energy-scan Enable energy scan capability.\n"
" -t --sleep-to-tx Let radio support direct transition from sleep to TX with CSMA.\n"
" -s --time-speed=val Speed up the time in simulation.\n",
aProgramName);
exit(aExitCode);
@@ -97,6 +99,7 @@ void otSysInit(int aArgCount, char *aArgVector[])
static const struct option long_options[] = {
{"help", no_argument, 0, OT_SIM_OPT_HELP},
{"enable-energy-scan", no_argument, 0, OT_SIM_OPT_SLEEP_TO_TX},
{"sleep-to-tx", no_argument, 0, OT_SIM_OPT_SLEEP_TO_TX},
{"time-speed", required_argument, 0, OT_SIM_OPT_TIME_SPEED},
{0, 0, 0, 0},
@@ -112,7 +115,7 @@ void otSysInit(int aArgCount, char *aArgVector[])
while (true)
{
int c = getopt_long(aArgCount, aArgVector, "hts:", long_options, NULL);
int c = getopt_long(aArgCount, aArgVector, "Ehts:", long_options, NULL);
if (c == -1)
{
@@ -127,6 +130,9 @@ void otSysInit(int aArgCount, char *aArgVector[])
case OT_SIM_OPT_HELP:
PrintUsage(aArgVector[0], EXIT_SUCCESS);
break;
case OT_SIM_OPT_ENABLE_ENERGY_SCAN:
gRadioCaps |= OT_RADIO_CAPS_ENERGY_SCAN;
break;
case OT_SIM_OPT_SLEEP_TO_TX:
gRadioCaps |= OT_RADIO_CAPS_SLEEP_TO_TX;
break;
@@ -197,8 +203,8 @@ void otSysProcessDrivers(otInstance *aInstance)
FD_ZERO(&error_fds);
platformUartUpdateFdSet(&read_fds, &write_fds, &error_fds, &max_fd);
platformRadioUpdateFdSet(&read_fds, &write_fds, &max_fd);
platformAlarmUpdateTimeout(&timeout);
platformRadioUpdateFdSet(&read_fds, &write_fds, &timeout, &max_fd);
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
platformTrelUpdateFdSet(&read_fds, &write_fds, &timeout, &max_fd);
#endif
+1 -1
View File
@@ -53,7 +53,7 @@ extern "C" {
* @note This number versions both OpenThread platform and user APIs.
*
*/
#define OPENTHREAD_API_VERSION (208)
#define OPENTHREAD_API_VERSION (209)
/**
* @addtogroup api-instance
+1
View File
@@ -236,6 +236,7 @@ typedef void (*otLinkRawEnergyScanDone)(otInstance *aInstance, int8_t aEnergySca
* @param[in] aCallback A pointer to a function called on completion of a scanned channel.
*
* @retval OT_ERROR_NONE Successfully started scanning the channel.
* @retval OT_ERROR_BUSY The radio is performing enery scanning.
* @retval OT_ERROR_NOT_IMPLEMENTED The radio doesn't support energy scanning.
* @retval OT_ERROR_INVALID_STATE If the raw link-layer isn't enabled.
*
+1
View File
@@ -857,6 +857,7 @@ int8_t otPlatRadioGetRssi(otInstance *aInstance);
* @param[in] aScanDuration The duration, in milliseconds, for the channel to be scanned.
*
* @retval OT_ERROR_NONE Successfully started scanning the channel.
* @retval OT_ERROR_BUSY The radio is performing enery scanning.
* @retval OT_ERROR_NOT_IMPLEMENTED The radio doesn't support energy scanning.
*
*/
+1
View File
@@ -164,6 +164,7 @@ public:
* @param[in] aCallback A pointer to a function called on completion of a scanned channel.
*
* @retval kErrorNone Successfully started scanning the channel.
* @retval kErrorBusy The radio is performing energy scanning.
* @retval kErrorNotImplemented The radio doesn't support energy scanning.
* @retval kErrorInvalidState If the raw link-layer isn't enabled.
*
+1
View File
@@ -572,6 +572,7 @@ public:
* @param[in] aScanDuration The duration, in milliseconds, for the channel to be scanned.
*
* @retval kErrorNone Successfully started scanning the channel.
* @retval kErrorBusy The radio is performing energy scanning.
* @retval kErrorInvalidState The radio was disabled or transmitting.
* @retval kErrorNotImplemented Energy scan is not supported by radio link.
*
+1
View File
@@ -379,6 +379,7 @@ public:
* @param[in] aScanDuration The duration, in milliseconds, for the channel to be scanned.
*
* @retval kErrorNone Successfully started scanning the channel.
* @retval kErrorBusy The radio is performing energy scanning.
* @retval kErrorInvalidState The radio was disabled or transmitting.
* @retval kErrorNotImplemented Energy scan is not supported (applicable in link-raw/radio mode only).
*
+1
View File
@@ -522,6 +522,7 @@ public:
* @param[in] aScanDuration The duration, in milliseconds, for the channel to be scanned.
*
* @retval kErrorNone Successfully started scanning the channel.
* @retval kErrorBusy The radio is performing energy scanning.
* @retval kErrorNotImplemented The radio doesn't support energy scanning.
*
*/
+42
View File
@@ -0,0 +1,42 @@
#!/usr/bin/expect -f
#
# Copyright (c) 2022, The OpenThread Authors.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# 3. Neither the name of the copyright holder nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
source "tests/scripts/expect/_common.exp"
spawn_node 1 "rcp" "spinel+hdlc+forkpty://$::env(OT_SIMULATION_APPS)/ncp/ot-rcp?forkpty-arg=1&forkpty-arg=-E&no-reset=1"
send "ifconfig up\n"
expect_line "Done"
# Actually returns last otPlatRadioGetRssi
send "scan energy 0\n"
expect_line "Done"
# Actually performs energy scan on RCP
send "scan energy 100\n"
expect_line "Done"
dispose_all