classdef helperBLEDeviceModel < handle
%helperBLEDeviceModel Create an object for Bluetooth Low Energy (BLE)
%device
%   BLEDEVICE = helperBLEDeviceModel creates an object, BLEDEVICE, for a
%   BLE device which is capable of transmitting and receiving BLE packets,
%   selecting a new channel for each communication and also classifying the
%   channels into good or bad channels based on packet failures.
%
%   BLEDEVICE = helperBLEDeviceModel(Name, Value) creates an object for BLE
%   device, BLEDEVICE, with the specified property Name set to the
%   specified Value. You can specify additional name-value pair arguments
%   in any order as (Name1, Value1, ..., NameN, ValueN).
%
%   helperBLEDeviceModel methods:
%
%   init     - Initialize the BLE device
%   run      - Run the BLE device for one time step
%
%   helperBLEDeviceModel properties:
%
%   Role                  - BLE device role in piconet
%   SlavesCount           - Number of Slaves to connect with the Master
%   ChannelClassification - BLE channel classification configuration object

%   Copyright 2018-2019 The MathWorks, Inc.

properties
    %Role The role of the BLE device in a piconet
    %   Specify the role as one of 'Master' | 'Slave'. It indicates the
    %   link layer role of the BLE device. The default value is 'Master'.
    Role = 'Master'
    
    %SlavesCount Number of Slaves to connect with the Master
    %   Specify the Slaves count as an integer in the range of [1, 5] in
    %   case the <a
    %   href="matlab:help('helperBLEDeviceModel.Role')">Role</a> is set to
    %   'Master'. This parameter is not applicable for the <a
    %   href="matlab:help('helperBLEDeviceModel.Role')">Role</a> is set to
    %   'Slave'. It indicates the number of Slaves that the Master is
    %   connected with. The default value is 1.
    SlavesCount = 1
    
    %ChannelClassification BLE channel classification configuration object
    %   Specify this property as a configuration object of type <a
    %   href="matlab:help('helperBLEChannelClassification')">helperBLEChannelClassification</a>.
    %   The BLE channel classification parameters are configured using
    %   settings of this configuration object. This property is applicable
    %   only when <a
    %   href="matlab:help('helperBLEDeviceModel.Role')">Role</a> is set to
    %   'Master'. The default value is an object of type
    %   'helperBLEChannelClassification' with all properties set to their
    %   default values.
    ChannelClassification
end

properties(Hidden)
    %LLConnectionConfigs List of LL connection configuration objects
    %   Specify this property as a list of configuration objects. Each
    %   configuration is an object of type <a
    %   href="matlab:help('helperBLEConnectionStateModel')">helperBLEConnectionStateModel</a>.
    %   Each Link Layer connection is represented using settings of this
    %   configuration object. Size of this list is dependent on <a
    %   href="matlab:help('helperBLEDeviceModel.SlavesCount')">SlavesCount</a>.
    %   The default value is list with an object of type
    %   'helperBLEConnectionStateModel' with all properties set to their default
    %   values.
    LLConnectionConfigs
    
    %CoexistenceVisualization Visualization object
    %   Specify this property as an objects of type <a
    %   href="matlab:help('helperBLEVisualizeCoexistence')">helperBLEVisualizeCoexistence</a>.
    %   It is used to visualize the BLE-WLAN coexistence behavior. The
    %   default value is an object of type 'helperBLEVisualizeCoexistence'
    %   with all properties set to their default values.
    CoexistenceVisualization
end

properties(Constant, Hidden)
    %StepTimerUnit Time unit for single step in microseconds
    % One step time is considered as 25 microseconds. All the timing
    % parameters (connection interval, scan interval, advertising interval,
    % etc.) in BLE specification are multiple of 625 microseconds. The
    % maximum packet size used in this example is 33 octets (264 bits). The
    % packet transmission time in different PHY modes are: 264 microseconds
    % (in LE1M), 132 microseconds (in LE2M), 528 microseconds (in LE500K)
    % and 1056 microseconds (in LE125K). Therefore, the step time is
    % considered as 25 microseconds (625 is multiple of 25) to achieve a
    % trade-off between the simulation time and accuracy.
    StepTimerUnit = 25
    
    RoleValues = {'Master', 'Slave'}
    
    DefaultAccessAddress = '12345678'
end

