function commblkoqpskmod(blkh, action, varargin)
% COMMBLKOQPSKMOD Mask helper fcn for OQPSK Modulator/Demodulator Baseband blocks.

%   Copyright 2017-2023 The MathWorks, Inc.

switch(action)
  case 'cbPulseShape'
    cbPulseShape(blkh); % change filter-prop visibilities
  case 'initmod'
    [sps, rolloff, filterSpan] = deal(varargin{:});
    oqpskModInit(blkh, sps, rolloff, filterSpan); % setup modulator subsystem
  case 'initdemod'
    [sps, Ph, Mapping, rolloff, filterSpan] = deal(varargin{:});
    oqpskDemodInit(blkh, sps, rolloff, filterSpan); % setup demodulator subsystem
end

function validateSps(sps)

validateattributes(sps, {'numeric'}, ...
      {'scalar', 'real', 'positive', 'finite', 'integer', 'even'}, ...
      '', 'Samples per symbol');

function cbPulseShape(blkh)
En   = get_param(blkh, 'maskenables');
Vis  = get_param(blkh, 'maskvisibilities');
    
% --- New (R2017b) properties (Denominator indroduced in 24a)
[idxNum, idxDenom, idxRolloff, idxFilterSpan] = dspGetMaskParamIdx(blkh, 'num', 'denom', 'rolloff', 'filterSpan');

pulseShape = get_param(blkh,'pulseShape');

% hide filter specific properties:
if any(strcmp(pulseShape, {'Normal raised cosine', 'Root raised cosine'}))
  En(  [idxRolloff, idxFilterSpan])  = {'on'};
  Vis( [idxRolloff, idxFilterSpan])  = {'on'};
else
  En(  [idxRolloff, idxFilterSpan])  = {'off'};
  Vis( [idxRolloff, idxFilterSpan])  = {'off'};
end

if strcmp(pulseShape, 'Custom')
  En( [idxNum, idxDenom])  = {'on'};
  Vis([idxNum, idxDenom])  = {'on'};
else
  En( [idxNum, idxDenom])  = {'off'};
  Vis([idxNum, idxDenom])  = {'off'};
end

% apply all changes:
set_param(blkh, 'maskenables', En, 'maskvisibilities', Vis);


function oqpskModInit(blkh, spsNum, rolloff, filterSpan)

% setup modulator subsystem
validateSps(spsNum); % in case it was set with set_param, not through the dialog

% Filter
firFilterBlock = [blkh '/Pulse shaping/FIR/Pulse shaping'];
oqpskCommonInit(blkh, firFilterBlock, spsNum, rolloff, filterSpan);
% iirFilter doesn't need setup, 'num' and 'denom' are already plugged to its mask dialog

% propagate configuration to the Subsystem implementation
% Bit to Integer converter
if strcmp(get_param(blkh, 'InType'), 'Bit')
  set_param([blkh sprintf('/Preprocessing')], 'LabelModeActiveChoice', 'BIC');
else
  set_param([blkh sprintf('/Preprocessing')], 'LabelModeActiveChoice', 'NoBIC');
end

% Symbol mapping
mapping = get_param(blkh, 'Mapping');
block = [blkh sprintf('/Symbol mapping')];
if strcmpi(mapping, 'Gray')
  % take into account the bit to quadrant mapping, so [0 1 3 2] -> [3 1 2 0]
  set_param(block, 'Table', '[3 1 2 0]');
elseif strcmpi(mapping, 'Binary')
  % take into account the bit to quadrant mapping, so [0 1 2 3] -> [3 1 0 2]
  set_param(block, 'Table', '[3 1 0 2]');
else % custom
  mapping = str2num(mapping); %#ok<ST2NM>
  % same as comm.OQPSKModulator
  % mapping = [a b c d]:
  %  b | a        -+ | ++        01 | 11      1 | 3
  % ---+---   => ----+----   => --------- => -------
  %  c | d        -- | +-        00 | 10      0 | 2
  %
  % [a b c d] => [3 1 0 2]
  [~, idx] = sort(mapping);
  bitRep = [3 1 0 2];
  set_param(block, 'Table', mat2str(bitRep(idx)));
end

function oqpskDemodInit(blkh, spsNum, rolloff, filterSpan)

% setup demodulator subsystem
validateSps(spsNum); % in case it was set with set_param, not through the dialog

