classdef bluetoothLEBIGConfig < comm.internal.ConfigBase
%bluetoothLEBIGConfig Bluetooth LE BIG configuration parameters
%   CFGBIG = bluetoothLEBIGConfig creates a default Bluetooth(R) low energy
%   (LE) broadcast isochronous group (BIG) configuration object, CFGBIG.
%
%   CFGBIG = bluetoothLEBIGConfig(Name=Value) creates a Bluetooth LE BIG
%   configuration object, CFGBIG, 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).
%
%   bluetoothLEBIGConfig properties:
%
%   SeedAccessAddress     - Seed access address of BIG
%   PHYMode               - Physical layer (PHY) mode
%   NumBIS                - Number of broadcast isochronous streams (BIS) 
%                           in the BIG
%   ISOInterval           - Isochronous event interval
%   BISSpacing            - Time interval between successive BIS events
%   SubInterval           - Time interval between successive subevents
%   MaxPDU                - Maximum payload length
%   BurstNumber           - Burst number
%   PretransmissionOffset - Pre-transmission offset
%   RepetitionCount       - Immediate repetition count
%   NumSubevents          - Number of subevents
%   BISArrangement        - Arrangement of BIS events
%   BIGOffset             - Anchor point of first BIG event
%   ReceiveBISNumbers     - BIS numbers to be received
%   UsedChannels          - List of used (good) data channels
%   InstantOffset         - Offset added to BIG event counter
%   BaseCRCInitialization - Base CRC initialization
%
%   bluetoothLEBIGConfig methods:
%
%   configureBIG - Configure BIG parameters at Bluetooth LE broadcaster and
%                  receiver
%
%   % Example 1:
%   % Create BIG configuration object
%   cfgBIG = bluetoothLEBIGConfig(BISArrangement="sequential", ...
%               RepetitionCount=2,NumSubevents=2, ...
%               PretransmissionOffset=1,BISSpacing=5e-3,ISOInterval=10e-3);
%
%   % Example 2:
%   % 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 two Bluetooth LE nodes with roles set as
%   % "isochronous-broadcaster" and "synchronized-receiver" respectively.
%   % Set the number of BIS events as 2, arrangement as "interleaved", 
%   % number of subevents as 2, and the burst number as 2.
% 
%   % Create Bluetooth LE nodes
%   isoBroadcaster = bluetoothLENode("isochronous-broadcaster");
%   receiver = bluetoothLENode("synchronized-receiver");
% 
%   % Create BIG configuration object
%   cfgBIG = bluetoothLEBIGConfig;
%   cfgBIG.NumBIS = 2;
%   cfgBIG.BISArrangement = "interleaved";
%   cfgBIG.NumSubevents = 2;
%   cfgBIG.BurstNumber = 2;
%   cfgBIG.SubInterval = 0.006;
%   cfgBIG.ISOInterval = 0.015
% 
%   % Assign the BIG configuration to the nodes
%   configureBIG(cfgBIG, isoBroadcaster, receiver);
%
%   % Add application traffic from the Central to the Peripheral nodes
%   traffic = networkTrafficOnOff(DataRate=200,PacketSize=27);
%   addTrafficSource(isoBroadcaster,traffic);
%   % Add the nodes to the simulator
%   addNodes(networkSimulator,[isoBroadcaster receiver]);
%
%   % Set the simulation time in seconds and run the simulation
%   run(networkSimulator,0.1);
%
%   % Retrieve statistics corresponding to the node
%   broadcasterStats = statistics(isoBroadcaster);
%   receiverStats = statistics(receiver);
%
%   See also bluetoothLECISConfig, bluetoothLEConnectionConfig,
%   bluetoothLENode, bluetoothMeshProfileConfig,
%   bluetoothMeshFriendshipConfig.

%   Copyright 2021-2023 The MathWorks, Inc.

