function y = ehtLTF(cfgEHT,varargin)
%ehtLTF EHT Long Training Field (EHT-LTF)
%
%   Y = ehtLTF(CFGEHT) generates the EHT Long Training Field (EHT-LTF)
%   time-domain signal for the EHT transmission format.
%
%   Y is the time-domain EHT-LTF signal. It is a complex matrix of size
%   Ns-by-Nt where Ns represents the number of time-domain samples and Nt
%   represents the number of transmit antennas.
%
%   CFGEHT is the format configuration object of type <a
%   href="matlab:help('ehtMUConfig')">ehtMUConfig</a>.
%
%   Y = ehtLTF(CFGEHT,OSF) generates a signal oversampled by the
%   oversampling factor OSF. When not specified, 1 is assumed.
%
%   % Example: Generate an EHT-LTF field for a 320 MHz, EHT MU PPDU format.
%
%   cfgEHT = ehtMUConfig('CBW320');
%   y = ehtLTF(cfgEHT);
%   plot(abs(y));
%
%   See also ehtMUConfig, ehtData

%   Copyright 2020-2021 The MathWorks, Inc.

osf = 1;
if nargin>1
    osf = varargin{1};
end

switch cfgEHT.EHTLTFType
    % EHT-LTF-Mode. IEEE P802.11be/D1.0, Section 36.3.12.10
    case 1
        N_EHT_LTF_Mode = 4;
    case 2
        N_EHT_LTF_Mode = 2;
    otherwise % 4
        N_EHT_LTF_Mode = 1;
end

cbw = wlan.internal.cbwStr2Num(cfgEHT.ChannelBandwidth);
numTx = cfgEHT.NumTransmitAntennas;

allocationInfo = ruInfo(cfgEHT); % RU allocation info
RU = cfgEHT.RU;
powerBoostFactor = allocationInfo.PowerBoostFactorPerRU;
Nltf = wlan.internal.numVHTLTFSymbols(max(allocationInfo.NumSpaceTimeStreamsPerRU));
stsStart = 1;

% Get EHT-LTF sequence
[EHTLTF,kEHTLTFSeq] = ehtLTFSequence(cbw,cfgEHT.EHTLTFType);

% Orthogonal mapping matrix
Pheltf = wlan.internal.mappingMatrix(Nltf);

Nfft = 256*osf*cbw/20;
ofdmGrid = complex(zeros(Nfft,Nltf,numTx));
cardKr = coder.nullcopy(zeros(1,allocationInfo.NumRUs));
cardKEHTLTFr = coder.nullcopy(zeros(1,allocationInfo.NumRUs));

for j = 1:allocationInfo.NumRUs
    % Get the index of the RU object containing the active RU properties
    jru = allocationInfo.RUNumbers(j);

    % Determine the number of STS in this RU, and the indices of the
    % space-time streams. The indices may not always start at 1 due to the
    % trigger-based format.
    numSTS = allocationInfo.NumSpaceTimeStreamsPerRU(j);
    stsIdx = stsStart-1+(1:numSTS).';

    % Extract the RU of interest from the full-bandwidth EHT-LTF
    [ruInd,kRU] = ehtOccupiedSubcarrierIndices(cbw,RU{jru}.Size,RU{jru}.Index);

    seqIdx = wlan.internal.intersectRUIndices(kEHTLTFSeq,kRU);
    EHTLTFRU = EHTLTF(seqIdx);

    % Calculate P and R matrices
    P = Pheltf(stsIdx,1:Nltf);
    R = repmat(Pheltf(1,1:Nltf),numSTS,1);
    ruGrid = coder.nullcopy(complex(zeros(numel(kRU),Nltf,numSTS)));

    for k = 1:Nltf
        % Single stream pilots
        ruGrid(ruInd.Data,k,:) = EHTLTFRU(ruInd.Data).*P(:, k).';
        ruGrid(ruInd.Pilot,k,:) = EHTLTFRU(ruInd.Pilot).*R(:, k).';
    end

    % Cyclic shift
    ruGrid = wlan.internal.heCyclicShift(ruGrid,cbw,kRU,stsIdx);

    % Spatial mapping  
    txRUGrid = wlan.internal.spatialMap(ruGrid,RU{jru}.SpatialMapping,numTx,RU{jru}.SpatialMappingMatrix);

    % Calculate per RU scaling as per IEEE P802.11ax/D4.1, Section 27.3.9,
    % Equation 27-5.
    cardKr(j) = numel(kRU);
    cardKEHTLTFr(j) = cardKr(j)/N_EHT_LTF_Mode;
    numSTSTotalRU = allocationInfo.NumSpaceTimeStreamsPerRU;

    % Note we scale by sqrt(cardK)/sqrt(cardKEHTLTFr). This causes us to
    % normalize the power in the RU.
    ruScalingFactor = (powerBoostFactor(j)*sqrt(cardKr(j)))/sqrt(numSTSTotalRU(j)*cardKEHTLTFr(j));

    % Map subcarriers to full FFT grid and scale
    ofdmGrid(kRU+Nfft/2+1,:,:) = txRUGrid*ruScalingFactor;
end

% Overall scaling factor
allScalingFactor = Nfft/sqrt(sum(powerBoostFactor.^2.*cardKr));

switch cfgEHT.GuardInterval
    case 0.8
        CPLenSamples = 0.8*cbw*osf;
    case 1.6
        CPLenSamples = 1.6*cbw*osf;
    otherwise % 3.2
        assert(cfgEHT.GuardInterval==3.2)
        CPLenSamples = 3.2*cbw*osf;
end
if cfgEHT.EHTLTFType == 1
    % Transmitting 1/4 of 12.8us OFDM symbol
    y = wlan.internal.wlanOFDMModulate(ofdmGrid(1:4:end,:,:),CPLenSamples).*allScalingFactor/4; %/4 to account for NFFT/4 actual FFT size
elseif cfgEHT.EHTLTFType == 2
    % Transmitting 1/2 of 12.8us OFDM symbol
    y = wlan.internal.wlanOFDMModulate(ofdmGrid(1:2:end,:,:),CPLenSamples).*allScalingFactor/2; %/2 to account for NFFT/2 actual FFT size
else % Sequence 4
    % Transmitting 12.8us OFDM symbol
    y = wlan.internal.wlanOFDMModulate(ofdmGrid,CPLenSamples).*allScalingFactor;
end

end