function demod = ehtDemodulate(rx,fieldname,cfg,varargin)
%ehtDemodulate Demodulate EHT fields
%
%   SYM = ehtDemodulate(RX,FIELDNAME,CFG) demodulates the time-domain
%   received signal RX using OFDM demodulation parameters appropriate for
%   the resource unit (RU) of interest for the specified FIELDNAME.
%
%   SYM is the demodulated frequency-domain signal, returned as a complex
%   matrix or 3-D array of size Nst-by-Nsym-by-Nr. Nst is the number of
%   active (occupied) subcarriers in the field. Nsym is the number of OFDM
%   symbols. Nr is the number of receive antennas.
%
%   RX is the received time-domain signal, specified as a complex matrix of
%   size Ns-by-Nr, where Ns represents the number of time-domain samples.
%   If Ns is not an integer multiple of the OFDM symbol length for the
%   specified field, then mod(Ns,symbol length) trailing samples are
%   ignored.
%
%   FIELDNAME is the field to demodulate and must be 'L-LTF', 'L-SIG',
%   'RL-SIG', 'U-SIG', 'EHT-SIG', 'EHT-LTF', or 'EHT-Data'.
%
%   CFG is a format configuration object of type <a
%   href="matlab:help('ehtMUConfig')">ehtMUConfig</a>.
%
%   SYM = ehtDemodulate(RX,EHTFIELD,CFG,RUNUMBER) returns the demodulated
%   symbols for the resource unit (RU) of interest for a multi-user
%   configuration. EHTFIELD is 'EHT-Data' or 'EHT-LTF'. When not provided
%   the RUNUMBER is assumed to be one.
%
%   SYM = ehtDemodulate (...,'OversamplingFactor',OSF) specifies the
%   optional oversampling factor of the waveform to demodulate. The
%   oversampling factor must be a greater than or equal to 1. The default
%   value is 1. Specifying an oversampling factor greater than 1 uses a
%   larger FFT size to demodulate the oversampled waveform. The
%   oversampling factor must result in an integer number of samples in the
%   cyclic prefix.
%
%   SYM = ehtDemodulate (...,'OFDMSymbolOffset',SYMOFFSET) specifies the
%   optional OFDM symbol sampling offset as a fraction of the cyclic prefix
%   length between 0 and 1, inclusive. When unspecified, a value of 0.75 is
%   used.
%
%   Example 1:
%   % Demodulate the EHT-Data field.
%
%      cfg = ehtMUConfig('CBW320');
%      txPSDU = randi([0 1],getPSDULength(cfg)*8,1);
%      tx = ehtData(txPSDU,cfg);
%       
%      % Demodulate the Data field
%      demod = ehtDemodulate(rx,'EHT-Data',cfg,1);
%
%   See also ehtOFDMInfo.

%   Copyright 2020-2021 The MathWorks, Inc.

narginchk(3,8);

% Validate rx and field name
validateattributes(rx,{'single','double'},{'2d','finite'},mfilename,'rx');
[numSamples,numRx] = size(rx);
coder.internal.errorIf(numRx==0,'wlan:shared:NotEnoughAntennas');
fieldname = validatestring(fieldname,{'EHT-Data','EHT-LTF','EHT-SIG','U-SIG','RL-SIG','L-SIG','L-LTF'},mfilename,'field');
validateattributes(cfg,{'ehtMUConfig'},{'scalar'},mfilename,'format configuration object');

[symOffset,osf,ruNum] = demodNVPairParse(varargin{:});
inp = [{ruNum},{'OversamplingFactor'},{osf}];
cfgOFDM = ehtOFDMInfo(fieldname,cfg,inp{:});

