% helperBLEMeshLPNLifetimeVSPolltimeout Script to compute the lifetime of
% Bluetooth mesh Low Power node for various poll timeout and receive
% window values.
%
% Bluetooth mesh network default configuration: Network consists of 55
% Bluetooth mesh nodes. Out of these, 30 nodes are configured as relay
% nodes, one pair is configured as Low Power & Friend nodes and one pair is
% configured as source & destination nodes. Remaining nodes are configured
% as end nodes. Mesh messages are originated at the configured source node
% and transmitted to configured destination node. Friend node also acts as
% a relay node.
%
% The lifetime of a node is calculated based on battery capacity,
% self-discharge current and average current consumption.
%
% This script generates a lifetime plot as a function of poll timeout with
% different receive window values. The results obtained from the lifetime
% plot concludes that lifetime increases with increase in poll timeout and
% decreases with increase in receive window for a Bluetooth mesh Low Power
% node. This script also displays a table with the statistics of Bluetooth
% mesh Low Power node and Friend node.

% Copyright 2019 The MathWorks, Inc. 

% Battery capacity in mAh
batteryCapacity = 1200;

% Self-discharge current of the battery. It is a constant value as a
% function of time. Here, self-discharge rate is equal to 1%/year of its
% initial capacity i.e. self-discharge current = (0.01*batteryCapacity
% mAh)/(365*24 h)
iSelfDischarge = (0.01*batteryCapacity)/(365*24);

% Current consumed during transmission on three advertising channels in mA
iTxch37 = 7.57;
iTxch38 = 7.77;
iTxch39 = 7.70;

% Current consumed during listening in mA
iListen = 10.3;

% Current consumed during sleep state in mA
iSleep = 0.20;

% Current consumed during idle state in mA
iIdle = 1.19;

% Average transmission current
iTxAvg = (iTxch37 + iTxch38 + iTxch39)/3;

% Different poll timeout values
pollTimeoutValues = [5; 10; 100; 1000; 10000; 100000];
ptsize = size(pollTimeoutValues,1);

% Different receive window values
receiveWindowValues = [120; 200; 255];
rwsize = size(receiveWindowValues,1);

% Pre-allocate the size of statistics of Low Power node
nTx = zeros(ptsize, rwsize);
nTxBytes = zeros(ptsize, rwsize);
nTxBits = zeros(ptsize, rwsize);
nRx = zeros(ptsize, rwsize);
nRxBytes = zeros(ptsize, rwsize);
listenTime = zeros(ptsize, rwsize);
sleepTime = zeros(ptsize, rwsize);
idleTime = zeros(ptsize, rwsize);

% Pre-allocate the size of current and lifetime of Low Power node
currentConsumption = zeros(ptsize, rwsize);
avgCurrentConsumption = zeros(ptsize, rwsize);
lifetime = zeros(ptsize, rwsize);
lifetimeInDays = zeros(ptsize, rwsize);

% Initialize the receive window index
receiveWindowIdx = 1;

% Number of nodes in Bluetooth mesh network
totalNodes = 55;

% Default random seed
rng('default');

% Load node positions
load('bleMeshNodesPositions.mat');

% Simulation time, in milliseconds
simulationTime = 900*1000;

% Enable/disable visualization
enableVisualization = false;

% Enable/disable the animation in the visualization. If the
% "enableVisualization" is set to false, "enableAnimation" is not
% considered.
enableAnimation = false;

% Source destination pairs - sample mesh message originates at the source
% node and delivered to the destination node
sourceDestinationPairs = [1 51];

% Time to live (TTL) value for each Source-Destination pair
ttl = 20;

% Pair of Friend & Low Power nodes - assuming that Friendship is
% established between the configured Friend and Low Power node pairs
friendLowPowerPairs = [2 51];

% Relay nodes
relayNodeIDs = [3 4 5 8 10 11 15 19 20 21 23 25 28 30 32 34 36 37 38 39 41 ...
    42 43 44 45 46 47 48 49];

% Time taken to transmit a bit is 0.001 milliseconds with 1 Mbps bit rate
% (PHY mode: LE1M)
txTime = 0.001;

% Initialize mesh nodes
meshNodes(1, totalNodes) = helperBLEMeshNode();