properties(SetAccess = private)
    %ActiveChannel Current transmitting or receiving channel
    ActiveChannel = -1
    
    ActiveAccessAddress = '12345678'
    
    ActiveConnectionIdxs = zeros(1, 5);
end

properties(Access = private)
    %pConnectionsStatus States of all the connections
    pConnectionsStatus
    
    %pActiveConnectionIdx Active connection index
    pActiveConnectionIdx = 1
    
    pOutputPacket = struct('LLPDU',[], ...
                           'AccessAddress','', ...
                           'RateIndex','', ...
                           'ChannelIndex',-1);
    
    pPacketInfo = struct('MoreData',-1, ...
                         'CRCFail',-1);
end

methods
    function obj = helperBLEDeviceModel(varargin)
        % Apply constructor name value pairs:
        obj.ChannelClassification = helperBLEChannelClassification;
        obj.LLConnectionConfigs = helperBLEConnectionStateModel;
        for idx = 1:2:nargin
            obj.(varargin{idx}) = varargin{idx+1};
        end
    end
    
    % For auto completion:
    function v = set(obj, prop)
        v = obj.([prop, 'Values']);
    end
    
    function set.Role(obj, value)
        obj.Role = validatestring(value, obj.RoleValues, mfilename, 'Role');
    end
    
    function set.SlavesCount(obj, value)
        validateattributes(value, {'numeric'}, {'scalar', 'integer', ...
            '>=', 1, '<=', 5}, mfilename, 'SlavesCount');
        obj.SlavesCount = value;
    end
    
    function set.ChannelClassification(obj, value)
        validateattributes(value, {'helperBLEChannelClassification'}, {'scalar'}, ...
            mfilename, 'ChannelClassification');
        obj.ChannelClassification = value;
    end
    
    function set.LLConnectionConfigs(obj, value)
        validateattributes(value, {'helperBLEConnectionStateModel'}, {'vector'}, ...
            mfilename, 'LLConnectionConfigs');
        obj.LLConnectionConfigs = value;
    end
    
    function set.CoexistenceVisualization(obj, value)
        validateattributes(value, {'helperBLEVisualizeCoexistence'}, {'scalar'}, ...
            mfilename, 'CoexistenceVisualization');
        obj.CoexistenceVisualization = value;
    end
end