switch fieldname
    case 'EHT-Data'
        % Validate input length
        wlan.internal.demodValidateMinInputLength(numSamples,cfgOFDM);

        % OFDM demodulate
        demod = wlan.internal.ofdmDemodulate(rx,cfgOFDM,symOffset);
    case 'EHT-LTF'
        switch cfg.EHTLTFType
            case 1
                N_EHT_LTF_Mode = 4;
            case 2
                N_EHT_LTF_Mode = 2;
            otherwise % 4
                N_EHT_LTF_Mode = 1;
        end

        uncompFFTLength = cfgOFDM.FFTLength;
        compFFTLength = uncompFFTLength/N_EHT_LTF_Mode;

        % Calculate number of symbols to demodulate
        Ns = compFFTLength+cfgOFDM.CPLength;

        % Validate input length
        wlan.internal.demodValidateMinInputLength(numSamples,Ns);

        if numSamples==0
            demod = complex(zeros(cfgOFDM.NumTones,0,numRx)); % Return empty for 0 samples
            return;
        end

        numSym = floor(numSamples/Ns);
        numSamples = numSym*(compFFTLength+cfgOFDM.CPLength);

        % OFDM demodulate
        prmStr = struct;
        prmStr.NumReceiveAntennas = numRx;
        prmStr.FFTLength = compFFTLength; % Use compressed FFT length
        prmStr.NumSymbols = numSym;
        prmStr.SymbolOffset = round(symOffset*cfgOFDM.CPLength);
        prmStr.CyclicPrefixLength = cfgOFDM.CPLength;
        compressedfftout = coder.nullcopy(complex(zeros(compFFTLength,numSym,numRx)));
        compressedfftout(:) = comm.internal.ofdm.demodulate(rx(1:numSamples,:),prmStr);

        % Decompress EHT-LTF
        fftout = complex(zeros(uncompFFTLength,numSym,numRx));
        fftout(1:N_EHT_LTF_Mode:end,:,:) = compressedfftout;

        % Extract active subcarriers from full FFT
        demod = fftout(cfgOFDM.ActiveFFTIndices,:,:);

        % Scale by number of tones and FFT length at transmitter
        demod = demod*sqrt(cfgOFDM.NumTones/N_EHT_LTF_Mode)/compFFTLength;
    case {'EHT-SIG','U-SIG','RL-SIG','L-SIG'}

        % Validate input length
        wlan.internal.demodValidateMinInputLength(numSamples,cfgOFDM);

        demod = wlan.internal.ofdmDemodulate(rx,cfgOFDM,symOffset);
    otherwise % 'L-LTF'
        assert(strcmp(fieldname,'L-LTF'))

        % Validate input length
        wlan.internal.demodValidateMinInputLength(numSamples,cfgOFDM);

        % Add extra L-SIG tones onto NumTones so we scale at receiver
        % the same for L-SIG field
        cfgOFDM.NumTones = cfgOFDM.NumTones+4*cfgOFDM.NumSubchannels;

        % Demodulate
        demod = wlan.internal.demodulateLLTF(rx,cfgOFDM,symOffset);
end

end

function [symOffset,osf,ruNum] = demodNVPairParse(varargin)
%demodNVPairParse Parse optional name-value pairs for demodulation
%
%   [SYMOFFSET,OSF,RUNUM] = demodNVPairParse(IN) parses the cell array IN
%   to determine the optional OFDM symbol offset, oversampling factor, and
%   RU Number as a name-value pair.

% Default
symOffset = 0.75;
osf = 1;
ruNum = 1;

% Validate each P-V pair
p = inputParser;
% Set defaults for the optional arguments
validSymOffset = @(x)validateattributes(x,{'numeric'},{'scalar','>=',0,'<=',1},mfilename,'OFDM symbol offset');
addOptional(p,'RUNumber',ruNum); % Do not validate ruNum as validated in ehtOFDMInfo
addParameter(p,'OFDMSymbolOffset',symOffset,validSymOffset);
addParameter(p,'OversamplingFactor',osf); % Do not validate OSF as validated in ehtOFDMInfo
parse(p,varargin{:}); % Parse inputs
res = p.Results;
symOffset = res.OFDMSymbolOffset;
osf = res.OversamplingFactor;
ruNum = res.RUNumber;

end