function dtInfo = getCommDigBBModDTInfo(obj)
% Communications Toolbox digital baseband Modulator block mask
% initialization helper function.  Returns dtInfo structure used by the
% block to determine the (signed floating- or fixed-point) output data type
% attributes to use in its implementation.
%
% NOTE: This is a Communications Toolbox mask utility function.
% It is not intended to be used as a general-purpose function.
% The function is also called by the 'View Constellation' callback
% function commblksViewConst
%
% USAGE
%
%   dtInfo = getCommDigBBModDTInfo(obj)
%
% ARGUMENTS
%
%   obj : block object, e.g. get_param(gcbh,'object')
%         It can also be a Mask Handle as passed by 'View Constellation' 
%         button in linear modulators
%
% DESCRIPTION
%
%   This function returns a structure containing information about
%   the block output data type.  The structure has the following fields:
%
%   DataTypeDeterminesFracBits : equals 1 if the data type BY ITSELF is
%                                sufficient to determine the number of
%                                fraction bits.  This field will
%                                be 1 for builtins and for data objects
%                                whose class is not equal to FIX
%
%   DataTypeIsSigned           : whether or not the data type is signed
%
%   DataTypeWordLength         : the total number of bits in the data type
%
%   DataTypeFracOrMantBits     : the number of fractional bits (fixed-
%                                point) or mantissa bits (floating-point)
%
%   DataType                   : the class object returned by the sint, uint,
%                                sfrac,ufrac,sfix, ufix, and float commands,
%                                equals [] in all other cases
%
%   MaskHdlErrStatus           : equals 1 if there is an error in parameter
%                                evaluation and the input is a Mask Handle 
%                                passed by the 'View Constellation' button
%                                in linear modulator blocks. This field
%                                does not exist if the input is a block
%                                handle
%
% ASSUMPTIONS
%
%   This function assumes the following mask parameter variables exist:
%
%   outDtype       : a string (popup choice) listing the supported builtin
%                    data types as well as 'Fixed-point' and 'User-defined'
%                    and 'Inherit via back propagation'
%
%   outWordLen     : an edit box value visible when 'Fixed-point' used
%
%   outUDDataType  : an edit box value visible when 'User-defined' used
%
%   outFracLenMode : a string (popup choice) that must be either
%                    'Best precision' or 'User-defined'
%
%   outFracLen     : an edit box value visible when 'User-defined' is the
%                    outFracLenMode (AND outDtype resolves to class FIX).

%   Copyright 1996-2018 The MathWorks, Inc.

if (nargin < 1) || isempty(obj)
    obj = get_param(gcbh,'object');
end

%Determine type of input - block handle / mask handle

dlgclass = class(obj);
usingMaskDlgHdl = 0; % block handle
dtInfo.MaskHdlErrStatus = 0; %Initializing error status


% Set reasonable default values in return structure
dtInfo.DataTypeDeterminesFracBits = 0;
dtInfo.DataTypeWordLength         = 2;
dtInfo.DataTypeFracOrMantBits     = 0;
dtInfo.DataType                   = [];

% ---------------------------------
% Determine dtSetting and className
% ---------------------------------
if usingMaskDlgHdl
    dtSetting = obj.Block.outDtype;
else
    dtSetting = obj.outDtype;
end

if ~(strcmp(dtSetting,'Fixed-point')  ||  ...
        strcmp(dtSetting,'User-defined') || ...
        strcmp(dtSetting,'Inherit via back propagation') || ...
        strncmp(dtSetting,'Same as',7))
    % Use the explicit string directly from the outDtype choice
    className = upper(dtSetting);
    
elseif strcmp(dtSetting,'Fixed-point')
    className = 'FIXEDPOINT';
    
elseif strcmp(dtSetting,'User-defined')
    if usingMaskDlgHdl
        [outUDDataType, status(1)] = localGetParamValue(obj,obj.Block.outUDDataType);
        if strcmp(obj.outFracLenMode,'User-defined')
            [outFracLen, status(2)] = localGetParamValue(obj,obj.Block.outFracLen);
        end
        if ~status
            dtInfo.MaskHdlErrStatus = 1;
            return;
        end
        dtInfo.DataType = outUDDataType;
    else
        dtInfo.DataType = dspGetEditBoxParamValue(obj.handle,'outUDDataType');
    end
    if isempty(dtInfo.DataType)
        %garbage in = garbage out
        return; % EARLY RETURN
    end
    
    if isa(dtInfo.DataType, 'Simulink.NumericType')
        %
        % BOOLEAN
        % DOUBLE
        % SINGLE
        % FIXED-POINT: UNSPECIFIED SCALING
        % FIXED-POINT: BINARY POINT SCALING
        % FIXED-POINT: SLOPE AND BIAS SCALING
        %
        className = upper(dtInfo.DataType.DataTypeMode);
    else
        if ~isfield(dtInfo.DataType,'Class')
            dtInfo.DataType = [];
            return; % EARLY RETURN
        end
        className = dtInfo.DataType.Class;
    end
    