% Run the loop for various receive window values
while receiveWindowIdx <= rwsize
    % Receive window (in milliseconds) for each friend-Low Power pairs (>=
    % 120 milliseconds and <=255 milliseconds)
    receiveWindow = receiveWindowValues(receiveWindowIdx, 1);
    pollTimeoutIdx = 1;
    
    % Run the loop for given poll timeout values
    while pollTimeoutIdx <= ptsize
        % Poll timeout (in seconds) for each friend-Low Power pairs (>= 2sec
        % and <= 95.9hrs)
        pollTimeout = pollTimeoutValues(pollTimeoutIdx,1);
        
        % Create mesh nodes
        for rowIdx = 1:totalNodes
            % Create an object for a mesh node
            meshNode = helperBLEMeshNode();
            % Unique identifier for a mesh node
            meshNode.Identifier = rowIdx;
            
            meshNodes(rowIdx) = meshNode;
        end
        
        % Visualization object for Bluetooth mesh network
        meshNetworkGraph = helperBLEMeshVisualizeNetwork();
        meshNetworkGraph.NumberOfNodes = totalNodes;
        meshNetworkGraph.NodePositionType = 'UserInput';
        meshNetworkGraph.Positions = bleMeshNodesPositions;
        meshNetworkGraph.VicinityRange = 25;
        meshNetworkGraph.Title = 'Energy Profiling: Bluetooth Mesh Network';
        
        % Simulate the Bluetooth mesh network with the above configuration
        [meshNodes, paths] = helperBLEMeshSimulation(meshNodes, meshNetworkGraph, simulationTime, ...
            sourceDestinationPairs, ttl, friendLowPowerPairs, receiveWindow, ...
            pollTimeout, relayNodeIDs, enableVisualization, enableAnimation);
        
        % Low Power node statistics
        lpnNode = friendLowPowerPairs(2);
        nTx(pollTimeoutIdx,receiveWindowIdx) = meshNodes(lpnNode).LLGAPBearer.TransmittedMsgs;
        nTxBytes(pollTimeoutIdx,receiveWindowIdx) = meshNodes(lpnNode).LLGAPBearer.BytesTransmitted;
        nTxBits(pollTimeoutIdx,receiveWindowIdx) = nTxBytes(pollTimeoutIdx,receiveWindowIdx)*8;
        nRx(pollTimeoutIdx,receiveWindowIdx) = meshNodes(lpnNode).LLGAPBearer.ReceivedMsgs;
        nRxBytes(pollTimeoutIdx,receiveWindowIdx) = meshNodes(lpnNode).LLGAPBearer.BytesReceived;
        listenTime(pollTimeoutIdx,receiveWindowIdx) = meshNodes(lpnNode).LLGAPBearer.ListenTime;
        sleepTime(pollTimeoutIdx,receiveWindowIdx) = meshNodes(lpnNode).LLGAPBearer.SleepTime;
        idleTime(pollTimeoutIdx,receiveWindowIdx) = meshNodes(lpnNode).LLGAPBearer.IdleTime;
        
        currentConsumption(pollTimeoutIdx,receiveWindowIdx) = (nTxBits(pollTimeoutIdx,receiveWindowIdx)*txTime*iTxAvg) + ...
            (listenTime(pollTimeoutIdx,receiveWindowIdx)*iListen) + (sleepTime(pollTimeoutIdx,receiveWindowIdx)*iSleep) + ...
            (idleTime(pollTimeoutIdx,receiveWindowIdx)*iIdle);
        
        % Average current consumption of Low Power node
        avgCurrentConsumption(pollTimeoutIdx,receiveWindowIdx) = currentConsumption(pollTimeoutIdx,receiveWindowIdx)/ ...
            (simulationTime*1000);
        
        % Lifetime of Low Power node in hours
        lifetime(pollTimeoutIdx,receiveWindowIdx) = batteryCapacity/(avgCurrentConsumption(pollTimeoutIdx,receiveWindowIdx) + ...
            iSelfDischarge);
        
        % Lifetime of Low Power node in days
        lifetimeInDays(pollTimeoutIdx,receiveWindowIdx) = lifetime(pollTimeoutIdx,receiveWindowIdx)/24;
        
        pollTimeoutIdx = pollTimeoutIdx+1;
    end
    receiveWindowIdx = receiveWindowIdx+1;
