function y = fskmod(x,M,freq_sep,nSamp,varargin)
%FSKMOD Frequency shift keying modulation
%   Y = FSKMOD(X,M,FREQ_SEP,NSAMP) outputs the complex envelope of the
%   modulation of the message signal X using frequency shift keying modulation. M
%   is the alphabet size and must be an integer power of two.  The message
%   signal must consist of integers between 0 and M-1.  FREQ_SEP is the desired
%   separation between successive frequencies, in Hz.  NSAMP denotes the number
%   of samples per symbol and must be an integer greater than 1.  For two
%   dimensional signals, the function treats each column as one channel.
%
%   Y = FSKMOD(X,M,FREQ_SEP,NSAMP,FS) specifies the sampling frequency (Hz).
%   The default sampling frequency is 1.
%
%   Y = FSKMOD(X,M,FREQ_SEP,NSAMP,FS,PHASE_CONT) specifies the phase continuity
%   across FSK symbols.  PHASE_CONT can be either 'cont' for continuous phase, 
%   or 'discont' for discontinuous phase.  The default is 'cont'.
%
%   Y = FSKMOD(X,M,FREQ_SEP,NSAMP,Fs,PHASE_CONT,SYMBOL_ORDER) specifies how the
%   function assigns binary words to corresponding integers. If SYMBOL_ORDER is
%   set to 'bin' (default), then the function uses a natural binary-coded
%   ordering. If SYMBOL_ORDER is set to 'gray', then the function uses a
%   Gray-coded ordering.
%
%   See also FSKDEMOD, PSKMOD, QAMMOD, PAMMOD, OQPSKMOD.

%   Copyright 1996-2012 The MathWorks, Inc.


% Error checks -----------------------------------------------------------------
if (nargin < 4)
    error(message('comm:fskmod:numarg1'));
end

if (nargin > 7)
    error(message('comm:fskmod:numarg2'));
end

% Check X
if (~isreal(x) || any(any(ceil(x) ~= x)) || ~isnumeric(x))
    error(message('comm:fskmod:xreal1'));
end

% Check that M is a positive integer
if (~isreal(M) || ~isscalar(M) || M<2 || (ceil(M)~=M) || ~isnumeric(M))
    error(message('comm:fskmod:Mreal'));
end

% Check that M is of the form 2^K
if(~isnumeric(M) || ceil(log2(M)) ~= log2(M))
    error(message('comm:fskmod:Mpow2'));
end

%Check that all X are integers within [0,M-1]
if ((min(min(x)) < 0) || (max(max(x)) > (M-1)))
    error(message('comm:fskmod:xreal2'));
end

% Check that the FREQ_SEP is greater than 0
if( ~isnumeric(freq_sep) || ~isscalar(freq_sep) || freq_sep<=0 )
    error(message('comm:fskmod:freqSep'));
end

% Check that NSAMP is an integer greater than 1
if((~isnumeric(nSamp) || (ceil(nSamp) ~= nSamp)) || (nSamp <= 1))
    error(message('comm:fskmod:nSampPos'));
end

% Check Fs
if (nargin >= 5)
Fs = varargin{1};
    if (isempty(Fs))
        Fs = 1;
    elseif (~isreal(Fs) || ~isscalar(Fs) || ~isnumeric(Fs) || Fs<=0)
        error(message('comm:fskmod:FsReal'));
    end
else
    Fs = 1;
end
samptime = 1/Fs;

% Check that the maximum transmitted frequency does not exceed Fs/2
maxFreq = ((M-1)/2) * freq_sep;
if (maxFreq > Fs/2)
    error(message('comm:fskmod:maxFreq'));
end

% Check if the phase is continuous or discontinuous
if (nargin >= 6)
    phase_type = varargin{2};
    %check the phase_type string
    if ~( strcmpi(phase_type,'cont') ||  strcmpi(phase_type,'discont') )
        error(message('comm:fskmod:phaseCont'));
    end

else
    phase_type = 'cont';
end

if (strcmpi(phase_type, 'cont'))
    phase_cont = 1;
else
    phase_cont = 0;
end

% Check SYMBOL_ORDER
if(nargin >= 4 && nargin <= 6 )    
   Symbol_Ordering = 'bin';         % default
else
    Symbol_Ordering = varargin{3};
    if (~ischar(Symbol_Ordering)) || (~strcmpi(Symbol_Ordering,'GRAY')) && (~strcmpi(Symbol_Ordering,'BIN'))
        error(message('comm:fskmod:SymbolOrder'));    
    end
end

% End of error checks ----------------------------------------------------------


% Assure that X, if one dimensional, has the correct orientation
wid = size(x,1);
if (wid == 1)
    x = x(:);
end

% Gray encode if necessary
if (strcmpi(Symbol_Ordering,'GRAY'))
    [~,gray_map] = bin2gray(x,'fsk',M);   % Gray encode
    [~,index]=ismember(x,gray_map);
     x=index-1;
end

% Obtain the total number of channels
[nRows, nChan] = size(x);

% Initialize the phase increments and the oscillator phase for modulator with 
% discontinuous phase.
phaseIncr = (0:nSamp-1)' * (-(M-1):2:(M-1)) * 2*pi * freq_sep/2 * samptime;
% phIncrSym is the incremental phase over one symbol, across all M tones.
phIncrSym = phaseIncr(end,:);
% phIncrSamp is the incremental phase over one sample, across all M tones.
phIncrSamp = phaseIncr(2,:);    % recall that phaseIncr(1,:) = 0
OscPhase = zeros(nChan, M);

% phase = nSamp*# of symbols x # of channels
Phase = zeros(nSamp*nRows, nChan);

% Special case for discontinuous-phase FSK: can use a table look-up for speed
if ( (~phase_cont) && ...
        ( floor(nSamp*freq_sep/2 * samptime) ==  nSamp*freq_sep/2 * samptime ) )
    exp_phaseIncr = exp(1i*phaseIncr);
    y = reshape(exp_phaseIncr(:,x+1),nRows*nSamp,nChan);
else
    for iChan = 1:nChan
        prevPhase = 0;
        for iSym = 1:nRows
            % Get the initial phase for the current symbol
            if (phase_cont)
                ph1 = prevPhase;
            else
                ph1 = OscPhase(iChan, x(iSym,iChan)+1);
            end

            % Compute the phase of the current symbol by summing the initial phase
            % with the per-symbol phase trajectory associated with the given M-ary
            % data element.
            Phase(nSamp*(iSym-1)+1:nSamp*iSym,iChan) = ...
                ph1*ones(nSamp,1) + phaseIncr(:,x(iSym,iChan)+1);

            % Update the oscillator for a modulator with discontinuous phase.
            % Calculate the phase modulo 2*pi so that the phase doesn't grow too
            % large.
            if (~phase_cont)
                OscPhase(iChan,:) = ...
                    rem(OscPhase(iChan,:) + phIncrSym + phIncrSamp, 2*pi);
            end

            % If in continuous mode, the starting phase for the next symbol is the
            % ending phase of the current symbol plus the phase increment over one
            % sample.
            prevPhase = Phase(nSamp*iSym,iChan) + phIncrSamp(x(iSym,iChan)+1);
        end
    end
    y = exp(1i*Phase);
end

% Restore the output signal to the original orientation
if(wid == 1)
    y = y.';
end

% EOF --- fskmod.m