methods
    % Init functionality
    function init(obj)
        if strcmp(obj.Role, 'Slave') && (obj.SlavesCount ~= 1)
            error('For a "Slave" device "SlavesCount" should be set to 1.');
        end
        
        for idx = 1:obj.SlavesCount
            initialize(obj.LLConnectionConfigs(idx), obj.ChannelClassification);
        end
        
        % Initialize the states of all the connections
        obj.pConnectionsStatus = zeros(1, obj.SlavesCount);
        for idx = 1:obj.SlavesCount
            obj.pConnectionsStatus(idx) = helperBLELLConnectionEventStatus.Idle;
        end
        
        obj.ActiveConnectionIdxs = zeros(1, numel(obj.LLConnectionConfigs)) * -1;
        
        for idx = 1:numel(obj.LLConnectionConfigs)
            obj.ActiveConnectionIdxs(idx) = idx;
        end
    end
    
    % Run functionality
    function outPacket = run(obj, inPacket)
        % Initialize output packet
        outPacket = obj.pOutputPacket;

        % Initialize packet info
        inPacketInfo = obj.pPacketInfo;
        
        isPacketLost = false;
        
        indexes = obj.ActiveConnectionIdxs(obj.ActiveConnectionIdxs ~= -1);
        
        % All the Slaves are disconnected from the Master
        if (numel(indexes) == 0) || (obj.pActiveConnectionIdx == 0)
            return
        end
        
        activeConnectionIdx = obj.pActiveConnectionIdx;
        
        activeConnection = obj.LLConnectionConfigs(activeConnectionIdx);
        
        % Connection is active (either transmit or receive)
        if (obj.pConnectionsStatus(activeConnectionIdx) ~= ...
                helperBLELLConnectionEventStatus.Idle)
            % Update channel map at "instant" connection event
            if (activeConnection.ConnectionEvent.EventCounter == activeConnection.Instant)
                updateChannelMap(obj, activeConnectionIdx);
            end
            
            indexes = obj.ActiveConnectionIdxs(obj.ActiveConnectionIdxs ~= -1);
            
            % All the Slaves are disconnected from the Master
            if (numel(indexes) == 0) || (obj.pActiveConnectionIdx == 0)
                return
            end
            
            if (obj.pConnectionsStatus(activeConnectionIdx) == helperBLELLConnectionEventStatus.Transmit)
               % Transmit path
               if activeConnection.RetransmitFlag
                   % Retransmission
                   if isempty(activeConnection.PreviousTxPacket.LLPDU)
                       outPacket = generatePacket(activeConnection, 'Random');
                   else
                       outPacket = activeConnection.PreviousTxPacket;
                       outPacket.LLPDU = updateCounters(activeConnection, ...
                           outPacket.LLPDU);
                   end
                   outPacket.ChannelIndex = activeConnection.CurrentChannel;
                   
               elseif activeConnection.IsChannelListUpdated
                   % Transmit Channel map update packet
                   activeConnection.Instant = ...
                       activeConnection.ConnectionEvent.EventCounter + activeConnection.InstantOffset;
                   
                   outPacket = generatePacket(activeConnection, 'ChannelMapUpdate');
                   
                   activeConnection.ChannelMapUpdateFlag = true;
                   activeConnection.IsChannelListUpdated = false;
               elseif activeConnection.SendResponse
                   activeConnection.SendResponse = false;
                   % Send the response packet
                   outPacket.LLPDU = activeConnection.ResponsePacket.LLPDU;
                   outPacket.AccessAddress = activeConnection.AccessAddress;
                   outPacket.ChannelIndex = activeConnection.CurrentChannel;
               else
                   % Transmit a random packet
                   outPacket = generatePacket(activeConnection, 'Random');
               end
               activeConnection.PreviousTxPacket = outPacket;
           else
               outPacket.ChannelIndex = activeConnection.CurrentChannel;
               % Received a packet
               if ~(isempty(inPacket.LLPDU))
                   [responsePacket, inPacketInfo] = receivePacket(obj, ...
                       activeConnectionIdx, inPacket, inPacketInfo);
                   if ~isempty(responsePacket.LLPDU)
                       activeConnection.SendResponse = true;
                       activeConnection.ResponsePacket = responsePacket;
                       outPacket.ChannelIndex = -1;
                   end
               else
                    isPacketLost = true;
               end
           end
        else
            if strcmp(obj.Role, 'Master')
                if strcmp(obj.ChannelClassification.BadChannelClassificationMethod, ...
                        'Using energy detections') && (activeConnection.PollBadChannels)
                    % Observe bad channels in idle time
                    observeBadChannels(obj, activeConnectionIdx);
                    activeConnection.PollBadChannels = false;
                end
            end
        end
        
        updateConnectionsStatus(obj, inPacketInfo);
        
        if (isPacketLost) && (obj.pConnectionsStatus(activeConnectionIdx) == ...
                helperBLELLConnectionEventStatus.Idle)
            if strcmp(obj.Role, 'Master')
                % Response packet lost
                if activeConnection.RetransmitFlag && ...
                        ~activeConnection.ResponseReceivedFlag
                    activeConnection.ResponseReceivedFlag = true;
                    
                    % Current BLE channel number
                    channelNumber = activeConnection.CurrentChannel;
                    
                    % Received packet index in received packets
                    % status array
                    rxIdx = activeConnection.RxIdx(channelNumber);
                    
                    % Increment the error packets received count
                    activeConnection.TotalRxFailureCount(channelNumber) = ...
                        activeConnection.TotalRxFailureCount(channelNumber)+1;
                    
                    % Increment the received packets count
                    activeConnection.TotalRxCount(channelNumber) = ...
                        activeConnection.TotalRxCount(channelNumber)+1;
                    
                    % Rx status statistics
                    activeConnection.LastNRxStatus(channelNumber, rxIdx) = 0;
                    
                    % Increment RxIdx
                    activeConnection.RxIdx(channelNumber) = ...
                        activeConnection.RxIdx(channelNumber)+1;
                    
                    % Reset the RxIdx to 1, if it exceeds size of
                    % the LastNRxStatus array
                    if (activeConnection.RxIdx(channelNumber) > ...
                            obj.ChannelClassification.RxStatusCount)
                        activeConnection.RxIdx(channelNumber) = 1;
                    end
                    
                    % Update the visualization
                    overallSuccessRate = getOverallSuccessRate(obj, activeConnectionIdx);
                    recentSuccessRate = getRecentSuccessRate(obj, activeConnectionIdx);
                    obj.CoexistenceVisualization.SlaveNumber = activeConnectionIdx;
                    obj.CoexistenceVisualization.Action = 'Corrupted ble channel';
                    obj.CoexistenceVisualization.BLEChannelNumber = channelNumber;
                    obj.CoexistenceVisualization.OverallSuccessRate = overallSuccessRate;
                    obj.CoexistenceVisualization.RecentSuccessRate = recentSuccessRate;
                    viewModel(obj.CoexistenceVisualization);
                end
            end
        end
    end