else
    % This is 'inherit via back-prop' or 'same as input' or etc.
    % Just return, we're not getting any useful info...
    return; % EARLY RETURN
end

% ---------------------------------
% DeterminesFracBits
% ---------------------------------
if strcmp(dtSetting,'User-defined')
    dtInfo.DataTypeDeterminesFracBits = dspDataTypeDeterminesFracBits(obj.outUDDataType);
elseif strcmp(dtSetting,'Fixed-point')
    dtInfo.DataTypeDeterminesFracBits = 0;
else
    dtInfo.DataTypeDeterminesFracBits = 1;
end

% ---------------------------------
% IsCustomFloat
% ---------------------------------
if strcmp(className,'FLOAT')
    % Custom Float types are not supported.
    % [Exceptions: (23,8) == SINGLE and (52,11) == DOUBLE]
    if ~( (dtInfo.DataType.MantBits==23 && dtInfo.DataType.ExpBits==8) || ...
          (dtInfo.DataType.MantBits==52 && dtInfo.DataType.ExpBits==11))
        error(message('comm:getCommDigBBModDTInfo:FloatingPtDataTypUnsupported'))
    end
end

% ---------------------------------
% IsSigned
% ---------------------------------
dtInfo.DataTypeIsSigned = 0; % DEFAULT
switch(className)
    case {'DOUBLE','SINGLE','INT8','INT16','INT32','FLOAT'}
        dtInfo.DataTypeIsSigned = 1;
    case {'INT','FRAC','FIX', ...
            'FIXED-POINT: BINARY POINT SCALING', ...
            'FIXED-POINT: SLOPE AND BIAS SCALING', ...
            'FIXED-POINT: UNSPECIFIED SCALING' }
        dtInfo.DataTypeIsSigned = double(dtInfo.DataType.IsSigned);
    case 'FIXEDPOINT'
        dtInfo.DataTypeIsSigned = 1;
end
if (dtInfo.DataTypeIsSigned == 0)
    error(message('comm:getCommDigBBModDTInfo:UnsignedDataTypUnsupported'))
end

% ---------------------------------
% WordLength
% ---------------------------------
switch(className)
    case 'DOUBLE'
        dtInfo.DataTypeWordLength = 64;
    case 'SINGLE'
        dtInfo.DataTypeWordLength = 32;
    case {'INT8','UINT8'}
        dtInfo.DataTypeWordLength = 8;
    case {'INT16','UINT16'}
        dtInfo.DataTypeWordLength = 16;
    case {'INT32','UINT32'}
        dtInfo.DataTypeWordLength = 32;
    case 'BOOLEAN'
        % using 1 for Boolean word length may cause an error,
        % and it's not really 1, anyway...
        dtInfo.DataTypeWordLength = 4;
    case 'FLOAT'
        dtInfo.DataTypeWordLength = dtInfo.DataType.MantBits+dtInfo.DataType.ExpBits+1;
    case {'INT','FRAC','FIX'}
        dtInfo.DataTypeWordLength = dtInfo.DataType.MantBits;
    case {'FIXED-POINT: BINARY POINT SCALING', ...
          'FIXED-POINT: SLOPE AND BIAS SCALING', ...
          'FIXED-POINT: UNSPECIFIED SCALING' }
        dtInfo.DataTypeWordLength = dtInfo.DataType.WordLength;
    case 'FIXEDPOINT'
        if usingMaskDlgHdl
            [dtInfo.DataTypeWordLength, status] = localGetParamValue(obj,obj.Block.outWordLen);
            if ~status
                dtInfo.MaskHdlErrStatus = 1;
                return;
            end
            
        else
            dtInfo.DataTypeWordLength = dspGetEditBoxParamValue(obj.handle,'outWordLen');
        end
end

