%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% All rights reserved by Krishna Pillai, http://www.dsplog.com
% The file may not be re-distributed without explicit authorization
% from Krishna Pillai.
% Checked for proper operation with Octave Version 3.0.0
% Author        : Krishna Pillai
% Email         : krishna@dsplog.com
% Version       : 1.0
% Date          : 26th July 2009
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Script for computing BER with Binary Convolutional Code 
% and Viterbi decoding with finite survivor state memory and
% variable traceback depth 
% Convolutional code of Rate-1/2, Generator polynomial - [7,5] octal 
% Hard decision decoding is used. 

clear
N = 10^4 ;% number of bits or symbols
tic
Eb_N0_dB = 4; % [0:1:10]; % multiple Eb/N0 values
Ec_N0_dB = Eb_N0_dB - 10*log10(2);

refHard = [0 0 ; 0 1; 1 0  ; 1 1 ];
refSoft = -1*[-1 -1; -1 1 ;1 -1; 1 1 ];

ipLUT = [ 0   0   0   0;...
	  0   0   0   0;...
          1   1   0   0;...
	  0   0   1   1 ];
TB_v = [1:2:21] ;
decodeDel = 1;

for yy = 1:length(TB_v)
  TB = TB_v(yy);

   % Transmitter
   ip = rand(1,N)>0.5; % generating 0,1 with equal probability

   % convolutional coding, rate - 1/2, generator polynomial - [7,5] octal
   cip1 = mod(conv(ip,[1 1 1 ]),2);
   cip2 = mod(conv(ip,[1 0 1 ]),2);
   cip = [cip1;cip2];
   cip = cip(:).';

   s = 2*cip-1; % BPSK modulation 0 -> -1; 1 -> 0 

   n = 1/sqrt(2)*[randn(size(cip)) + j*randn(size(cip))]; % white gaussian noise, 0dB variance 

   % Noise addition
   y = s + 10^(-Ec_N0_dB/20)*n; % additive white gaussian noise

   % receiver 
   cipHard = real(y)>0; % hard decision

   % Viterbi decoding
   pmHard  = zeros(4,1);  % hard path metric
   svHard_v  = zeros(4,length(y)/2); % hard survivor path
   
   count=1; 
   ipHatHard_v = zeros(1,length(y)/2);
   svHard_v    = zeros(4,length(y)/2); 
   for ii = 1:length(y)/2
      rHard = cipHard(2*ii-1:2*ii); % taking 2 hard bits
      
      % computing the Hamming distance and euclidean distance
      rHardv = kron(ones(4,1),rHard);
      hammingDist = sum(xor(rHardv,refHard),2);
 
      if (ii == 1) || (ii == 2) 

         % branch metric and path metric for state 0
         bm1Hard = pmHard(1,1) + hammingDist(1);
         pmHard_n(1,1)  = bm1Hard; 
         svHard(1,1)  = 1; 
 
         % branch metric and path metric for state 1
         bm1Hard = pmHard(3,1) + hammingDist(3);
         pmHard_n(2,1) = bm1Hard;
         svHard(2,1)  = 3; 
         

         % branch metric and path metric for state 2
         bm1Hard = pmHard(1,1) + hammingDist(4);
         pmHard_n(3,1) = bm1Hard;
         svHard(3,1)  = 1; 

         % branch metric and path metric for state 3
         bm1Hard = pmHard(3,1) + hammingDist(2);
         pmHard_n(4,1) = bm1Hard;
         svHard(4,1)  = 3; 

      else
         % branch metric and path metric for state 0
         bm1Hard = pmHard(1,1) + hammingDist(1);
         bm2Hard = pmHard(2,1) + hammingDist(4);
         [pmHard_n(1,1) idx] = min([bm1Hard,bm2Hard]);
         svHard(1,1)  = idx; 
 
         % branch metric and path metric for state 1
         bm1Hard = pmHard(3,1) + hammingDist(3);
         bm2Hard = pmHard(4,1) + hammingDist(2);
         [pmHard_n(2,1) idx] = min([bm1Hard,bm2Hard]);
         svHard(2,1)  = idx+2; 

         % branch metric and path metric for state 2
         bm1Hard = pmHard(1,1) + hammingDist(4);
         bm2Hard = pmHard(2,1) + hammingDist(1);
         [pmHard_n(3,1) idx] = min([bm1Hard,bm2Hard]);
         svHard(3,1)  = idx; 

         % branch metric and path metric for state 3
         bm1Hard = pmHard(3,1) + hammingDist(2);
         bm2Hard = pmHard(4,1) + hammingDist(3);
         [pmHard_n(4,1) idx] = min([bm1Hard,bm2Hard]);
         svHard(4,1)  = idx+2; 

      end

   pmHard = pmHard_n; 
   svHard_v(:,ii) = svHard;
   
   % trace back unit
   if ( (mod(ii,count*decodeDel+TB) == 0) || (ii==(length(y)/2)) )
   
      if (ii == length(y)/2)
         currHardState0 = 1;
         currHardState_min = 1;
	 stopTB  = startTB+1;
         startTB = length(y)/2;
         stopDecode  = startDecode+1;
         startDecode = startTB;
      else
         [ dum1 currHardState_min ] = min(pmHard);
         currHardState0 = 1;
         startTB = count*decodeDel+TB;
         stopTB  = startTB-TB+1;
         startDecode = stopTB-1;
         stopDecode  = startDecode - decodeDel+1;
      end

    if (ii ~= (length(y)/2))
       for jj = startTB:-1:stopTB
           prevHardState0   = svHard_v(currHardState0,jj); 
           currHardState0   = prevHardState0;

           prevHardState_min   = svHard_v(currHardState_min,jj); 
           currHardState_min   = prevHardState_min;

        end
    end
     
     for tt = startDecode:-1:stopDecode
        prevHardState0   = svHard_v(currHardState0,tt); 
        ipHatHard_v0(tt) = ipLUT(currHardState0,prevHardState0);
        currHardState0   = prevHardState0;

        prevHardState_min   = svHard_v(currHardState_min,tt); 
        ipHatHard_v_min(tt) = ipLUT(currHardState_min,prevHardState_min);
        currHardState_min   = prevHardState_min;
     end

     count = count+1;
    end
  
  end
   
   % counting the errors
   nErrHardViterbi0(yy) = size(find([ip - ipHatHard_v0(1:N)]),2);
   nErrHardViterbi_min(yy) = size(find([ip - ipHatHard_v_min(1:N)]),2);

end

simBer_HardViterbi0 = nErrHardViterbi0/N;
simBer_HardViterbi_min = nErrHardViterbi_min/N;

close all
figure
semilogy(TB_v,simBer_HardViterbi0,'sb-','LineWidth',2);
hold on
semilogy(TB_v,simBer_HardViterbi_min,'mp-','LineWidth',2);
grid on
axis([0 25 10^-3 10^0])
xlabel('traceback depth, TB')
ylabel('Bit Error Rate');
title('BER with Viterbi decoding vs traceback at Eb/N0=4dB')
legend('start = state_{00}', 'start = min_{path metric}')
toc







