function Signal = OFDMMod( Symbols, NCarriers, LPrefix )
% OFDMMod - Orthogonal frequency divison multiplexing modulator
%
% Inputs:
%   Symbols    - vector of information symbols to be modulated
%   NCarriers  - number of carriers; block size
%   LPrefix    - length of cyclic prefix
%
% Output:
%   Signal     - OFDM signal
%
% Example:
%   Signal = OFDMMod( Symbols, NCarriers, LPrefix )

%% convert symbols to row vector
% the processing below assumes signals passed as row vector. We enforce
% this convention here and restore matters at the end.
OrigSize     = size(Symbols);
DoReverseDim = false;

if ( min( OrigSize ) > 1 )
    error('First input must be a row or column vector');
end

if( OrigSize(1) > 1 ) % it's a column vector
    Symbols = Symbols.';
    DoReverseDim = true;
end

%% Serial-to-parallel conversion
NBlocks = ceil( length(Symbols) / NCarriers );
% zero-pad, if needed
Blocks = zeros( 1, NBlocks*NCarriers );
Blocks(1:length(Symbols)) = Symbols;
% serial-to-parallel
Blocks = reshape( Blocks, NCarriers, NBlocks );

%% IFFT
% ifft works column-wise by default
Blocks = ifft( Blocks );

%% cyclic prefix
% copy last LPrefix samples of each column and prepend
Blocks = [ Blocks( end-LPrefix+1 : end, : ) ; Blocks ];

%% Parallel-to-serial conversion
Signal = reshape( Blocks, 1, NBlocks*(NCarriers+LPrefix) ) * ...
    sqrt(NCarriers); % makes "gain" of ifft equal to 1.

%% restore original dimensions
if ( DoReverseDim )
    Signal = Signal.';
end