classdef bluetoothLECISConfig < comm.internal.ConfigBase
%bluetoothLECISConfig Bluetooth LE CIS configuration parameters
%   CFGCIS = bluetoothLECISConfig creates a default Bluetooth(R) low energy
%   (LE) connected isochronous stream (CIS) configuration object, CFGCIS.
%
%   CFGCIS = bluetoothLECISConfig(Name=Value) creates a Bluetooth LE CIS
%   configuration object, CFGCIS, with the specified property Name set to
%   the specified Value. You can specify additional name-value arguments in
%   any order as (Name1=Value1, ..., NameN=ValueN).
%
%   bluetoothLECISConfig properties:
%
%   ISOInterval    - Time between successive anchor points of a CIS in seconds
%   NumSubevents   - Number of subevents in the CIS event
%   SubInterval    - Time between the start of two consecutive subevents in the CIS in seconds
%   BurstNumber    - Number of unique payloads in a CIS event
%   MaxPDU         - Maximum number of data octets that a CIS PDU can carry
%   FlushTimeout   - Maximum number of CIS events that can be used to transmit and retransmit a given payload
%   CISArrangement - Arrangement of CIS events
%
%   bluetoothLECISConfig methods:
%
%   subeventLength - Compute subevent length of the CIS
%
%   % Example 1:
%   % Create CIS configuration object
%   CFGCIS = bluetoothLECISConfig(ISOInterval=20e-3,NumSubevents=2,CISArrangement="sequential");
%
%   % Example 2:
%   % Create, configure, and simulate a Bluetooth LE audio network with a Central node and
%   % two Peripheral nodes communicating using CIS events.
%
%   % Check if the Communications Toolbox(TM) Wireless Network Simulation
%   % Library support package is installed. If the support package is not
%   % installed, MATLAB(R) returns an error with a link to download and install
%   % the support package.
%   wirelessnetworkSupportPackageCheck
%
%   % Initialize the wireless network simulator
%   networkSimulator = wirelessNetworkSimulator.init;
%
%   % Create the Bluetooth LE piconet with a "central" and two
%   % "peripheral" nodes for modeling CIS events along with connection
%   % events
%
%   % Create a Bluetooth LE node with "central" role.
%   centralNode = bluetoothLENode("central");
%   % Create Bluetooth LE nodes with "peripheral" role.
%   peripheralNode1 = bluetoothLENode("peripheral");
%   peripheralNode2 = bluetoothLENode("peripheral");
%
%   % Create a configuration object to form CIS connection between Central and
%   % Peripheral node 1.
%   cfgCIS1 = bluetoothLECISConfig(ISOInterval=30e-3,NumSubevents=2);
%   % Create a configuration object to form connection between Central and
%   % Peripheral node 1.
%   cfgConnection1 = bluetoothLEConnectionConfig(ConnectionInterval=60e-3,...
%         ActivePeriod=10e-3,AccessAddress="5DA44270");
%   % Configure the CIS connection between "central" and "peripheral" node 1.
%   [connectionConfig1,cisConfig1] = configureConnection(cfgConnection1,...
%        centralNode,peripheralNode1,CISConfig=cfgCIS1)
%
%   % Create a configuration object to form CIS connection between Central and
%   % Peripheral node 2.
%   cfgCIS2 = bluetoothLECISConfig(ISOInterval=30e-3,NumSubevents=1);
%   % Create a configuration object to form connection between Central and
%   % Peripheral node 2.
%   cfgConnection2 = bluetoothLEConnectionConfig(ConnectionInterval=60e-3,...
%         ConnectionOffset=30e-3,ActivePeriod=10e-3,AccessAddress="5DA44271");
%
%   % Configure the connection between "central" and "peripheral" node 2.
%   [connectionConfig2,cisConfig2] = configureConnection(cfgConnection2,...
%       centralNode,peripheralNode2,CISConfig=cfgCIS2)
%
%   % Add application traffic from the Central to the Peripheral nodes for the
%   % CIS connections
%   traffic1 = networkTrafficOnOff(DataRate=200,PacketSize=100,GeneratePacket=true);
%   addTrafficSource(centralNode,traffic1,DestinationNode=peripheralNode1,CISConfig=cisConfig1);
%   traffic2 = networkTrafficOnOff(DataRate=100,PacketSize=100,GeneratePacket=true);
%   addTrafficSource(centralNode,traffic2,DestinationNode=peripheralNode2,CISConfig=cisConfig2);
%
%   % Add the nodes to the simulator
%   addNodes(networkSimulator,[centralNode peripheralNode1 peripheralNode2]);
%
%   % Set the simulation time in seconds and run the simulation
%   run(networkSimulator,0.2);
%
%   % Retrieve statistics corresponding to the Central node
%   centralStats = statistics(centralNode);
%   peripheral1Stats = statistics(peripheralNode1);
%   peripheral2Stats = statistics(peripheralNode2);
%
%   See also bluetoothLEBIGConfig, bluetoothLEConnectionConfig,
%   bluetoothLENode, bluetoothMeshProfileConfig,
%   bluetoothMeshFriendshipConfig.

