% test_qps3k.m
% David Rowe March 2014
%
% QPSK modem simulation, version 2.  Simplifed version of
% test_qpsk. Initially based on code by Bill Cowley Generates curves
% BER versus E/No curves for different modems.  Design to test
% coherent demodulation ideas on HF channels without building a full
% blown modem.  Uses 'genie provided' estimates for timing estimation,
% frame sync.
%
% Compared to test_qsk2.m this version supports phase estimation
% (coherent demod)

1;

% main test function 

function sim_out = ber_test(sim_in, modulation)
    Fs = 8000;

    newldpc          = sim_in.newldpc;
    verbose          = sim_in.verbose;
    framesize        = sim_in.framesize;
    Ntrials          = sim_in.Ntrials;
    Esvec            = sim_in.Esvec;
    phase_offset     = sim_in.phase_offset;
    phase_est        = sim_in.phase_est;
    w_offset         = sim_in.w_offset;
    plot_scatter     = sim_in.plot_scatter;
    Rs               = sim_in.Rs;
    hf_sim           = sim_in.hf_sim;
    nhfdelay         = sim_in.hf_delay_ms*Rs/1000;
    hf_phase_only    = sim_in.hf_phase_only;
    hf_mag_only      = sim_in.hf_mag_only;
    Nc               = sim_in.Nc;
    sim_coh_dpsk     = sim_in.sim_coh_dpsk;

    bps              = 2;
    Nsymb            = framesize/bps;
    for k=1:Nc
      prev_sym_tx(k) = qpsk_mod([0 0]);
      prev_sym_rx(k) = qpsk_mod([0 0]);
    end

    phase_est_method = sim_in.phase_est_method;
    if phase_est_method == 2
      Np             = sim_in.Np;
      Ns             = sim_in.Ns;
      if Np/2 == floor(Np/2)
        printf("Np must be odd\n");
        return;
      end
      Nps            = (Np-1)*Ns+1; 
    else
      Nps            = sim_in.Np; 
    end
    r_delay_line     = zeros(Nc, Nps);
    s_delay_line     = zeros(Nc, Nps);
    ph_est_log       = [];

    phase_noise_amp  = sim_in.phase_noise_amp;

    ldpc_code = sim_in.ldpc_code;

    tx_bits_buf = zeros(1,2*framesize);
    rx_bits_buf = zeros(1,2*framesize);
    rx_symb_buf = zeros(1,2*Nsymb);
    hf_fading_buf = zeros(1,2*Nsymb);

    % Init LDPC --------------------------------------------------------------------

    if ldpc_code
        % Start CML library

        currentdir = pwd;
        addpath '/home/david/tmp/cml/mat'    % assume the source files stored here
        cd /home/david/tmp/cml
        CmlStartup                           % note that this is not in the cml path!
        cd(currentdir)
  
        % Our LDPC library

        ldpc;

        rate = sim_in.ldpc_code_rate; 
        mod_order = 4; 
        modulation2 = 'QPSK';
        mapping = 'gray';

        demod_type = 0;
        decoder_type = 0;
        max_iterations = 100;

        code_param = ldpc_init(rate, framesize, modulation2, mod_order, mapping);
        code_param.code_bits_per_frame = framesize;
        code_param.symbols_per_frame = framesize/bps;
    else
        rate = 1;
    end

    % Init HF channel model from stored sample files of spreading signal ----------------------------------

    % convert "spreading" samples from 1kHz carrier at Fs to complex
    % baseband, generated by passing a 1kHz sine wave through PathSim
    % with the ccir-poor model, enabling one path at a time.
    
    Fc = 1000; M = Fs/Rs;
    fspread = fopen("../raw/sine1k_2Hz_spread.raw","rb");
    spread1k = fread(fspread, "int16")/10000;
    fclose(fspread);
    fspread = fopen("../raw/sine1k_2ms_delay_2Hz_spread.raw","rb");
    spread1k_2ms = fread(fspread, "int16")/10000;
    fclose(fspread);

    % down convert to complex baseband
    spreadbb = spread1k.*exp(-j*(2*pi*Fc/Fs)*(1:length(spread1k))');
    spreadbb_2ms = spread1k_2ms.*exp(-j*(2*pi*Fc/Fs)*(1:length(spread1k_2ms))');

    % remove -2000 Hz image
    b = fir1(50, 5/Fs);
    spread = filter(b,1,spreadbb);
    spread_2ms = filter(b,1,spreadbb_2ms);

    % discard first 1000 samples as these were near 0, probably as
    % PathSim states were ramping up

    spread    = spread(1000:length(spread));
    spread_2ms = spread_2ms(1000:length(spread_2ms));

    % decimate down to Rs

    spread = spread(1:M:length(spread));
    spread_2ms = spread_2ms(1:M:length(spread_2ms));

    % Determine "gain" of HF channel model, so we can normalise
    % carrier power during HF channel sim to calibrate SNR.  I imagine
    % different implementations of ccir-poor would do this in
    % different ways, leading to different BER results.  Oh Well!

    hf_gain = 1.0/sqrt(var(spread)+var(spread_2ms));

    % Start Simulation ----------------------------------------------------------------

    for ne = 1:length(Esvec)
        EsNodB = Esvec(ne);
        EsNo = 10^(EsNodB/10);
    
        variance = 1/EsNo;
         if verbose > 1
            printf("EsNo (dB): %f EsNo: %f variance: %f\n", EsNodB, EsNo, variance);
        end
        
        Terrs = 0;  Tbits = 0;  Terrsldpc = 0;  Tbitsldpc = 0; Ferrsldpc = 0;

        tx_symb_log      = [];
        rx_symb_log      = [];
        noise_log        = [];
        mod_strip_log    = [];
        
        % init HF channel

        hf_n = 1;
        hf_angle_log = [];
        hf_fading = ones(1,Nsymb);             % default input for ldpc dec
        hf_model = ones(Ntrials*Nsymb/Nc, Nc); % defaults for plotting surface

        sim_out.errors_log = [];
        sim_out.ldpc_errors_log = [];

        for nn = 1: Ntrials
                  
            tx_bits = round( rand( 1, framesize*rate ) );
 
            % modulate --------------------------------------------

            if ldpc_code
                [tx_bits, s] = ldpc_enc(tx_bits, code_param);
            end
            s = zeros(1, Nsymb);
            for i=1:Nc:Nsymb
              for k=1:Nc
                tx_symb = qpsk_mod(tx_bits(2*(i-1+k-1)+1:2*(i+k-1)));
                if strcmp(modulation,'dqpsk')
                    tx_symb *= prev_sym_tx(k);
                    prev_sym_tx(k) = tx_symb;
                end 
                s(i+k-1) = tx_symb;
              end
            end
            tx_bits_buf(1:framesize) = tx_bits_buf(framesize+1:2*framesize);
            tx_bits_buf(framesize+1:2*framesize) = tx_bits;
            s_ch = s;

            % HF channel simulation  ------------------------------------
            
            if hf_sim

                % separation between carriers.  Note this is
                % effectively under samples at Rs, I dont think this
                % matters.  Equivalent to doing freq shift at Fs, then
                % decimating to Rs.

                wsep = 2*pi*(1+0.5);  % e.g. 75Hz spacing at Rs=50Hz, alpha=0.5 filters

                if Nsymb/Nc != floor(Nsymb/Nc)
                    printf("Error: Nsymb/Nc must be an integrer\n")
                    return;
                end

                % arrange symbols in Nsymb/Nc by Nc matrix

                for i=1:Nc:Nsymb

                    % Determine HF channel at each carrier for this symbol

                    for k=1:Nc
                        hf_model(hf_n, k) = hf_gain*(spread(hf_n) + exp(-j*k*wsep*nhfdelay)*spread_2ms(hf_n));
                        hf_fading(i+k-1) = abs(hf_model(hf_n, k));
                        if hf_mag_only
                             s_ch(i+k-1) *= abs(hf_model(hf_n, k));
                        else
                             s_ch(i+k-1) *= hf_model(hf_n, k);
                        end
                    end
                    hf_n++;
                end
            end
            
            tx_symb_log = [tx_symb_log s_ch];

            % AWGN noise and phase/freq offset channel simulation
            % 0.5 factor ensures var(noise) == variance , i.e. splits power between Re & Im

            noise = sqrt(variance*0.5)*(randn(1,Nsymb) + j*randn(1,Nsymb));
            noise_log = [noise_log noise];
            phase_noise = phase_noise_amp*(2.0*rand(1,Nsymb)-1.0);
 
            % organise into carriers to apply frequency and phase offset

            for i=1:Nc:Nsymb
              for k=1:Nc
                 s_ch(i+k-1) = s_ch(i+k-1)*exp(j*(phase_offset+phase_noise(i+k-1))) + noise(i+k-1);
              end 
              phase_offset += w_offset;
            end
               
            % phase estimation
            
            ph_est = zeros(Nc,1);

            if phase_est

                % organise into carriers

                for i=1:Nc:Nsymb

                  for k=1:Nc
                      centre = floor(Nps/2)+1;

                      % delay line for phase est window

                      r_delay_line(k,1:Nps-1) = r_delay_line(k,2:Nps);
                      r_delay_line(k,Nps) = s_ch(i+k-1);

                      % delay in tx data to compensate data for phase est window

                      s_delay_line(k,1:Nps-1) = s_delay_line(k,2:Nps);
                      s_delay_line(k,Nps) = s(i+k-1);
 
                      if phase_est_method == 1
                        % QPSK modulation strip and phase est

                        mod_strip_pol  = angle(r_delay_line(k,:)) * 4;
                        mod_strip_rect = exp(j*mod_strip_pol);

                        ph_est_pol = atan2(sum(imag(mod_strip_rect)),sum(real(mod_strip_rect)))/4;
                        ph_est(k)  = exp(j*ph_est_pol);

                        s_ch(i+k-1) = r_delay_line(k,centre).*exp(-j*ph_est_pol);
                        % s_ch(i+k-1) = r_delay_line(k,centre);
                     end

                     if phase_est_method == 3
                        % QPSK modulation strip and phase est with original symbol mags

                        mod_strip_pol  = angle(r_delay_line(k,:)) * 4;
                        mod_strip_rect = abs(r_delay_line(k,:)) .* exp(j*mod_strip_pol);

                        ph_est_pol = atan2(sum(imag(mod_strip_rect)),sum(real(mod_strip_rect)))/4;
                        ph_est(k)  = exp(j*ph_est_pol);

                        s_ch(i+k-1) = r_delay_line(k,centre).*exp(-j*ph_est_pol);
                        % s_ch(i+k-1) = r_delay_line(k,centre);
                     end

                     if phase_est_method == 2

                        % estimate phase from surrounding known pilot symbols and correct

                        corr = 0;
                        for m=1:Ns:Nps
                          if (m != centre)
                            corr += s_delay_line(k,m) * r_delay_line(k,m)';
                          end
                        end
                         ph_est(k)  = conj(corr/(1E-6+abs(corr)));
                         s_ch(i+k-1) = r_delay_line(k,centre).*exp(j*angle(corr));
                         %s_ch(i+k-1) = r_delay_line(k,centre);
                     end

                 end
                  
                  ph_est_log = [ph_est_log ph_est];
               end    
               %printf("corr: %f angle: %f\n", corr, angle(corr));
            end

            % de-modulate

            rx_bits = zeros(1, framesize);
            for i=1:Nc:Nsymb
              for k=1:Nc
                rx_symb = s_ch(i+k-1);
                if strcmp(modulation,'dqpsk')
                    tmp = rx_symb;
                    rx_symb *= conj(prev_sym_rx(k)/abs(prev_sym_rx(k)));
                    if sim_coh_dpsk
                      prev_sym_rx(k) = qpsk_mod(qpsk_demod(tmp));
                    else
                      prev_sym_rx(k) = tmp;
                    end
                    s_ch(i+k-1) = rx_symb;
                end
                rx_bits((2*(i-1+k-1)+1):(2*(i+k-1))) = qpsk_demod(rx_symb);
                rx_symb_log = [rx_symb_log rx_symb];
              end
            end

if newldpc
            rx_bits_buf(1:framesize) = rx_bits_buf(framesize+1:2*framesize);
            rx_bits_buf(framesize+1:2*framesize) = rx_bits;
            rx_symb_buf(1:Nsymb) = rx_symb_buf(Nsymb+1:2*Nsymb);
            rx_symb_buf(Nsymb+1:2*Nsymb) = s_ch;
            hf_fading_buf(1:Nsymb) = hf_fading_buf(Nsymb+1:2*Nsymb);
            hf_fading_buf(Nsymb+1:2*Nsymb) = hf_fading;

            % determine location of start and end of frame depending on processing delays

            if phase_est
              st_rx_bits = 1+(floor(Nps/2)+1-1)*Nc*2;
              st_rx_symb = 1+(floor(Nps/2)+1-1)*Nc;
            else
              st_rx_bits = 1;
              st_rx_symb = 1;
            end
            en_rx_bits = st_rx_bits+framesize-1;
            en_rx_symb = st_rx_symb+Nsymb-1;

            if nn > 1
              % Measure BER

              %printf("nn: %d centre: %d\n", nn, floor(Nps/2)+1);
              %tx_bits_buf(1:20)
              %rx_bits_buf(st_rx_bits:st_rx_bits+20-1)
              error_positions = xor(rx_bits_buf(st_rx_bits:en_rx_bits), tx_bits_buf(1:framesize));
              Nerrs = sum(error_positions);
              sim_out.errors_log = [sim_out.errors_log error_positions];
              Terrs += Nerrs;
              Tbits += length(tx_bits);

              % Optionally LDPC decode
            
              if ldpc_code
                detected_data = ldpc_dec(code_param, max_iterations, demod_type, decoder_type, ...
                                         rx_symb_buf(st_rx_symb:en_rx_symb), min(100,EsNo), hf_fading_buf(1:Nsymb));
                error_positions = xor( detected_data(1:framesize*rate), tx_bits_buf(1:framesize*rate) );
                %detected_data = ldpc_dec(code_param, max_iterations, demod_type, decoder_type, s_ch, min(100,EsNo), hf_fading);
                %error_positions = xor( detected_data(1:framesize*rate), tx_bits(1:framesize*rate) );
                Nerrs = sum(error_positions);
                sim_out.ldpc_errors_log = [sim_out.ldpc_errors_log error_positions];
                if Nerrs
                    Ferrsldpc++;
                end
                Terrsldpc += Nerrs;
                Tbitsldpc += framesize*rate;
              end
            end

else    
            error_positions = xor(rx_bits, tx_bits);
            Nerrs = sum(error_positions);
            Terrs += Nerrs;
            Tbits += length(tx_bits);

            % Optionally LDPC decode
            
            if ldpc_code
                detected_data = ldpc_dec(code_param, max_iterations, demod_type, decoder_type, s_ch, min(100,EsNo), hf_fading);
                error_positions = xor( detected_data(1:framesize*rate), tx_bits(1:framesize*rate) );
                Nerrs = sum(error_positions);
                if Nerrs
                    Ferrsldpc++;
                end
                Terrsldpc += Nerrs;
                Tbitsldpc += framesize*rate;

            end
        end
end

        TERvec(ne) = Terrs;
        BERvec(ne) = Terrs/Tbits;
        if ldpc_code
            TERldpcvec(ne) = Terrsldpc;
            FERldpcvec(ne) = Ferrsldpc;
            BERldpcvec(ne) = Terrsldpc/Tbitsldpc;
        end

        if verbose 
            printf("EsNo (dB): %f  Terrs: %d BER %f BER theory %f", EsNodB, Terrs,
                   Terrs/Tbits, 0.5*erfc(sqrt(EsNo/2)));
            if ldpc_code
                printf(" LDPC: Terrs: %d BER: %f Ferrs: %d FER: %f", 
                       Terrsldpc, Terrsldpc/Tbitsldpc, Ferrsldpc, Ferrsldpc/(Ntrials-1));
            end
            printf("\n");
        end
        if verbose > 1
            printf("Terrs: %d BER %f BER theory %f C %f N %f Es %f No %f Es/No %f\n\n", Terrs,
                   Terrs/Tbits, 0.5*erfc(sqrt(EsNo/2)), var(tx_symb_log), var(noise_log),
                   var(tx_symb_log), var(noise_log), var(tx_symb_log)/var(noise_log));
        end
    end
    
    Ebvec = Esvec - 10*log10(bps);

    % account for extra power rqd for pilot symbols

    if (phase_est_method == 2) && (phase_est)
      Ebvec += 10*log10(Ns/(Ns-1));
    end

    sim_out.BERvec          = BERvec;
    sim_out.Ebvec           = Ebvec;
    sim_out.TERvec          = TERvec;
    if ldpc_code
        sim_out.BERldpcvec  = BERldpcvec;
        sim_out.TERldpcvec  = TERldpcvec;
        sim_out.FERldpcvec  = FERldpcvec;
    end

    if plot_scatter
        figure(2);
        clf;
        scat = rx_symb_log .* exp(j*pi/4);
        plot(real(scat(Nps*Nc:length(scat))), imag(scat(Nps*Nc:length(scat))),'+');
        title('Scatter plot');

        figure(3);
        clf;
        
        y = 1:Rs*2;
        x = 1:Nc;
        EsNodBSurface = 20*log10(abs(hf_model(y,:))) - 10*log10(variance);
        mesh(x,y,EsNodBSurface);
        grid
        %axis([1 Nc 1 Rs*2 -10 10])
        title('HF Channel Es/No');

        figure(4);
        clf;
        %mesh(x,y,unwrap(angle(hf_model(y,:))));
        subplot(211)
        plot(y,abs(hf_model(y,1)))
        title('HF Channel Carrier 1 Mag');
        subplot(212)
        plot(y,angle(hf_model(y,1)))
        title('HF Channel Carrier 1 Phase');

        if phase_est
          scat = ph_est_log(1,floor(Nps/2):Rs*2+floor(Nps/2)-1);
          hold on;
          plot(angle(scat),'r');
          hold off;

          figure(5)
          clf;
          scat = ph_est_log(1,y);
          plot(real(scat), imag(scat),'+');
          title('Carrier 1 Phase Est');
          axis([-1 1 -1 1])
        end
if 0        
        figure(5);
        clf;
        subplot(211)
        plot(real(spread));
        hold on;
        plot(imag(spread),'g');     
        hold off;   
        subplot(212)
        plot(real(spread_2ms));
        hold on;
        plot(imag(spread_2ms),'g');     
        hold off;   

        figure(6)
        tmp = [];
        for i = 1:hf_n-1
            tmp = [tmp abs(hf_model(i,:))];
        end
        hist(tmp);
end
     end

size(sim_out.errors_log)

endfunction

% Gray coded QPSK modulation function

function symbol = qpsk_mod(two_bits)
    two_bits_decimal = sum(two_bits .* [2 1]); 
    switch(two_bits_decimal)
        case (0) symbol =  1;
        case (1) symbol =  j;
        case (2) symbol = -j;
        case (3) symbol = -1;
    endswitch
endfunction

% Gray coded QPSK demodulation function

function two_bits = qpsk_demod(symbol)
    if isscalar(symbol) == 0
        printf("only works with scalars\n");
        return;
    end
    bit0 = real(symbol*exp(j*pi/4)) < 0;
    bit1 = imag(symbol*exp(j*pi/4)) < 0;
    two_bits = [bit1 bit0];
endfunction

function sim_in = standard_init
  sim_in.verbose          = 1;
  sim_in.plot_scatter     = 0;

  sim_in.Esvec            = 5; 
  sim_in.Ntrials          = 30;
  sim_in.framesize        = 576;
  sim_in.Rs               = 100;
  sim_in.Nc               = 8;

  sim_in.phase_offset     = 0;
  sim_in.w_offset         = 0;
  sim_in.phase_noise_amp  = 0;

  sim_in.hf_delay_ms      = 2;
  sim_in.hf_sim           = 0;
  sim_in.hf_phase_only    = 0;
  sim_in.hf_mag_only      = 1;

  sim_in.phase_est        = 0;
  sim_in.phase_est_method = 1;
  sim_in.Np               = 5;
  sim_in.Ns               = 5;

  sim_in.ldpc_code_rate   = 1/2;
  sim_in.ldpc_code        = 1;
endfunction

function ideal

  sim_in = standard_init();

  sim_in.sim_coh_dpsk     = 0;
  sim_in.newldpc          = 1;
  sim_in.verbose          = 2;
  sim_in.plot_scatter     = 1;

  sim_in.Esvec            = 5; 
  sim_in.hf_sim           = 1;
  sim_in.Ntrials          = 30;

  sim_qpsk_hf             = ber_test(sim_in, 'qpsk');

  sim_in.hf_sim           = 0;
  sim_in.plot_scatter     = 0;
  sim_in.Esvec            = 2:15; 
  sim_in.ldpc_code        = 0;
  Ebvec = sim_in.Esvec - 10*log10(2);
  BER_theory = 0.5*erfc(sqrt(10.^(Ebvec/10)));
  sim_qpsk                = ber_test(sim_in, 'qpsk');
  sim_dqpsk               = ber_test(sim_in, 'dqpsk');

  sim_in.hf_sim           = 1;
  sim_in.Esvec            = 2:15; 
  sim_qpsk_hf             = ber_test(sim_in, 'qpsk');
  sim_dqpsk_hf            = ber_test(sim_in, 'dqpsk');
  sim_in.ldpc_code        = 1;
  sim_in.ldpc_code_rate   = 3/4;
  sim_qpsk_hf_ldpc1       = ber_test(sim_in, 'qpsk');
  sim_in.ldpc_code_rate   = 1/2;
  sim_qpsk_hf_ldpc2       = ber_test(sim_in, 'qpsk');
  sim_in.ldpc_code_rate   = 3/4;
  sim_in.hf_sim           = 0;
  sim_qpsk_awgn_ldpc      = ber_test(sim_in, 'qpsk');

  figure(1); 
  clf;
  semilogy(Ebvec, BER_theory,'r;QPSK theory;')
  hold on;
  semilogy(sim_qpsk.Ebvec, sim_qpsk.BERvec,'g;QPSK AWGN;')
  semilogy(sim_qpsk_hf.Ebvec, sim_qpsk_hf.BERvec,'r;QPSK HF;')
  semilogy(sim_dqpsk.Ebvec, sim_dqpsk.BERvec,'c;DQPSK AWGN;')
  semilogy(sim_dqpsk_hf.Ebvec, sim_dqpsk_hf.BERvec,'m;DQPSK HF;')
  semilogy(sim_qpsk_hf_ldpc1.Ebvec, sim_qpsk_hf_ldpc1.BERldpcvec,'k;QPSK HF LDPC 3/4;')
  semilogy(sim_qpsk_hf_ldpc2.Ebvec, sim_qpsk_hf_ldpc2.BERldpcvec,'b;QPSK HF LDPC 1/2;')
  semilogy(sim_qpsk_awgn_ldpc.Ebvec, sim_qpsk_awgn_ldpc.BERldpcvec,'k;QPSK AWGN LDPC 3/4;')

  hold off;
  xlabel('Eb/N0')
  ylabel('BER')
  grid("minor")
  axis([min(Ebvec) max(Ebvec) 1E-3 1])
endfunction

function phase_noise
  sim_in = standard_init();

  sim_in.verbose          = 1;
  sim_in.plot_scatter     = 1;

  sim_in.Esvec            = 100; 
  sim_in.Ntrials          = 100;

  sim_in.ldpc_code_rate   = 1/2;
  sim_in.ldpc_code        = 1;

  sim_in.phase_noise_amp  = pi/16;
  tmp                     = ber_test(sim_in, 'qpsk');

  sim_in.plot_scatter     = 0;
  sim_in.Esvec            = 2:8; 
  sim_qpsk_hf             = ber_test(sim_in, 'qpsk');

  Ebvec = sim_in.Esvec - 10*log10(2);
  BER_theory = 0.5*erfc(sqrt(10.^(Ebvec/10)));

  sim_in.phase_noise_amp = 0;
  sim_qpsk               = ber_test(sim_in, 'qpsk');
  sim_in.phase_noise_amp = pi/8;
  sim_qpsk_pn8           = ber_test(sim_in, 'qpsk');
  sim_in.phase_noise_amp = pi/16;
  sim_qpsk_pn16          = ber_test(sim_in, 'qpsk');
  sim_in.phase_noise_amp = pi/32;
  sim_qpsk_pn32          = ber_test(sim_in, 'qpsk');

  figure(1); 
  clf;
  semilogy(sim_qpsk.Ebvec, sim_qpsk.BERvec,'g;QPSK phase noise 0;')
  hold on;
  semilogy(sim_qpsk_pn8.Ebvec, sim_qpsk_pn8.BERvec,'c;QPSK phase noise +/- pi/8;')
  semilogy(sim_qpsk_pn16.Ebvec, sim_qpsk_pn16.BERvec,'b;QPSK phase noise +/- pi/16;')
  semilogy(sim_qpsk_pn32.Ebvec, sim_qpsk_pn32.BERvec,'k;QPSK phase noise +/- pi/32;')

  semilogy(sim_qpsk.Ebvec, sim_qpsk.BERldpcvec,'g;QPSK phase noise 0 ldpc;')
  semilogy(sim_qpsk_pn8.Ebvec, sim_qpsk_pn8.BERldpcvec,'c;QPSK phase noise +/- pi/8 ldpc;')
  semilogy(sim_qpsk_pn16.Ebvec, sim_qpsk_pn16.BERldpcvec,'b;QPSK phase noise +/- pi/16 ldpc;')
  semilogy(sim_qpsk_pn32.Ebvec, sim_qpsk_pn32.BERldpcvec,'k;QPSK phase noise +/- pi/32 ldpc;')

  hold off;
  xlabel('Eb/N0')
  ylabel('BER')
  grid("minor")
  axis([min(Ebvec) max(Ebvec) 1E-2 1])
endfunction

function phase_est_hf
  sim_in = standard_init();

  sim_in.Rs               = 100;
  sim_in.Nc               = 8;

  sim_in.verbose          = 1;
  sim_in.plot_scatter     = 0;

  sim_in.Esvec            = 5:15; 
  sim_in.Ntrials          = 100;

  sim_in.newldpc          = 1;
  sim_in.ldpc_code_rate   = 1/2;
  sim_in.ldpc_code        = 1;

  sim_in.phase_est        = 0;
  sim_in.sim_coh_dpsk     = 0;
  sim_in.phase_est_method = 2;
  sim_in.Np               = 3;
  sim_in.phase_offset     = 0;
  sim_in.w_offset         = 0;

  sim_in.hf_sim           = 1;
  sim_in.hf_mag_only      = 1;

  Ebvec = sim_in.Esvec - 10*log10(2);

  baseline                = ber_test(sim_in, 'qpsk');

  sim_in.hf_mag_only      = 0;
  sim_in.phase_est_method = 2;
  sim_in.phase_est        = 1;
  sim_in.Np               = 3;
  pilot_3                 = ber_test(sim_in, 'qpsk');
  sim_in.Np               = 5;
  pilot_5                 = ber_test(sim_in, 'qpsk');
  sim_in.Np               = 7;
  pilot_7                 = ber_test(sim_in, 'qpsk');

if 1
  sim_in.phase_est        = 0;
  dqpsk                   = ber_test(sim_in, 'dqpsk');

  figure(1); 
  clf;
  semilogy(baseline.Ebvec, baseline.BERvec,'r;QPSK CCIR poor;')
  hold on;
  semilogy(baseline.Ebvec, baseline.BERldpcvec,'r;QPSK CCIR poor ldpc;')
  semilogy(pilot_3.Ebvec, pilot_3.BERvec,'b;QPSK CCIR poor ldpc pilot 3;')
  semilogy(pilot_3.Ebvec, pilot_3.BERldpcvec,'b;QPSK CCIR poor ldpc pilot 3;')
  semilogy(pilot_5.Ebvec, pilot_5.BERvec,'g;QPSK CCIR poor ldpc pilot 5;')
  semilogy(pilot_5.Ebvec, pilot_5.BERldpcvec,'g;QPSK CCIR poor ldpc pilot 5;')
  semilogy(pilot_7.Ebvec, pilot_7.BERvec,'m;QPSK CCIR poor ldpc pilot 7;')
  semilogy(pilot_7.Ebvec, pilot_7.BERldpcvec,'m;QPSK CCIR poor ldpc pilot 7;')
  semilogy(dqpsk.Ebvec, dqpsk.BERvec,'k;DQPSK CCIR poor ldpc;')
  semilogy(dqpsk.Ebvec, dqpsk.BERldpcvec,'k;DQPSK CCIR poor ldpc;')

  hold off;
  xlabel('Eb/N0')
  ylabel('BER')
  grid("minor")
  axis([min(Ebvec) max(Ebvec) 1E-2 1])
end
endfunction

function phase_est_awgn
  sim_in = standard_init();

  sim_in.Rs               = 100;
  sim_in.Nc               = 8;

  sim_in.verbose          = 1;
  sim_in.plot_scatter     = 0;

  sim_in.Esvec            = 0:0.5:3; 
  sim_in.Ntrials          = 30;

  sim_in.newldpc          = 1;
  sim_in.ldpc_code_rate   = 1/2;
  sim_in.ldpc_code        = 1;

  sim_in.phase_est        = 0;
  sim_in.phase_est_method = 1;
  sim_in.Np               = 3;
  sim_in.phase_offset     = 0;
  sim_in.w_offset         = 0;

  sim_in.hf_sim           = 0;
  sim_in.hf_mag_only      = 1;

  ideal                   = ber_test(sim_in, 'qpsk');

  sim_in.phase_est        = 1;
  sim_in.Np               = 21;
  sim_in.phase_est_method = 3;
  strip_21_mag            = ber_test(sim_in, 'qpsk');

  sim_in.Np               = 41;
  strip_41_mag            = ber_test(sim_in, 'qpsk');

  sim_in.phase_est_method = 1;
  sim_in.Np               = 21;
  strip_21                = ber_test(sim_in, 'qpsk');

  sim_in.Np               = 41;
  strip_41                = ber_test(sim_in, 'qpsk');

  sim_in.Np               = 7;
  sim_in.phase_est_method = 2;
  pilot_7                 = ber_test(sim_in, 'qpsk');

  Ebvec = sim_in.Esvec - 10*log10(2);

  figure(1); 
  clf;
  semilogy(ideal.Ebvec, ideal.BERvec,'r;QPSK;')
  hold on;
  semilogy(ideal.Ebvec, ideal.BERldpcvec,'r;QPSK LDPC;')
  semilogy(strip_21.Ebvec, strip_21.BERvec,'g;QPSK strip 21;')
  semilogy(strip_21.Ebvec, strip_21.BERldpcvec,'g;QPSK LDPC strip 21;')
  semilogy(strip_41.Ebvec, strip_41.BERvec,'b;QPSK strip 41;')
  semilogy(strip_41.Ebvec, strip_41.BERldpcvec,'b;QPSK LDPC strip 41;')
  semilogy(strip_21_mag.Ebvec, strip_21_mag.BERvec,'m;QPSK strip 21 mag;')
  semilogy(strip_21_mag.Ebvec, strip_21_mag.BERldpcvec,'m;QPSK LDPC strip 21 mag;')
  semilogy(strip_41_mag.Ebvec, strip_41_mag.BERvec,'c;QPSK strip 41 mag;')
  semilogy(strip_41_mag.Ebvec, strip_41_mag.BERldpcvec,'c;QPSK LDPC strip 41 mag;')
  semilogy(pilot_7.Ebvec, pilot_7.BERvec,'k;QPSK pilot 7;')
  semilogy(pilot_7.Ebvec, pilot_7.BERldpcvec,'k;QPSK LDPC pilot 7;')

  hold off;
  xlabel('Eb/N0')
  ylabel('BER')
  grid("minor")
  axis([min(Ebvec) max(Ebvec) 1E-2 1])
endfunction

function test_dpsk
  sim_in = standard_init();

  sim_in.Rs               = 100;
  sim_in.Nc               = 8;

  sim_in.verbose          = 1;
  sim_in.plot_scatter     = 0;

  sim_in.Esvec            = 5; 
  sim_in.Ntrials          = 30;

  sim_in.newldpc          = 1;
  sim_in.ldpc_code_rate   = 1/2;
  sim_in.ldpc_code        = 1;

  sim_in.phase_est        = 0;
  sim_in.phase_est_method = 3;
  sim_in.Np               = 41;
  sim_in.phase_offset     = 0;
  sim_in.w_offset         = 0;
  sim_in.sim_coh_dpsk     = 0;

  sim_in.hf_sim           = 0;
  sim_in.hf_mag_only      = 1;

  Ebvec = sim_in.Esvec - 10*log10(2);

  baseline                = ber_test(sim_in, 'qpsk');
  sim_in.phase_est        = 0;
  dqpsk                   = ber_test(sim_in, 'dqpsk');

  sim_in.phase_est        = 1;
  sim_in.phase_est_method = 3;
  sim_in.sim_coh_dpsk     = 1;
  sim_in.Np               = 41;
  dqpsk_strip_41          = ber_test(sim_in, 'dqpsk');
  
  figure(1); 
  clf;
  semilogy(baseline.Ebvec, baseline.BERvec,'r;QPSK CCIR poor;')
  hold on;
  semilogy(baseline.Ebvec, baseline.BERldpcvec,'r;QPSK CCIR poor ldpc;')
  semilogy(dqpsk.Ebvec, dqpsk.BERvec,'c;DQPSK CCIR poor ldpc;')
  semilogy(dqpsk.Ebvec, dqpsk.BERldpcvec,'c;DQPSK CCIR poor ldpc;')
  semilogy(dqpsk_strip_41.Ebvec, dqpsk_strip_41.BERvec,'m;DQPSK CCIR poor ldpc strip 41;')
  semilogy(dqpsk_strip_41.Ebvec, dqpsk_strip_41.BERldpcvec,'m;DQPSK CCIR poor ldpc strip 41;')

  hold off;
  xlabel('Eb/N0')
  ylabel('BER')
  grid("minor")
  axis([min(Ebvec) max(Ebvec) 1E-2 1])

endfunction

function gen_error_pattern_qpsk()
  sim_in = standard_init();

  % model codec and uncoded streams as 1000 bit/s each

  sim_in.Rs               = 100;
  sim_in.Nc               = 4;

  sim_in.verbose          = 1;
  sim_in.plot_scatter     = 0;

  sim_in.Esvec            = 10; % Eb/No=2dB
  sim_in.Ntrials          = 30;

  sim_in.newldpc          = 1;
  sim_in.ldpc_code_rate   = 1/2;
  sim_in.ldpc_code        = 1;

  sim_in.phase_est        = 1;
  sim_in.phase_est_method = 2;
  sim_in.Np               = 5;
  sim_in.phase_offset     = 0;
  sim_in.w_offset         = 0;
  sim_in.sim_coh_dpsk     = 0;

  sim_in.hf_sim           = 1;
  sim_in.hf_mag_only      = 0;

  qpsk                    = ber_test(sim_in, 'qpsk');
 
  length(qpsk.errors_log) 
  length(qpsk.ldpc_errors_log)
  % multiplex errors into prot and unprot halves of 52 bit codec frames

  error_pattern = [];
  for i=1:26:length(qpsk.ldpc_errors_log)-52
    error_pattern = [error_pattern qpsk.ldpc_errors_log(i:i+25) qpsk.errors_log(i:i+25)  zeros(1,4)];
    %error_pattern = [error_pattern qpsk.ldpc_errors_log(i:i+25) zeros(1,26)  zeros(1,4)];
    %error_pattern = [error_pattern zeros(1,26) qpsk.errors_log(i:i+25)  zeros(1,4)];
  end

  fep=fopen("qpsk_errors_2dB.bin","wb"); fwrite(fep, error_pattern, "short"); fclose(fep);

endfunction

function gen_error_pattern_dpsk()
  sim_in = standard_init();

  sim_in.Rs               = 50;
  sim_in.Nc               = 16;

  sim_in.verbose          = 1;
  sim_in.plot_scatter     = 1;

  sim_in.Esvec            = 10; % Eb/No=Es/No-3
  sim_in.Ntrials          = 30;

  sim_in.newldpc          = 1;
  sim_in.ldpc_code_rate   = 1/2;
  sim_in.ldpc_code        = 0;

  sim_in.phase_est        = 0;
  sim_in.phase_est_method = 3;
  sim_in.Np               = 41;
  sim_in.phase_offset     = 0;
  sim_in.w_offset         = 0;
  sim_in.sim_coh_dpsk     = 0;

  sim_in.hf_sim           = 1;
  sim_in.hf_mag_only      = 1;

  dqpsk                    = ber_test(sim_in, 'dqpsk');
 
  fep=fopen("dqpsk_errors_12dB.bin","wb"); fwrite(fep, dqpsk.errors_log, "short"); fclose(fep);

endfunction

% Start simulations ---------------------------------------

more off;

ideal();
%phase_est_hf();
%phase_est_awgn();
%test_dpsk();
%gen_error_pattern_qpsk
