1 /* 2 * Copyright (c) 2018, The OpenThread Authors. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 3. Neither the name of the copyright holder nor the 13 * names of its contributors may be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 /** 30 * @file 31 * This file includes definitions for channel monitoring module. 32 */ 33 34 #ifndef CHANNEL_MONITOR_HPP_ 35 #define CHANNEL_MONITOR_HPP_ 36 37 #include "openthread-core-config.h" 38 39 #if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE 40 41 #include <openthread/platform/radio.h> 42 43 #include "common/locator.hpp" 44 #include "common/non_copyable.hpp" 45 #include "common/timer.hpp" 46 #include "mac/mac.hpp" 47 #include "radio/radio.hpp" 48 49 namespace ot { 50 namespace Utils { 51 52 /** 53 * @addtogroup utils-channel-monitor 54 * 55 * @brief 56 * This module includes definitions monitoring quality of channels. 57 * 58 * @{ 59 */ 60 61 /** 62 * Implements the channel monitoring logic. 63 * 64 * Channel Monitoring will periodically monitor all channels to help determine the cleaner channels (channels 65 * with less interference). 66 * 67 * When Channel Monitoring is active, every `kSampleInterval`, a zero-duration Energy Scan is performed on every 68 * channel collecting a single RSSI sample per channel. The RSSI samples are compared with a pre-specified RSSI 69 * threshold `kRssiThreshold`. As an indicator of channel quality, the `ChannelMonitor` maintains and provides the 70 * average rate/percentage of RSSI samples that are above the threshold within (approximately) a specified sample 71 * window (referred to as "channel occupancy"). 72 */ 73 class ChannelMonitor : public InstanceLocator, private NonCopyable 74 { 75 public: 76 /** 77 * The channel RSSI sample interval in milliseconds. 78 */ 79 static constexpr uint32_t kSampleInterval = OPENTHREAD_CONFIG_CHANNEL_MONITOR_SAMPLE_INTERVAL; 80 81 /** 82 * The RSSI threshold in dBm. 83 * 84 * It is recommended that this value is set to same value as the CCA threshold used by radio. 85 */ 86 static constexpr int8_t kRssiThreshold = OPENTHREAD_CONFIG_CHANNEL_MONITOR_RSSI_THRESHOLD; 87 88 /** 89 * The averaging sample window length (in units of sample interval). 90 */ 91 static constexpr uint32_t kSampleWindow = OPENTHREAD_CONFIG_CHANNEL_MONITOR_SAMPLE_WINDOW; 92 93 /** 94 * Initializes the object. 95 * 96 * @param[in] aInstance A reference to the OpenThread instance. 97 */ 98 explicit ChannelMonitor(Instance &aInstance); 99 100 /** 101 * Starts the Channel Monitoring operation. 102 * 103 * Once started, any previously collected data is cleared. 104 * 105 * @retval kErrorNone Channel Monitoring started successfully. 106 * @retval kErrorAlready Channel Monitoring has already been started. 107 */ 108 Error Start(void); 109 110 /** 111 * Stops the Channel Monitoring operation. 112 * 113 * @note After `Stop()`, the previous data is still valid and can be read. 114 * 115 * @retval kErrorNone Channel Monitoring stopped successfully. 116 * @retval kErrorAlready Channel Monitoring has already been stopped. 117 */ 118 Error Stop(void); 119 120 /** 121 * Indicates whether the Channel Monitoring operation is started and running. 122 * 123 * @returns TRUE if the Channel Monitoring operation is running, FALSE otherwise. 124 */ IsRunning(void) const125 bool IsRunning(void) const { return mTimer.IsRunning(); } 126 127 /** 128 * Clears all currently stored data. 129 */ 130 void Clear(void); 131 132 /** 133 * Returns the total number of RSSI samples (per channel) taken so far (since call to `Start()`). 134 * 135 * @returns total number of RSSI sample taken since last call to `Start()`. 136 */ GetSampleCount(void) const137 uint32_t GetSampleCount(void) const { return mSampleCount; } 138 139 /** 140 * Returns the current channel occupancy for a given channel. 141 * 142 * The channel occupancy represents the average rate/percentage of RSSI samples that were above RSSI threshold 143 * `kRssiThreshold` ("bad" RSSI samples). 144 * 145 * For the first `kSampleWindow` samples, the average is maintained as the actual percentage (i.e., ratio of number 146 * of "bad" samples by total number of samples). After `kSampleWindow` samples, the averager uses an exponentially 147 * weighted moving average logic with weight coefficient `1/kSampleWindow` for new values. Practically, this means 148 * the occupancy is representative of up to `3 * kSampleWindow` last samples with highest weight given to the 149 * latest `kSampleWindow` samples. 150 * 151 * Max value of `0xffff` indicates all RSSI samples were above RSSI threshold (i.e. 100% of samples were "bad"). 152 * 153 * @param[in] aChannel The channel for which to get the link occupancy. 154 * 155 * @returns the current channel occupancy for the given channel. 156 */ 157 uint16_t GetChannelOccupancy(uint8_t aChannel) const; 158 159 /** 160 * Finds the best channel(s) (with least occupancy rate) in a given channel mask. 161 * 162 * The channels are compared based on their occupancy rate from `GetChannelOccupancy()` and lower occupancy rate 163 * is considered better. 164 * 165 * @param[in] aMask A channel mask (the search is limited to channels in @p aMask). 166 * @param[out] aOccupancy A reference to `uint16` to return the occupancy rate associated with best channel(s). 167 * 168 * @returns A channel mask containing the best channels. A mask is returned in case there are more than one 169 * channel with the same occupancy rate value. 170 */ 171 Mac::ChannelMask FindBestChannels(const Mac::ChannelMask &aMask, uint16_t &aOccupancy) const; 172 173 private: 174 #if (OPENTHREAD_CONFIG_RADIO_2P4GHZ_OQPSK_SUPPORT && OPENTHREAD_CONFIG_RADIO_915MHZ_OQPSK_SUPPORT) 175 static constexpr uint8_t kNumChannelMasks = 8; 176 #else 177 static constexpr uint8_t kNumChannelMasks = 4; 178 #endif 179 static constexpr uint8_t kNumChannels = (Radio::kChannelMax - Radio::kChannelMin + 1); 180 static constexpr uint32_t kTimerInterval = (kSampleInterval / kNumChannelMasks); 181 static constexpr uint16_t kMaxJitterInterval = 4096; 182 static constexpr uint32_t kMaxOccupancy = 0xffff; 183 184 void HandleTimer(void); 185 static void HandleEnergyScanResult(Mac::EnergyScanResult *aResult, void *aContext); 186 void HandleEnergyScanResult(Mac::EnergyScanResult *aResult); 187 void LogResults(void); 188 189 using ScanTimer = TimerMilliIn<ChannelMonitor, &ChannelMonitor::HandleTimer>; 190 191 static const uint32_t mScanChannelMasks[kNumChannelMasks]; 192 193 uint8_t mChannelMaskIndex : 3; 194 uint32_t mSampleCount : 29; 195 uint16_t mChannelOccupancy[kNumChannels]; 196 ScanTimer mTimer; 197 }; 198 199 /** 200 * @} 201 */ 202 203 } // namespace Utils 204 } // namespace ot 205 206 #endif // OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE 207 208 #endif // CHANNEL_MONITOR_HPP_ 209