%   Copyright 2023 The MathWorks, Inc.

properties
    %ISOInterval Time between successive anchor points of a CIS in seconds
    %   Specify the isochronous event interval in the range [0.005,4]. Units
    %   are in seconds. Specify the interval as a multiple of 1.25 milliseconds.
    %   This property specifies the time between two adjacent CIS anchor points.
    %
    %   As the connection events and CIS events in the LE network operate
    %   simultaneously at regular intervals, specify this property as
    %   "(ConnectionInterval/NumPeripherals)". The "<a href="matlab:help('bluetoothLEConnectionConfig.ConnectionInterval')">ConnectionInterval</a>" value
    %   specifies the interval between the start of two consecutive connection
    %   events. The "NumPeripherals" value specifies the total number of
    %   Peripheral nodes simulated. The default value is 0.020 seconds. For
    %   more information about ISOInterval, see Bluetooth Core Specification
    %   v5.3, Volume 6, Part B, Section 4.5.13.1.
    ISOInterval = 0.020

    %NumSubevents Number of subevents in the CIS event
    %   Specify the number of subevents as an integer in the range [1,31].
    %   This property specifies the number of subevents in each CIS event. The
    %   default value is 1. For more information about number of subevents,
    %   see Bluetooth Core Specification v5.3, Volume 6, Part B, Section
    %   4.5.13.2.
    NumSubevents (1,1) {mustBeInteger,mustBeInRange(NumSubevents,1,31,"inclusive")} = 1

    %SubInterval Time between the start of two consecutive subevents in the CIS in seconds
    %   Specify the subevent interval in the range [0.0004,4]. Units are in seconds.
    %   This property specifies the time between the start of two consecutive subevents
    %   of a CIS. If you set the "NumSubevents" property to 1, then this value is 0.
    %   Otherwise, the value is in the range [SubeventLength,ISOInterval], where 
    %   the "SubeventLength" is calculated using the <a href="matlab:help('bluetoothLECISConfig.subeventLength')">subeventLength</a> object function
    %   and the "<a href="matlab:help('bluetoothLECISConfig.ISOInterval')">ISOInterval</a>" specifies the time between the successive anchor points of a CIS.
    %
    %   If you set the "CISArrangement" property to "sequential", this value
    %   must be greater than or equal to the "SubeventLength" value of the CIS.
    %   The default value in this case is the "SubeventLength" value of the
    %   CIS. If you set the "CISArrangement" property to "interleaved", this
    %   value must be greater than or equal to the "SubeventLength" values of
    %   all the CIS events in the connected isochronous group (CIG). The
    %   default value in this case is the sum of all "SubeventLength" values of
    %   the CIS events in the CIG. For more information about subinterval, see
    %   Bluetooth Core Specification v5.3, Volume 6, Part B, Section 4.5.14.2.
    SubInterval

    %BurstNumber Number of unique payloads in a CIS event
    %   Specify the burst number as an integer in the range [0,15]. This
    %   property specifies the number of unique payloads associated with a CIS
    %   event. You can specify this property as a 1-by-2 vector or a scalar. If
    %   you set this value as a vector, the first element specifies the burst
    %   number from the Central node to the Peripheral node, and the second
    %   element specifies the burst number from the Peripheral node to the
    %   Central node. If you set this value as a scalar, the object uses the
    %   same burst number value for both directions. The default value is 1.
    %   For more information about burst number, see Bluetooth Core
    %   Specification v5.3, Volume 6, Part B, Section 4.5.13.3.
    BurstNumber = 1

    %MaxPDU Maximum number of data octets that a CIS PDU can carry
    %   Specify the maximum payload length in decimal octets in the range
    %   [0,251]. This property specifies the maximum number of data octets
    %   (excluding the message integrity check, if any) that can be carried in
    %   each CIS data PDU in a CIS. You can specify this property as a 1-by-2
    %   vector or a scalar. If you set this value as a vector, the first
    %   element specifies the maximum PDU size from the Central node to the
    %   Peripheral node, and the second element specifies the maximum PDU size
    %   from the Peripheral node to the Central node. If you set this value as
    %   a scalar, the object uses the same max PDU value for both directions.
    %   The default value is 251 octets. For more information about Max PDU,
    %   see Bluetooth Core Specification v5.3, Volume 6, Part B, Section
    %   4.5.13.3.
    MaxPDU = 251

    %FlushTimeout Maximum number of CIS events that can be used to transmit and
    %retransmit a payload
    %   Specify the flush timeout as an integer in the range [1,255]. This
    %   property specifies the maximum number of CIS events that can be used to
    %   transmit and retransmit a specific payload. This property enables you
    %   to determine the appropriate CIS event and subevent numbers at which
    %   the Link Layer (LL) discards the payload associated with the CIS Data
    %   PDU to prevent continuous retransmissions. You can specify this
    %   property as a 1-by-2 vector or a scalar. If you set this value as a
    %   vector, the first element specifies the flush timeout from the Central
    %   node to the Peripheral node, and the second element specifies the flush
    %   timeout from the Peripheral node to the Central node. If you set this
    %   value as a scalar, the object uses the same flush timeout value for
    %   both directions. The default value is 1. For more information about
    %   flush timeout, see Bluetooth Core Specification v5.3, Volume 6, Part B,
    %   Section 4.5.13.3.
    FlushTimeout = 1

    %CISArrangement Arrangement of CIS events
    %   Specify the arrangement of CIS events as "sequential" or "interleaved".
    %   This property specifies the arrangement of CIS events in a CIS. If you
    %   set the property to "sequential", all the subevents of a CIS event
    %   occur simultaneously. If you set the property to "interleaved",
    %   subevents corresponding to each CIS event are adjacent to each other.
    %   The default value is "sequential". For more information about CIS
    %   arrangement, see Bluetooth Core Specification v5.3, Volume 6, Part B,
    %   Section 4.5.14.2.
    CISArrangement = "sequential"