properties
    %SeedAccessAddress Seed access address of BIG
    %   Specify the seed access address as an 8-element character vector or
    %   a string scalar denoting a 4-octet hexadecimal value. This property
    %   specifies the seed access address from which the access addresses
    %   of the BIS events are derived. The default value is "78E52493". For
    %   more information about seed access address, refer Bluetooth Core
    %   Specification v5.3, Volume 6, Part B, Section 2.1.2.
    SeedAccessAddress = "78E52493"

    %PHYMode Physical layer (PHY) mode
    %   Specify the PHY mode as "LE1M", "LE2M", "LE500K" or "LE125K". This
    %   property specifies the PHY mode used for the transmission or
    %   reception. The default value is "LE1M".
    PHYMode = "LE1M"

    %NumBIS Number of BISes in the BIG
    %   Specify the number of BISes as an integer in the range [1, 31]. The
    %   default value is 1. For more information about NumBIS, refer
    %   Bluetooth Core Specification v5.3, Volume 6, Part B, Section
    %   4.4.6.3.
    %
    %   Note that updating this value might require you to modify the
    %   ISOInterval value to accommodate all the subevents.
    NumBIS (1,1) {mustBeInteger, mustBePositive, mustBeLessThanOrEqual(NumBIS, 31)} = 1

    %ISOInterval Isochronous event interval
    %   Specify the isochronous event interval in the range of [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
    %   BIG anchor points. The default value is 0.005 seconds. For more
    %   information about ISO interval, refer Bluetooth Core Specification
    %   v5.3, Volume 6, Part B, Section 4.4.6.3.
    %
    %   Note that updating the NumBIS, NumSubevents, MaxPDU, or SubInterval
    %   values might require you to modify this value to accommodate all the
    %   subevents.
    ISOInterval = 0.005

    %BISSpacing Time interval between successive BIS events
    %   Specify the BIS spacing as a positive real number in the range of
    %   [0.000198, 1.048575]. Units are in seconds. This property specifies
    %   the time between the start of corresponding subevents in adjacent
    %   BIS events in the BIG and the start of the first subevent of the
    %   last BIS and the control subevent (if present). The default value
    %   is 0.002238 seconds. For more information about BIS spacing, refer
    %   Bluetooth Core Specification v5.3, Volume 6, Part B, Section
    %   4.4.6.3.
    %
    %   Note that updating the NumBIS, NumSubevents, MaxPDU, SubInterval, or
    %   BISArrangement values might require you to modify this value to
    %   accommodate all the subevents.
    BISSpacing (1,1) {mustBeInRange(BISSpacing,198e-6,1.048575,'inclusive')} = 0.002238

    %SubInterval Time interval between successive subevents
    %   Specify the subevent interval as a positive real number in the
    %   range of [0.000198, 1.048575]. Units are in seconds. This property
    %   specifies the time between the start of two consecutive subevents
    %   of each BIS. The default value is 0.002238 seconds. For more
    %   information about subevent interval, refer Bluetooth Core
    %   Specification v5.3, Volume 6, Part B, Section 4.4.6.3.
    %
    %    Note that updating the NumBIS, NumSubevents, MaxPDU, or BISArrangement
    %    values might require you to modify this value to accommodate all the
    %    subevents.
    SubInterval (1,1) {mustBeInRange(SubInterval,198e-6,1.048575,'inclusive')} = 0.002238

    %MaxPDU Maximum payload length
    %   Specify the maximum payload length in decimal octets in the range
    %   [1, 251]. This property specifies the maximum number of data octets
    %   (excluding the message integrity check, if any) that can be carried
    %   in each BIS data PDU in the BIG. The default value is 251 octets.
    %   For more information about Max PDU, refer Bluetooth Core
    %   Specification v5.3, Volume 6, Part B, Section 4.4.6.3.
    %
    %   Note that updating this value might require you to modify the
    %   ISOInterval, SubInterval, or BISSpacing values to accommodate all the
    %   subevents.
    MaxPDU (1,1) {mustBeInteger, mustBeInRange(MaxPDU,1,251,'inclusive')} = 251

    %BurstNumber Burst number
    %   Specify the burst number as an integer in the range [1, 7]. This
    %   property specifies the number of payloads associated with a BIS
    %   event. The default value is 1. For more information about burst
    %   number, refer Bluetooth Core Specification v5.3, Volume 6, Part B,
    %   Section 4.4.6.3.
    %
    %   Note that updating this value might require you to modify the
    %   ISOInterval, SubInterval, or BISSpacing values to accommodate all the
    %   subevents. Further, you might also need to modify the RepetitionCount
    %   or NumSubevents values to consider all the unique transmission,
    %   repetitions, and pretransmissions.
    BurstNumber (1,1) {mustBeInteger, mustBeInRange(BurstNumber,1,7,'inclusive')} = 1

    %PretransmissionOffset Pre-transmission offset
    %   Specify the pre-transmission offset (PTO) as an integer in the
    %   range [0, 15]. The subevents carrying pre-transmissions contain
    %   data associated with the future BIS events specified by this value.
    %   The default value is 0. For more information about PTO, refer
    %   Bluetooth Core Specification v5.3, Volume 6, Part B, Section
    %   4.4.6.3.
    PretransmissionOffset (1,1) {mustBeInteger, mustBeInRange(PretransmissionOffset,0,15,'inclusive')} = 0

    %RepetitionCount Immediate repetition count
    %   Specify the immediate repetition count (IRC) as an integer in the
    %   range [1, 15]. This property specifies the number of times a
    %   payload is transmitted in a BIS event. The default value is 1. For
    %   more information about repetition count, refer Bluetooth Core
    %   Specification v5.3, Volume 6, Part B, Section 4.4.6.3.
    %
    %   Note that updating this value might require you to modify the
    %   ISOInterval, SubInterval, or BISSpacing values to accommodate all the
    %   subevents. Further, you might also need to modify the BurstNumber or
    %   NumSubevents values to consider all the unique transmission,
    %   repetitions, and pretransmissions.
    RepetitionCount (1,1) {mustBeInteger, mustBeInRange(RepetitionCount,1,15,'inclusive')} = 1

    %NumSubevents Number of subevents
    %   Specify the number of subevents as an integer in the range of [1,
    %   31]. This property specifies the number of subevents in each BIS
    %   event in the BIG. The default value is 1. For more information
    %   about number of subevents, refer Bluetooth Core Specification v5.3,
    %   Volume 6, Part B, Section 4.4.6.3.
    %
    %   Note that updating this value might require you to modify the
    %   ISOInterval, SubInterval, or BISSpacing values to accommodate all the
    %   subevents. Further, you might also need to modify the BurstNumber or
    %   RepetitionCount values to consider all the unique transmission,
    %   repetitions, and pretransmissions.
    NumSubevents (1,1) {mustBeInteger, mustBeInRange(NumSubevents,1,31,'inclusive')} = 1

    %BISArrangement Arrangement of BIS events
    %   Specify the arrangement of BIS events as "sequential" or
    %   "interleaved". This property specifies the arrangement of BIS
    %   events in a BIG. In sequential arrangement, all the subevents of a
    %   BIS event occur together. In interleaved arrangement, corresponding
    %   subevents of each BIS event are adjacent to each other. The default
    %   value is "sequential". For more information about BIS arrangement,
    %   refer Bluetooth Core Specification v5.3, Volume 6, Part B, Section
    %   4.4.6.4.
    %
    %    Note that updating this value might require you to modify the
    %    BISSpacing or SubInterval values to accommodate all the subevents.
    BISArrangement = "sequential"

    %BIGOffset Offset of anchor point of first BIG event
    %   Specify the offset for the anchor point of the first BIG event.
    %   Units are in seconds. This property specifies the time after which
    %   the first BIG event starts. The default value is 0. For more
    %   information about BIG offset, refer Bluetooth Core Specification
    %   v5.3, Volume 6, Part B, Section 4.4.6.11.
    BIGOffset (1,1) {mustBeNonnegative, mustBeNonNan, mustBeFinite} = 0

    %ReceiveBISNumbers BIS numbers to be received
    %   Specify the BIS numbers to be received by the receiver as a vector
    %   of integers in the range [1, 31]. This property specifies the
    %   indices of the BIS streams that the receiver has to render. The
    %   default value is 1.
    ReceiveBISNumbers (1,:) {mustBeInteger, mustBeInRange(ReceiveBISNumbers,1,31,'inclusive')} = 1

    %UsedChannels List of used (good) data channels
    %   Specify the assigned data channel indices as an integer vector with
    %   element values in the range [0, 36]. To ensure that at least two
    %   channels are set as used (good) channels, specify the vector length
    %   greater than 1. This property specifies the set of good channels
    %   that the broadcaster classifies. The default value is a row vector
    %   containing all the channel indices [0:36].
    UsedChannels = 0:36

    %InstantOffset Offset added to BIG event counter
    %   Specify instant offset as an integer in the range [6, 65535]. This
    %   property indicates the offset added to the BIG event count. The
    %   control procedure takes effect after instant offset BIG events from
    %   the current BIG event. The default value is 6. For more information
    %   about instant offset, refer Bluetooth Core Specification v5.3,
    %   Volume 6, Part B, Section 2.6.3.
    InstantOffset (1,1) {mustBeInteger, mustBeInRange(InstantOffset,6,65535,'inclusive')} = 6

    %BaseCRCInitialization Base CRC initialization
    %   Specify the base cyclic redundancy check (CRC) initialization as a
    %   4-element character vector or a string scalar denoting a 2-octet
    %   hexadecimal value. This property specifies the most significant 2
    %   octets of the CRC initialization vector. For more information about
    %   base CRC initialization, refer Bluetooth Core Specification v5.3,
    %   Volume 6, Part B, Section 3.1.1. The default value is "1234".
    BaseCRCInitialization = "1234"
end

properties (SetAccess = private, Hidden)
    %BroadcasterName Name of the broadcaster associated with the BIG
    BroadcasterName = blanks(0)

    %BroadcasterID ID of the broadcaster associated with the BIG
    BroadcasterID = 0
end

properties(Constant, Hidden)
    PHYMode_Values = ble.internal.networkUtilities.PHYMode_Values

    BISArrangement_Values = ["sequential", "interleaved"]

    %TMSS Minimum subevent space in seconds
    TMSS = 0.00015
end

methods
    function obj = bluetoothLEBIGConfig(varargin)
        % For codegen, initialize variable sized properties in constructor
        obj@comm.internal.ConfigBase("UsedChannels",0:36, ...
                                    "PHYMode","LE1M",varargin{:});
    end

    % Set ISO interval
    function obj = set.ISOInterval(obj, value)
        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 seed access address
    function obj = set.SeedAccessAddress(obj, value)
        ble.internal.validateHex(value, 8, "SeedAccessAddress");
        obj.SeedAccessAddress = string(value);
    end

    % Set used channels list
    function obj = set.UsedChannels(obj, value)
        validateattributes(value, {'numeric'}, {'vector', 'nonnegative', ...
            'integer', '<=', 36}, mfilename, "UsedChannels");
        value = unique(value);
        % At least two channels must be set as used (good) channels as
        % specified in Bluetooth Core Specification v5.3 | Vol 6, Part B,
        % Section 4.5.8.1
        coder.internal.errorIf((numel(value) < 2), ...
            "bluetooth:bleLL:InvalidUsedChannels");
        obj.UsedChannels = value;
    end

    % Set base CRC initialization
    function obj = set.BaseCRCInitialization(obj, value)
        ble.internal.validateHex(value, 4, "BaseCRCInitialization");
        obj.BaseCRCInitialization = string(value);
    end

    % Set PHY mode
    function obj = set.PHYMode(obj, value)
        value = validatestring(value, obj.PHYMode_Values, mfilename, "PHYMode");
        obj.PHYMode = string(value);
    end

    % Set arrangement of BIS events
    function obj = set.BISArrangement(obj, value)
        obj.BISArrangement = validatestring(value, obj.BISArrangement_Values,...
            mfilename, "BISArrangement");
    end
end

methods
    function obj = configureBIG(obj, isoBroadcasterNode, receiverNode)
        %configureBIG Configure BIG parameters at Bluetooth LE broadcaster
        %and receiver
        %
        %   OBJ = configureBIG(OBJ, ISOBROADCASTERNODE, RECEIVERNODE)
        %   configures the BIG parameters at the Bluetooth LE isochronous
        %   broadcaster, ISOBROADCASTERNODE, and receiver, RECEIVERNODE.
        %
        %   OBJ is an object of type bluetoothLEBIGConfig.
        %
        %   ISOBROADCASTERNODE is an object of type bluetoothLENode with
        %   the Role property set as "isochronous-broadcaster".
        %
        %   RECEIVERNODE is an object of type bluetoothLENode with the Role
        %   property set as "synchronized-receiver".

        % Validate the inputs
        validateattributes(isoBroadcasterNode, {'bluetoothLENode'}, {'scalar'}, ...
            mfilename, "isoBroadcasterNode", 2);
        validateattributes(receiverNode, {'bluetoothLENode'}, {'scalar'}, ...
            mfilename, "receiverNode", 3);
        validatestring(isoBroadcasterNode.Role, {'isochronous-broadcaster'}, ...
            mfilename, "isoBroadcasterNode.Role");
        validatestring(receiverNode.Role, {'synchronized-receiver'}, ...
            mfilename, "receiverNode.Role");
        
        % Validate BIG configuration
        obj = validateConfig(obj);

        % Update broadcaster information
        obj.BroadcasterName = isoBroadcasterNode.Name;
        obj.BroadcasterID = isoBroadcasterNode.ID;

        % Add BIG configuration at the nodes
        addBIG(isoBroadcasterNode, obj);
        addBIG(receiverNode, obj);
    end
end

methods (Access = protected)
    function flag = isInactiveProperty(~, ~)
        flag = false;
    end
end

methods (Hidden)
    function obj = validateConfig(obj)
    %validateConfig Validate the configured BIG parameters

        % Compute maximum data packet time for the BIG
        mpt = computeMPT(obj, obj.MaxPDU);
        % Compute maximum packet time for the BIG
        controlSubeventTime = computeMPT(obj, 251);

        % Compute group count
        groupCount = obj.NumSubevents/obj.BurstNumber;

        obj.BISSpacing = round(obj.BISSpacing, 9);
        obj.SubInterval = round(obj.SubInterval, 9);
        obj.ISOInterval = round(obj.ISOInterval, 9);

        % Validate immediate repetition count
        coder.internal.errorIf(obj.RepetitionCount > groupCount, ...
            "bluetooth:bluetoothLENode:InvalidIRC");

        % Validate pre-transmission offset
        coder.internal.errorIf(obj.RepetitionCount < groupCount && ...
            ~logical(obj.PretransmissionOffset), "bluetooth:bluetoothLENode:InvalidPTO");

        % Validate number of subevents
        coder.internal.errorIf(mod(obj.NumSubevents, obj.BurstNumber), ...
            "bluetooth:bluetoothLENode:InvalidNSE");

        % Validate sub-interval
        coder.internal.errorIf(obj.SubInterval < round(mpt + obj.TMSS, 9), ...
            "bluetooth:bluetoothLENode:InvalidSubInterval");

        % Validate BIS spacing
        coder.internal.errorIf(obj.BISSpacing < round(mpt + obj.TMSS, 9), ...
            "bluetooth:bluetoothLENode:InvalidBISSpacing");

        % Validate arrangement of BIS events
        switch obj.BISArrangement
            case "sequential"
                status = (obj.BISSpacing*1e6 >= obj.SubInterval*1e6*obj.NumSubevents);
                coder.internal.errorIf(~status, "bluetooth:bluetoothLENode:InvalidBISArrangement", ...
                    obj.BISArrangement, "BISSpacing", "NSE", "SubInterval");

            case "interleaved"
                status = (obj.SubInterval*1e6 >= obj.BISSpacing*1e6*obj.NumBIS);
                coder.internal.errorIf(~status, "bluetooth:bluetoothLENode:InvalidBISArrangement", ...
                    obj.BISArrangement, "SubInterval", "NumBIS", "BISSpacing");
        end

        % Validate ISO interval
        if strcmpi(obj.BISArrangement, "sequential")
            status = (round(obj.ISOInterval*1e6,9) >= round((obj.NumBIS*obj.BISSpacing*1e6 + ...
                controlSubeventTime*1e6 + obj.TMSS*1e6),9));
        else
            status = (round(obj.ISOInterval*1e6,9) >= round((obj.NumSubevents*obj.SubInterval*1e6 + ...
                controlSubeventTime*1e6 + obj.TMSS*1e6),9));
        end
        coder.internal.errorIf(~status,"bluetooth:bluetoothLENode:InsufficientISOInterval");


        % Validate receive BIS numbers
        coder.internal.errorIf((any(obj.ReceiveBISNumbers > obj.NumBIS)), ...
            "bluetooth:bluetoothLENode:InvalidReceiveBISNumbers");
    end
end

methods (Access = private)
    function packetTime = computeMPT(obj, payloadLength)
        %computeMPT Compute the maximum packet waveform duration

        if strcmpi(obj.PHYMode, "LE500K")
            % For LE500K, MPT is computed by considering LE125K as PHY
            packetTime = bluetoothPacketDuration("LE125K","Disabled",payloadLength);

        else
            % PHY modes other than LE500K
            packetTime = bluetoothPacketDuration(obj.PHYMode,"Disabled",payloadLength);
        end
        packetTime = round(packetTime/1e6, 9);
    end
end
end

% LocalWords:  isochronous IRC TMSS NSE