% Data type:
typeConversionBlock  = [blkh '/Data Type Conversion'];  
qpskDemodulator = [blkh sprintf('/QPSK\nDemodulator\nBaseband')];  
type = get_param(blkh, 'outDtype2');
if any(strcmp(type, {'double', 'single'}))
  set_param(typeConversionBlock, 'OutDataTypeStr', type);  
else % most efficient processing for integer outputs:
  set_param(typeConversionBlock, 'OutDataTypeStr', 'single');
end
set_param(qpskDemodulator, 'outDtype2', type);

% Filter  
filterBlock = [blkh '/Matched filter'];

% Common mod/demod set-up:
oqpskCommonInit(blkh, filterBlock, spsNum, rolloff, filterSpan);

% Decimation offset
pulseShape = get_param(blkh, 'pulseShape');
offset = spsNum/2;
if strcmp(pulseShape, 'Custom')
  num = str2num(get_param(blkh, 'num')); %#ok<ST2NM>
  % This is to ensure proper decimation for rectangular with SPS = 2
  if numel(num) <= 2
    offset = 0;
  end 
end
set_param(filterBlock, 'inputOffset', num2str(offset));

% Integer-to-bit conversion:
if strcmp(get_param(blkh, 'OutType'), 'Bit')
  set_param([blkh sprintf('/Postprocessing')], 'LabelModeActiveChoice', 'IBC');
else
  set_param([blkh sprintf('/Postprocessing')], 'LabelModeActiveChoice', 'NoIBC');
end

% Symbol mapping
mapping = get_param(blkh, 'Mapping');
block = [blkh sprintf('/Symbol mapping')];
if strcmpi(mapping, 'Gray')
  set_param(block, 'Table', '[0 1 2 3]');
elseif strcmpi(mapping, 'Binary')
  set_param(block, 'Table', '[0 1 3 2]');
else % Custom
  mapping = str2num(mapping); %#ok<ST2NM>
  set_param(block, 'Table', mat2str(mapping([1 2 4 3])));
end


function oqpskCommonInit(blkh, filterBlock, spsNum, rolloff, filterSpan)


% Filtering
set_param(filterBlock, 'FilterSource', 'Dialog parameters');


pulseShape = get_param(blkh, 'pulseShape');
if strcmp(pulseShape, 'Half sine')
  halfSinePulse = sin(0:pi/spsNum:(spsNum)*pi/spsNum);
  set_param(filterBlock, 'h', ['[' num2str(halfSinePulse, '%.5f ') ']' ]);
  
elseif strcmp(pulseShape, 'Custom')
  if ~(strcmp(get_param(blkh, 'Name'), sprintf('OQPSK\nModulator\nBaseband')) && ...
       strcmp('on', get_param(blkh, 'isIIR')))

    set_param(filterBlock, 'h', get_param(blkh, 'num'));
  end
  % else, for IIR filter, num and denom are already entered in the mask dialog

elseif strcmp(pulseShape, 'Rectangular')
    
  coeff = ones(1, spsNum);
  set_param(filterBlock, 'h', ['[' num2str(coeff)  ']' ]);
  
else % Normal/root raised cosine
  
  if strcmp(pulseShape, 'Normal raised cosine')
    coeff = rcosdesign(rolloff, filterSpan, spsNum, 'normal');
  else % Root
    coeff = rcosdesign(rolloff, filterSpan, spsNum, 'sqrt');
  end
  set_param(filterBlock, 'h', ['[' num2str(sqrt(spsNum/2)*coeff/sqrt(sum(coeff.^2))) ']' ]);
end

% Rate options:
rateOptions = get_param(blkh, 'RateOptions');
set_param(filterBlock, 'framing', rateOptions);
delayBlock = [blkh sprintf('/Delay')];
% support the two old modes:
if strcmp(rateOptions, 'Allow multirate processing')
  set_param([blkh sprintf('/Check Multirate\nDimensionality')], 'LabelModeActiveChoice', 'Check');
  set_param(filterBlock, 'InputProcessing',  'Elements as channels (sample based)');
  set_param(delayBlock, 'InputProcessing',  'Elements as channels (sample based)');
else
  set_param([blkh sprintf('/Check Multirate\nDimensionality')], 'LabelModeActiveChoice', 'NoCheck');
  set_param(filterBlock, 'InputProcessing',  'Columns as channels (frame based)');
  set_param(delayBlock, 'InputProcessing',  'Columns as channels (frame based)');
end


