function self=generateGenericOFDM(self, varargin)

p=vararginToStruct(varargin);
defaultParams=struct('modulation', '16QAM');
p=mergeDefaultParams(p, defaultParams);

sc=p.subcarrierIndices;
assert(isRow(sc));
nSc=size(sc, 2);

% * ********************************************
% * build modulation alphabet if changed / new
% * ********************************************
modulation=p.modulation;
switch(modulation)
  case 'QPSK'
    u=1;
  case '16QAM'
    u=3;
  case '64QAM'
    u=7;
  case '256QAM'
    u=15;
  otherwise
    error('unknown modulation')
end

alphabet=[];
for x=0:u
    for y=0:u
        s=x+1i*y;
        alphabet=[alphabet, s];      
    end
end  
alphabetNorm=alphabet-(1+1i)*(u)/2;

RMS=sum(alphabetNorm .* conj(alphabetNorm)) / size(alphabetNorm, 2);
alphabetNorm=alphabetNorm / sqrt(RMS);

% *******************************************************************
% IFFT input stream
% *******************************************************************
IFFT_indices=mod(sc, p.nFFT)+1; % 0 maps to 1
IFFT_input=zeros(p.nSymbols, p.nFFT);
for symIndex=1:p.nSymbols
    
    index=floor(rand(1, nSc)*size(alphabet, 2))+1;
    s=alphabetNorm(index);

    % *******************************************************************
    % scale to target power
    % note: an nFFT factor comes from matlab's ifft normalization
    % *******************************************************************
    s=s / sqrt(nSc); 
    
    IFFT_input(symIndex, IFFT_indices)=s;
end

% *************************************************************
% build time-domain stream
% *************************************************************
stream=[];
gap=zeros(1, p.nCp);
for symIndex=1:p.nSymbols
    thisSymbolIndex=mod(symIndex-1, p.nSymbols)+1;
    prevSymbolIndex=mod(symIndex-2, p.nSymbols)+1;
    
    thisSymbol=IFFT_input(thisSymbolIndex, :);
    prevSymbol=IFFT_input(prevSymbolIndex, :);
    
    % *************************************************************
    % symbol-body from IFFT
    % *************************************************************
    body=p.nFFT * ifft(thisSymbol);
    stream=[stream; [gap, body]]; % stack vertically
end
stream=reshape(transpose(stream), 1, numel(stream)); % convert to horizontal stream

% *************************************************************
% Create window ]0, 1[
% *************************************************************
winThis=linspace(0, 1, p.nCp+2);
winThis=winThis(2:end-1);

winThis=(1-cos(pi*winThis))/2;
winPrev=fliplr(winThis);

% *************************************************************
% Apply window
% *************************************************************
symLen=p.nFFT+p.nCp;
for symIndex=1:p.nSymbols
    
    thisSymbolIndex=mod(symIndex-1, p.nSymbols)+1;
    prevSymbolIndex=mod(symIndex-2, p.nSymbols)+1;
    
    % sample indices pointing at the gap preceding "this" symbol
    thisGapFirst=(thisSymbolIndex-1)*symLen+1;
    thisGapLast=thisGapFirst+p.nCp-1;

    % sample indices used to extend "this" symbol periodically into the gap
    thisGapReplFirst=thisGapFirst+p.nFFT;
    thisGapReplLast=thisGapLast+p.nFFT;
    
    % sample indices pointing at the gap before the preceding symbol
    prevGapFirst=(prevSymbolIndex-1)*symLen+1;
    prevGapLast=prevGapFirst+p.nCp-1;

    % sample indices used to extend the preceding symbol periodically into the gap
    prevGapReplFirst=prevGapFirst+p.nCp;
    prevGapReplLast=prevGapLast+p.nCp;
    
    % retrieve periodic extensions 
    srcPrev=stream(prevGapReplFirst:prevGapReplLast);
    srcThis=stream(thisGapReplFirst:thisGapReplLast);
    
    % window and add
    xover=srcPrev .* winPrev + srcThis .* winThis;

    % replace current gap in stream
    stream(thisGapFirst:thisGapLast) = xover;
end

self=setTD(self, stream);