end

properties (SetAccess=private)
    %AccessAddress Unique connection address
    %   This property specifies a unique 32-bit address for the link layer CIS
    %   connection between Central and Peripheral nodes.
    AccessAddress

    %CISID Identifier of the CIS connection
    %   This property specifies a unique identifier for each CIS connection
    %   between Central and Peripheral nodes.
    CISID

    %CISOffset Offset of anchor point of first CIS event in seconds
    %   This value specifies the time after which the CIS event starts in the
    %   connection.
    CISOffset
end

properties (SetAccess=private,Hidden)
    %CentralName Name of the Central node associated with this connection
    CentralName = blanks(0)

    %PeripheralName Name of the Peripheral node associated with this
    %connection
    PeripheralName = blanks(0)

    %CentralID ID of the Central node associated with this connection
    CentralID

    %PeripheralID ID of the Peripheral node associated with this connection
    PeripheralID

    %SubeventLength Length of subevent in the CIS
    SubeventLength
end

properties(Constant,Hidden)
    CISArrangement_Values = ["sequential","interleaved"]
end

methods
    function obj = bluetoothLECISConfig(varargin)
        obj@comm.internal.ConfigBase(varargin{:});
    end

    % Set ISO interval
    function obj = set.ISOInterval(obj,value)
        validateattributes(value,{'numeric'},{'scalar'},mfilename,"ISOInterval");
        value = round(value*1e6)/1e6;
        validateattributes(value,{'numeric'},{'scalar','>=',5e-3,'<=',4},mfilename,"ISOInterval");
        coder.internal.errorIf((mod(value,1.25e-3)~=0),"bluetooth:bluetoothLENode:InvalidISOInterval",num2str(value));
        obj.ISOInterval = value;
    end

    % Set subinterval
    function obj = set.SubInterval(obj,value)
        validateattributes(value,{'numeric'},{'scalar'},mfilename,"SubInterval");
        value = round(value*1e6)/1e6;
        validateattributes(value,{'numeric'},{'scalar','>=',4e-4,'<=',4},mfilename,"SubInterval");
        obj.SubInterval = value;
    end

    % Set arrangement of CIS events
    function obj = set.CISArrangement(obj,value)
        obj.CISArrangement = validatestring(value,obj.CISArrangement_Values,mfilename,"CISArrangement");
    end

    % Set burst number
    function obj = set.BurstNumber(obj,value)
        if isscalar(value)
            validateattributes(value,{'numeric'},{'size',[1 1],'nonnegative','integer','>=',0,'<=',15,},mfilename,"BurstNumber");
        else
            validateattributes(value,{'numeric'},{'size',[1 2],'nonnegative','integer','>=',0,'<=',15},mfilename,"BurstNumber");
        end
        obj.BurstNumber = value;
    end

    % Set maximum PDU size
    function obj = set.MaxPDU(obj,value)
        if isscalar(value)
            validateattributes(value,{'numeric'},{'size',[1 1],'nonnegative','integer','>=',0,'<=',251},mfilename,"MaxPDU");
        else
            validateattributes(value,{'numeric'},{'size',[1 2],'nonnegative','integer','>=',0,'<=',251},mfilename,"MaxPDU");
        end
        obj.MaxPDU = value;
    end

    % Set flush timeout
    function obj = set.FlushTimeout(obj,value)
        if isscalar(value)
            validateattributes(value,{'numeric'},{'size',[1 1],'nonnegative','integer','>=',1,'<=',255},mfilename,"FlushTimeout");
        else
            validateattributes(value,{'numeric'},{'size',[1 2],'nonnegative','integer','>=',1,'<=',255},mfilename,"FlushTimeout");
        end
        obj.FlushTimeout = value;
    end

    function subEventLength = subeventLength(obj,phyMode)
        %subeventLength Compute subevent length of the CIS
        %
        %   SELENGTH = subeventLength(OBJ,PHYMODE) computes subevent length
        %   based on the given configuration parameters. SubeventLength value is is
        %   the sum of maximum payload transmission time from Central to Peripheral,
        %   maximum payload transmission time from Peripheral to Central, inter
        %   frame spacing (T_IFS), and minimum subevent spacing (T_MSS), as per the
        %   Bluetooth Core Specification v5.3, Volume 6, Part B, Section 4.5.13.2.
        %
        %   SELENGTH is the subevent length value in seconds.
        %
        %   PHYMODE is generating or decoding PHY mode specified as any one of
        %   "LE125K", "LE500K", "LE1M", and "LE2M".

        T_IFS = 150; % In microseconds
        T_MSS = 150; % In microseconds
        % On the LE Coded PHY, the S=8 coding shall be assumed. as per the
        % Bluetooth Core Specification v5.3, Volume 6, Part B, Section 4.5.13.1.
        if strcmpi(phyMode,"LE500K")
            phy = "LE125K";
        else
            phy = phyMode;
        end
        if isscalar(obj.MaxPDU)
            [payloadLengthC2P,payloadLengthP2C] = deal(obj.MaxPDU);
        else
            payloadLengthC2P = obj.MaxPDU(1);
            payloadLengthP2C = obj.MaxPDU(2);
        end
        packetTimeC2P = bluetoothPacketDuration(phy,"Disabled",payloadLengthC2P);
        packetTimeP2C = bluetoothPacketDuration(phy,"Disabled",payloadLengthP2C);
        subEventLength = packetTimeC2P+packetTimeP2C+T_IFS+T_MSS; % In microseconds
        subEventLength = round(subEventLength/1e6,6); % In seconds
    end
end

methods (Hidden)
    function obj = setDerivedParams(obj,varargin)
        %setDerivedParams Set the read-only parameters of this configuration object

        % Name-value pairs
        for idx = 1:2:nargin-1
            obj.(char(varargin{idx})) = varargin{idx+1};
        end
    end
end
end