% ---------------------------------
% FracOrMantBits
% ---------------------------------
switch(className)
    case 'DOUBLE'
        dtInfo.DataTypeFracOrMantBits = 52;
    case 'SINGLE'
        dtInfo.DataTypeFracOrMantBits = 23;
    case {'INT8','UINT8','INT16','UINT16','INT32','UINT32','BOOLEAN'}
        dtInfo.DataTypeFracOrMantBits = 0;
    case {'FLOAT','INT','FRAC','FIX', ...
          'FIXED-POINT: BINARY POINT SCALING', ...
          'FIXED-POINT: SLOPE AND BIAS SCALING', ...
          'FIXED-POINT: UNSPECIFIED SCALING' }
        if usingMaskDlgHdl
            [dtInfo.DataTypeFracOrMantBits status] = localGetFracOrMantLen(obj, dtInfo.DataType,usingMaskDlgHdl);
            if ~status
                dtInfo.MaskHdlErrStatus = 1;
                return;
            end
        else
            [dtInfo.DataTypeFracOrMantBits status] = localGetFracOrMantLen(obj.handle, dtInfo.DataType,usingMaskDlgHdl);
        end
    case 'FIXEDPOINT'
        if usingMaskDlgHdl
            [wordLen, status] = localGetParamValue(obj,obj.Block.outWordLen);
            if ~status
                dtInfo.MaskHdlErrStatus = 1;
                return;
            end
        else
            wordLen = dspGetEditBoxParamValue(obj.handle,'outWordLen');
        end
        
        if (isempty(wordLen))
            wordLen = 0;
        end
        if usingMaskDlgHdl
            [dtInfo.DataTypeFracOrMantBits status] = localGetFracOrMantLen(obj, sfix(wordLen),usingMaskDlgHdl);
            if ~status
                dtInfo.MaskHdlErrStatus = 1;
                return;
            end
        else
            [dtInfo.DataTypeFracOrMantBits status] = localGetFracOrMantLen(obj.handle, sfix(wordLen),usingMaskDlgHdl);
        end
end


% ===================================
% LOCAL HELPER FUNCTIONS LISTED BELOW
% ===================================

function [numBits,status] = localGetFracOrMantLen(blkh,dtObject,usingMaskDlgHdl)
% usingMaskDlgHdl is 1 if the input is a Mask Handle and 0 if it is a Block
% handle. The status output is 0 if there is any error in the parameter
% evaluation and 1 if no errors.

className = upper(localGetCategory(dtObject));
status = 1;

switch(className)
    case 'SINGLE'
        numBits = 23;
    case 'DOUBLE'
        numBits = 52;
    case 'FLOAT'
        numBits = dtObject.MantBits;
    case 'INT'
        numBits = 0;
    case 'BOOLEAN'
        numBits = 0;
    case 'FRAC'
        numBits = dtObject.MantBits - dtObject.GuardBits - dtObject.IsSigned;
    case {'FIX', 'FIXED-POINT: UNSPECIFIED SCALING' }
        % If the scale param is invisible, must make it visible before accessing it
        if ~usingMaskDlgHdl
            maskParams = get_param(blkh,'MaskNames');
            ii         = find(strcmp(maskParams,'outFracLenMode'));
            vis        = get_param(blkh,'MaskVisibilities');
            vis_off    = strcmp(vis(ii),{'off'});
            
            if vis_off
                vis(ii) = {'on'};
                set_param(blkh,'MaskVisibilities',vis);
            end
        end
        
        if usingMaskDlgHdl
            isBestPrec = strcmp(blkh.Block.outFracLenMode,'Best precision');
        else
            isBestPrec = strcmp(get_param(blkh,'outFracLenMode'),'Best precision');
        end
        
        if ~usingMaskDlgHdl
            if vis_off
                vis(ii) = {'off'};
                set_param(blkh,'MaskVisibilities',vis);
            end
        end
        
        if isBestPrec
            % Unknown fraction length -> it will be determined by the S-fcn
            numBits = NaN;
        else
            % Fraction length specified in the fraction length edit box
            if usingMaskDlgHdl
                [numBits, status] = localGetParamValue(blkh,blkh.Block.outFracLen);
            else
                numBits = dspGetEditBoxParamValue(blkh,'outFracLen');
            end
        end
    case 'FIXED-POINT: BINARY POINT SCALING'
        numBits = -dtObject.FixedExponent;
    case 'FIXED-POINT: SLOPE AND BIAS SCALING'
        numBits = -dtObject.FixedExponent;
        if (dtObject.SlopeAdjustmentFactor ~= 1.0) || (dtObject.Bias ~= 0.0)
            error(message('comm:getCommDigBBModDTInfo:UnsupportedFixPtScaling'))
        end
    otherwise
        % unknown class => unknown fracbits
        numBits = NaN;
end



function category = localGetCategory(dtObject)

if isa(dtObject, 'Simulink.NumericType')
    category = dtObject.DataTypeMode;
elseif isfield(dtObject,'Class')
    category = dtObject.Class;
else
    category = [];
end



function [val,status] = localGetParamValue(hBlkDlg,param)
% The function evaluates parameters in the following order:
% 1. Model Workspace as done by Simulink
% 2. Base Workspace
% The function returns the value and the error status
% hBlkDlg input is the Mask Handle

hws      = hBlkDlg.Root.ModelWorkspace;
try
    try
        val   = hws.evalin(param);
    catch MEParam1
        val   = evalin('base',param);
    end
    status = 1;
catch MEParam1 %#ok<*NASGU>
    % The parameter expression is invalid
    val = 0;
    status = 0;
end


% [EOF] getCommDigBBModDTInfo.m