end

methods(Hidden)
    function overallSuccessRate = getOverallSuccessRate(obj, activeConnectionIdx)
    % Get the overall success rate from the simulation starts for each
    % channel
        activeConnection = obj.LLConnectionConfigs(activeConnectionIdx);
        overallSuccessRate = zeros(1, 37);
        for idx = 1:activeConnection.DataChannelsCount
            % Total packets received
            rxCount = activeConnection.TotalRxCount(idx);
            
            if rxCount ~= 0
                % Total error packets received
                rxFailureCount = activeConnection.TotalRxFailureCount(idx);
                
                % Success rate
                overallSuccessRate(idx) = ...
                    round(((rxCount - rxFailureCount)/rxCount)*100);
            end
        end
    end
end

methods(Access = private)
    function activeConnectionIdx = getActiveConnectionIdx(obj)
        % Only one connection will be active at any instance of time
        
        % Select a random connection for updating the energy detection
        % statistics when all the connections are idle
        indexes = obj.ActiveConnectionIdxs(obj.ActiveConnectionIdxs ~= -1);
        activeConnectionIdx = indexes(randi([1 numel(indexes)]));
        for idx = 1:numel(obj.LLConnectionConfigs)
            if obj.pConnectionsStatus(idx) ~= helperBLELLConnectionEventStatus.Idle
                activeConnectionIdx = idx;
                break;
            end
        end
        if obj.pConnectionsStatus(activeConnectionIdx) ~= helperBLELLConnectionEventStatus.Idle
            obj.ActiveChannel = obj.LLConnectionConfigs(activeConnectionIdx).CurrentChannel;
            obj.ActiveAccessAddress = obj.LLConnectionConfigs(activeConnectionIdx).AccessAddress;
        else
            obj.ActiveChannel = -1;
            obj.ActiveAccessAddress = '';
        end
    end
    
    function updateConnectionsStatus(obj, inPacketInfo)
        indexes = obj.ActiveConnectionIdxs(obj.ActiveConnectionIdxs ~= -1);
        for idx = 1:numel(indexes)
            obj.pConnectionsStatus(indexes(idx)) = updateConnection(obj.LLConnectionConfigs(indexes(idx)), ...
                inPacketInfo, obj.ChannelClassification);
        end
        
        obj.pActiveConnectionIdx = getActiveConnectionIdx(obj);
    end
    
    function updateChannelMap(obj, activeConnectionIdx)
        % Active connection
        activeConnection = obj.LLConnectionConfigs(activeConnectionIdx);
        
        % If the Master didn't yet receive a response to channel map update
        % packet, terminate connection
        if (strcmp(obj.Role, 'Master') && activeConnection.ChannelMapUpdateFlag)
            % Update the visualization
            obj.CoexistenceVisualization.Action = 'Failed to update channel map';
            obj.CoexistenceVisualization.SlaveNumber = activeConnectionIdx;
            viewModel(obj.CoexistenceVisualization);
            
            % Reset the terminated connection context
            obj.ActiveConnectionIdxs(obj.ActiveConnectionIdxs == activeConnectionIdx) = -1;
            obj.pConnectionsStatus(activeConnectionIdx) = helperBLELLConnectionEventStatus.Idle;
            
            disp(['Master failed to send channel map update packet to ' ...
                'Slave-' num2str(activeConnectionIdx) ' ("' ...
                activeConnection.AccessAddress '"), Slave has to re-connect']);
        end
        if strcmp(obj.Role, 'Slave')
            % Update channel map
            activeConnection.ChannelSelection.UsedChannels = ...
                find(activeConnection.NewChannelMap)-1;
        else
            if ~activeConnection.ChannelMapUpdateFlag
                activeConnection.ChannelSelection.UsedChannels = ...
                    find(activeConnection.NewChannelMap)-1;
                
                % Update success count
                overallSuccessRate = zeros(1, 37);
                for idx = 1:activeConnection.DataChannelsCount
                    % Total packets received
                    rxCount = activeConnection.TotalRxCount(idx);
                    
                    if rxCount ~= 0
                        % Total error packets received
                        rxFailureCount = activeConnection.TotalRxFailureCount(idx);
                        
                        % Success rate
                        overallSuccessRate(idx) = ...
                            round(((rxCount - rxFailureCount)/rxCount)*100);
                    end
                end
                
                obj.CoexistenceVisualization.Action = 'Update channel map';
                obj.CoexistenceVisualization.SlaveNumber = activeConnectionIdx;
                obj.CoexistenceVisualization.ChannelMap = activeConnection.NewChannelMap;
                obj.CoexistenceVisualization.OverallSuccessRate = overallSuccessRate;
                viewModel(obj.CoexistenceVisualization);
            end
        end
    end
    
    function recentSuccessRate = getRecentSuccessRate(obj, activeConnectionIdx)
    % Get the recent success rate for each channel
        activeConnection = obj.LLConnectionConfigs(activeConnectionIdx);
        recentSuccessRate = zeros(1, 37);
        for idx = 1:activeConnection.DataChannelsCount
            % Total packets received
            rxCount = sum(activeConnection.LastNRxStatus(idx, :) ~= -1);
            
            rxFailureCount = sum(activeConnection.LastNRxStatus(idx, :) == 0);
            
            % Success rate
            recentSuccessRate(idx) = ...
                round(((rxCount - rxFailureCount)/rxCount)*100);
        end
    end
    
    function [response, inPacketInfo] = receivePacket(obj, activeConnectionIdx, ...
            inPacket, inPacketInfo)
        %receivePacket Decodes the given link layer packet. If a valid packet
        %is received returns the response packet if applicable. Otherwise
        %empty packet will be returned
        
        activeConnection = obj.LLConnectionConfigs(activeConnectionIdx);
        
        response = struct('LLPDU',[], ...
                          'AccessAddress','', ...
                          'RateIndex','', ...
                          'ChannelIndex',-1);
        
        % Current BLE channel number
        channelNumber = activeConnection.CurrentChannel;
        
        % Increment the received packets count
        activeConnection.TotalRxCount(channelNumber) = ...
            activeConnection.TotalRxCount(channelNumber)+1;
        
        accessAddressFailed = 0;
        crcFailed = 0;
        
        if strcmp(inPacket.AccessAddress, obj.ActiveAccessAddress)
            if strcmp(obj.Role, 'Master')
                % Response received
                activeConnection.ResponseReceivedFlag = true;
            end
            
            % Decode packet
            [status, cfg] = bleLLDataChannelPDUDecode(inPacket.LLPDU, ...
                activeConnection.CRCInitialization);
            
            if status == blePacketDecodeStatus.CRCFailed
                crcFailed = 1;
                % Increment the error packets received count
                activeConnection.TotalRxFailureCount(channelNumber) = ...
                    activeConnection.TotalRxFailureCount(channelNumber)+1;
            end
            
            inPacketInfo.MoreData = double(cfg.MoreData);
            inPacketInfo.CRCFail = crcFailed;
            
            % Received packet CRC passed
            if ~crcFailed
                % Verify the received packet sequence number with NESN
                if (activeConnection.NextExpectedSequenceCounter == cfg.SequenceNumber)
                    activeConnection.NextExpectedSequenceCounter = ~activeConnection.NextExpectedSequenceCounter;
                end
                
                if (cfg.NESN ~= activeConnection.SequenceCounter) || isempty(activeConnection.PreviousTxPacket.LLPDU)
                    activeConnection.RetransmitFlag = false;
                    activeConnection.SequenceCounter = ~activeConnection.SequenceCounter;
                end
                
                % Slave
                if strcmp(obj.Role, 'Slave')
                    
                    % If the channel map update packet is received
                    if strcmp(cfg.LLID, 'Control') && ...
                            strcmp(cfg.ControlConfig.Opcode, 'Channel map indication')
                        chMap = false(1, activeConnection.DataChannelsCount);
                        chMap(cfg.ControlConfig.UsedChannels+1) = true;
                        activeConnection.Instant = cfg.ControlConfig.Instant;
                        activeConnection.NewChannelMap = chMap;
                    end
                    
                    if activeConnection.RetransmitFlag
                        % Retransmission
                        response = activeConnection.PreviousTxPacket;
                        
                    else
                        % Transmit a random packet
                        response = generatePacket(activeConnection, 'Random');
                    end
                    
                else % Master
                    
                    % Previous Tx successful
                    if ~activeConnection.RetransmitFlag
                        % Response received to channel map update
                        if activeConnection.ChannelMapUpdateFlag
                            [status, tmp] = bleLLDataChannelPDUDecode(...
                                activeConnection.PreviousTxPacket.LLPDU, activeConnection.CRCInitialization);
                            if status == blePacketDecodeStatus.Success
                                activeConnection.Instant = tmp.ControlConfig.Instant;
                                chMap = false(1, activeConnection.DataChannelsCount);
                                chMap(tmp.ControlConfig.UsedChannels+1) = true;
                                activeConnection.NewChannelMap = chMap;
                                activeConnection.ChannelMapUpdateFlag = false;
                            end
                        end
                    end
                end
            else % Received packet CRC Failed
                if ~isempty(activeConnection.PreviousTxPacket.LLPDU)
                    activeConnection.RetransmitFlag = true;
                end
            end
            % Extract received packet index from received packets status array
            rxIdx = activeConnection.RxIdx(channelNumber);
            
            % Rx status statistics
            activeConnection.LastNRxStatus(channelNumber, rxIdx) = ~crcFailed;
        else
            accessAddressFailed = 1;
            
            % Extract received packet index from received packets status array
            rxIdx = activeConnection.RxIdx(channelNumber);
            
            % Rx status statistics
            activeConnection.LastNRxStatus(channelNumber, rxIdx) = ~accessAddressFailed;
            
            % Increment the error packets received count
            activeConnection.TotalRxFailureCount(channelNumber) = ...
                activeConnection.TotalRxFailureCount(channelNumber)+1;
        end
        
        % Increment RxIdx
        activeConnection.RxIdx(channelNumber) = activeConnection.RxIdx(channelNumber)+1;
        
        % Reset the RxIdx to 1, if it exceeds size of the LastNRxStatus
        % array
        if (activeConnection.RxIdx(channelNumber) > obj.ChannelClassification.RxStatusCount)
            activeConnection.RxIdx(channelNumber) = 1;
        end
        
        if strcmp(obj.Role, 'Master')
            % Update the visualization
            overallSuccessRate = getOverallSuccessRate(obj, activeConnectionIdx);
            recentSuccessRate = getRecentSuccessRate(obj, activeConnectionIdx);
            obj.CoexistenceVisualization.SlaveNumber = activeConnectionIdx;
            obj.CoexistenceVisualization.BLEChannelNumber = channelNumber;
            obj.CoexistenceVisualization.OverallSuccessRate = overallSuccessRate;
            obj.CoexistenceVisualization.RecentSuccessRate = recentSuccessRate;
            if crcFailed || accessAddressFailed
                obj.CoexistenceVisualization.Action = 'Corrupted ble channel';
            else
                obj.CoexistenceVisualization.Action = 'Active ble channel';
            end
            viewModel(obj.CoexistenceVisualization);
        end
    end
    
    function observeBadChannels(obj, activeConnectionIdx)
        %observeBadChannels Observe all the bad channels for energy detection
        %indications and update the statistics.
        
        activeConnection = obj.LLConnectionConfigs(activeConnectionIdx);
        
        % Check for energy detection(ED) indication in bad channels
        for idx = 1:activeConnection.DataChannelsCount
            if activeConnection.ChannelMap(idx)
                % Ignore if the channel is a good channel
                continue;
            end
            
            % Randomly choosing whether the channel is busy or not
            activeConnection.ChannelBusyStatus(idx, ...
                activeConnection.ChannelBusyStatusIdx(idx)) = (rand() < 0.3);
            
            % Increment ChannelBusyStatusIdx
            activeConnection.ChannelBusyStatusIdx(idx) = ...
                activeConnection.ChannelBusyStatusIdx(idx)+1;
            
            % Reset the ChannelBusyStatusIdx to 1, if it exceeds size of
            % the ChannelBusyStatus array
            if (activeConnection.ChannelBusyStatusIdx(idx) > ...
                    obj.ChannelClassification.EDIndicationsCount)
                activeConnection.ChannelBusyStatusIdx(idx) = 1;
            end
        end
    end
end
end