end

% Generate poll timeout vs lifetime (in days) plot
lifetimePlot = figure;
lifetimeAxes = axes(lifetimePlot);
lifetimePlot.Name = 'Low power node: Lifetime vs Polltimeout';
semilogx(lifetimeAxes, pollTimeoutValues, lifetimeInDays)
lifetimePlot.CurrentAxes.Box = 'off';
lifetimePlot.CurrentAxes.TickDir = 'out';
xlabel(lifetimeAxes, 'PollTimeout (s)')
ylabel(lifetimeAxes, 'Lifetime (days)');
title(lifetimeAxes, 'Low power node: Lifetime vs Polltimeout');
xlim(lifetimeAxes, [5 100000])
legend(lifetimeAxes, 'Receive Window = 120 milliseconds', 'Receive Window = 200 milliseconds', ...
    'Receive Window = 255 milliseconds', 'Location', 'northwest')

% Variable names used in the table
varNames = {'Poll timeout (sec)'; ...
    'Receive window'; 'Transmitted messages'; 'Bytes transmitted'; ...
    'Received messages'; 'Bytes received'; 'Transmission time (milliseconds)'; ...
    'Listen time (milliseconds)'; 'Sleep time (milliseconds)'; 'Idle time (milliseconds)'; ...
    'Average current consumption (mA)'; 'Lifetime (days)'};

% Data type of the each variable
varTypes = {'double'; 'double'; ...
    'double'; 'double'; 'double'; 'double'; 'double'; 'double'; 'double'; ...
    'double'; 'double'; 'double'};

% Initialize the number of rows and columns
ncols = numel(varNames);
nrows = numel(receiveWindowValues)*numel(pollTimeoutValues);

% Create table for Low Power node statistics for various poll timeout and
% receive window values
lpnStats = table('Size', [nrows ncols], 'VariableTypes', varTypes, ...
    'VariableNames', varNames);

pollTimeoutIdx = 1;
receiveWindowIdx = 1;
% Update the Low Power node statistics for all the different combinations
% of poll timeout and receive window values
for rowIdx = 1:nrows
    lpnStats{rowIdx, 1} = pollTimeoutValues(pollTimeoutIdx);
    lpnStats{rowIdx, 2} = receiveWindowValues(receiveWindowIdx);
    lpnStats{rowIdx, 3} = nTx(pollTimeoutIdx, receiveWindowIdx);
    lpnStats{rowIdx, 4} = nTxBytes(pollTimeoutIdx, receiveWindowIdx);
    lpnStats{rowIdx, 5} = nRx(pollTimeoutIdx, receiveWindowIdx);
    lpnStats{rowIdx, 6} = nRxBytes(pollTimeoutIdx, receiveWindowIdx);
    lpnStats{rowIdx, 7} = nTxBits(pollTimeoutIdx, receiveWindowIdx)*txTime;
    lpnStats{rowIdx, 8} = listenTime(pollTimeoutIdx, receiveWindowIdx);
    lpnStats{rowIdx, 9} = sleepTime(pollTimeoutIdx, receiveWindowIdx);
    lpnStats{rowIdx, 10} = idleTime(pollTimeoutIdx, receiveWindowIdx);
    lpnStats{rowIdx, 11} = avgCurrentConsumption(pollTimeoutIdx, receiveWindowIdx);
    lpnStats{rowIdx, 12} = lifetimeInDays(pollTimeoutIdx, receiveWindowIdx);
    pollTimeoutIdx = pollTimeoutIdx + 1;
    % Reset the pollTimeoutIdx for all the receive window values and update
    % the receiveWindowIdx
    if rem(rowIdx, numel(pollTimeoutValues)) == 0
        pollTimeoutIdx = 1;
        receiveWindowIdx = receiveWindowIdx + 1;
    end
end

% Display the table for Low Power node statistics for various poll timeout
% and receive window values
fprintf(['Statistics of Bluetooth mesh Low Power node (%d) for various ' ...
    'poll timeout and receive window values:\n'], friendLowPowerPairs(1, 2));
lpnStats
