added ldpc codec to QPSK test simulation
authordrowe67 <drowe67@01035d8c-6547-0410-b346-abe4f91aad63>
Sat, 15 Feb 2014 04:37:32 +0000 (04:37 +0000)
committerdrowe67 <drowe67@01035d8c-6547-0410-b346-abe4f91aad63>
Sat, 15 Feb 2014 04:37:32 +0000 (04:37 +0000)
git-svn-id: https://svn.code.sf.net/p/freetel/code@1403 01035d8c-6547-0410-b346-abe4f91aad63

codec2-dev/octave/test_qpsk.m

index 3f8d6fb2c66093450ce9bd6cf44e2ff8c501c245..a7dc6a6ace629f82413a638cc1879e922394bc7f 100644 (file)
@@ -4,10 +4,8 @@
 % QPSK modem simulation, initially based on code by Bill Cowley\r
 % Generates curves BER versus E/No curves for different modems.\r
 % Design to test coherent demodulation ideas on HF channels without\r
-% building a full blown modem.  Lacks timing estimation, frame sync.\r
-\r
-% TODO\r
-%   [ ] put spreading sample files sonewhere useful\r
+% building a full blown modem.  Uses 'genie provided' estimates for\r
+% timing estimation, frame sync.\r
 \r
 1;\r
 \r
@@ -16,6 +14,7 @@
 function sim_out = ber_test(sim_in, modulation)\r
     Fs = 8000;\r
 \r
+    verbose          = sim_in.verbose;\r
     framesize        = sim_in.framesize;\r
     Ntrials          = sim_in.Ntrials;\r
     Esvec            = sim_in.Esvec;\r
@@ -26,6 +25,8 @@ function sim_out = ber_test(sim_in, modulation)
     Rs               = sim_in.Rs;\r
     hf_sim           = sim_in.hf_sim;\r
     Nhfdelay         = floor(sim_in.hf_delay_ms*1000/Fs);\r
+    hf_phase_only    = sim_in.hf_phase_only;\r
+    hf_mag_only      = sim_in.hf_mag_only;\r
 \r
     bps              = 2;\r
     Nsymb            = framesize/bps;\r
@@ -33,15 +34,50 @@ function sim_out = ber_test(sim_in, modulation)
     prev_sym_rx      = qpsk_mod([0 0]);\r
     rx_symb_log      = [];\r
 \r
-    Np               = 5;\r
-    r_delay_line     = zeros(1,Np);\r
-    s_delay_line     = zeros(1,Np);\r
+    Np               = sim_in.Np;  % number of pilot symbols to use in phase est\r
+    Ns               = sim_in.Ns;  % spacing of pilot symbols, so (Nps-1) data symbols between every pilot\r
+    Nps              = Np*Ns;\r
+    r_delay_line     = zeros(1,Nps+1);\r
+    s_delay_line     = zeros(1,Nps+1);\r
     spread_main_phi  = 0;\r
     spread_delay_phi = 0;\r
     spread_main_phi_log = [];\r
 \r
-    % convert "spreading" samples from 1kHz carrier at Fs to complex baseband\r
+    ldpc_code = sim_in.ldpc_code;\r
+\r
+    if ldpc_code\r
+        % Start CML library\r
+\r
+        currentdir = pwd;\r
+        addpath '/home/david/tmp/cml/mat'    % assume the source files stored here\r
+        cd /home/david/tmp/cml\r
+        CmlStartup                           % note that this is not in the cml path!\r
+        cd(currentdir)\r
+  \r
+        % Our LDPC library\r
+\r
+        ldpc;\r
 \r
+        rate = 1/2; \r
+        mod_order = 4; \r
+        modulation = 'QPSK';\r
+        mapping = 'gray';\r
+\r
+        demod_type = 0;\r
+        decoder_type = 0;\r
+        max_iterations = 100;\r
+\r
+        code_param = ldpc_init(rate, framesize, modulation, mod_order, mapping);\r
+        code_param.code_bits_per_frame = framesize;\r
+        code_param.symbols_per_frame = framesize/bps;\r
+    else\r
+        rate = 1;\r
+    end\r
+\r
+    % convert "spreading" samples from 1kHz carrier at Fs to complex\r
+    % baseband, generated by passing a 1kHz sine wave through PathSim\r
+    % with the ccir-poor model, enabling one path at a time.\r
+    \r
     Fc = 1000;\r
     fspread = fopen("../raw/sine1k_2Hz_spread.raw","rb");\r
     spread1k = fread(fspread, "int16")/10000;\r
@@ -59,18 +95,25 @@ function sim_out = ber_test(sim_in, modulation)
     spread = filter(b,1,spreadbb);\r
     spread_2ms = filter(b,1,spreadbb_2ms);\r
 \r
-    % Determine "gain" of HF channel model, so we can normalise later\r
+    % discard first 1000 samples as these were near 0, probably as\r
+    % PathSim states were ramping up\r
 \r
-    hf_gain = 1.0/sqrt(var(spread)+var(spread_2ms));\r
+    spread    = spread(1000:length(spread));\r
+    spread_2ms = spread_2ms(1000:length(spread_2ms));\r
 \r
-    sc = 1;\r
+    % Determine "gain" of HF channel model, so we can normalise\r
+    % carrier power during HF channel sim to calibrate SNR.  I imagine\r
+    % different implementations of ccir-poor would do this in\r
+    % different ways, leading to different BER results.  Oh Well!\r
+\r
+    hf_gain = 1.0/sqrt(var(spread)+var(spread_2ms));\r
 \r
     % design root nyquist (root raised cosine) filter and init tx and rx filter states\r
 \r
     alpha = 0.5; T=1/Fs; Nfiltsym=7; M=Fs/Rs;\r
     if floor(Fs/Rs) != Fs/Rs\r
         printf("oversampling ratio must be an integer\n");\r
-        exit;\r
+        return;\r
     end\r
     hrn = gen_rn_coeffs(alpha, T, Rs, Nfiltsym, M);\r
     Nfilter = length(hrn);\r
@@ -97,37 +140,48 @@ function sim_out = ber_test(sim_in, modulation)
         %   No    = CFs/(Rs(Es/No))\r
 \r
         variance = Fs/(Rs*EsNo);\r
-        Terrs = 0;  Tbits = 0;  Ferrs = 0;\r
-        printf("EsNo (dB): %f EsNo: %f variance: %f\n", Es, EsNo, variance);\r
+        Terrs = 0;  Tbits = 0;  Terrsldpc = 0;  Tbitsldpc = 0; Ferrsldpc = 0;\r
+        if verbose > 1\r
+            printf("EsNo (dB): %f EsNo: %f variance: %f\n", Es, EsNo, variance);\r
+        end\r
+        \r
+        % init HF channel\r
+        sc = 1;\r
 \r
         tx_filt_log = [];\r
         rx_filt_log = [];\r
         rx_baseband_log = [];\r
         tx_baseband_log = [];\r
         noise_log = [];\r
-        c_est_log = [];\r
-        c_est = 0;\r
-\r
+        hf_angle_log = [];\r
         tx_phase = rx_phase = 0;\r
+        tx_data_buffer = zeros(1,2*framesize);\r
+        s_data_buffer = zeros(1,2*Nsymb);\r
 \r
         for nn = 1: Ntrials\r
                   \r
-            tx_bits = round( rand( 1, framesize ) );\r
+            %tx_bits = round( rand( 1, framesize*rate ) );\r
+            tx_bits = [1 0 zeros(1,framesize*rate-2)];\r
 \r
             % modulate\r
 \r
-            s = zeros(1, Nsymb);\r
-            for i=1:Nsymb\r
-                tx_symb = qpsk_mod(tx_bits(2*(i-1)+1:2*i));\r
-                %printf("shift: %f prev_sym: %f  ", tx_symb, prev_sym_tx);\r
-                if strcmp(modulation,'dqpsk')\r
-                    tx_symb *= prev_sym_tx;\r
-                    %printf("tx_symb: %f\n", tx_symb);\r
-                    prev_sym_tx = tx_symb;\r
-                end \r
-                s(i) = tx_symb;\r
+            if ldpc_code\r
+                [tx_bits, s] = ldpc_enc(tx_bits, code_param);\r
+                t2 = tx_bits;\r
+                s2 = s;\r
+            else\r
+                s = zeros(1, Nsymb);\r
+                for i=1:Nsymb\r
+                    tx_symb = qpsk_mod(tx_bits(2*(i-1)+1:2*i));\r
+                    %printf("shift: %f prev_sym: %f  ", tx_symb, prev_sym_tx);\r
+                    if strcmp(modulation,'dqpsk')\r
+                        tx_symb *= prev_sym_tx;\r
+                        %printf("tx_symb: %f\n", tx_symb);\r
+                        prev_sym_tx = tx_symb;\r
+                    end \r
+                    s(i) = tx_symb;\r
+                end\r
             end\r
-\r
             s_ch = s;\r
 \r
             % root nyquist filter symbols\r
@@ -152,18 +206,35 @@ function sim_out = ber_test(sim_in, modulation)
                % HF channel simulation\r
 \r
                if hf_sim\r
+\r
                    hf_sim_delay_line(1:Nhfdelay) = hf_sim_delay_line(M+1:M+Nhfdelay);\r
                    hf_sim_delay_line(Nhfdelay+1:M+Nhfdelay) = tx_filt;\r
 \r
-                   if ((sc+M) > length(spread)) || ((sc+M) > length(spread_2ms))\r
-                       sc =1 ;\r
+                   % disable as a wrap around will cause a nasty phase jump.  Best to generate\r
+                   % longer files\r
+                   %if ((sc+M) > length(spread)) || ((sc+M) > length(spread_2ms))\r
+                   %    sc =1 ;\r
+                   %end\r
+                   if hf_phase_only\r
+                        comb = conj(spread(sc:sc+M-1))' + conj(spread_2ms(sc:sc+M-1))';\r
+                        tx_filt = tx_filt.*exp(j*angle(comb));\r
+                        hf_angle_log = [hf_angle_log angle(comb)];\r
+                   else\r
+                       if hf_mag_only\r
+                           comb = conj(spread(sc:sc+M-1))' + conj(spread_2ms(sc:sc+M-1))';\r
+                           tx_filt = tx_filt.*abs(comb);   \r
+                       else\r
+                           % regular HF channel sim\r
+                           tx_filt = tx_filt.*conj(spread(sc:sc+M-1))' + hf_sim_delay_line(1:M).*conj(spread_2ms(sc:sc+M-1))';\r
+                       end\r
                    end\r
-                   tx_filt = tx_filt.*spread(sc:sc+M-1)' + hf_sim_delay_line(1:M).*spread_2ms(sc:sc+M-1)';\r
                    sc += M;\r
-\r
\r
                    % normalise so average HF power C=1\r
 \r
-                   tx_filt *= hf_gain;\r
+                   if hf_phase_only == 0   % C already 1 if we are just tweaking phase\r
+                       tx_filt *= hf_gain;\r
+                   end\r
                end\r
                tx_filt_log = [tx_filt_log tx_filt];\r
 \r
@@ -199,31 +270,34 @@ function sim_out = ber_test(sim_in, modulation)
                s_ch(k) = rx_filt;               \r
             end\r
 \r
-            % coherent demod phase estimation and correction\r
-            % need sliding window\r
-            % use known (pilot) symbols\r
-            % start with all symbols pilots, then gradually decimate, e.g. 1 in 5 pilots\r
+            % coherent demod phase estimation and correction using pilot symbols\r
+            % cheating a bit here, we use fact that all tx-ed symbols are known\r
 \r
             if phase_est\r
                 for i=1:Nsymb\r
 \r
                     % delay line for phase est window\r
 \r
-                    r_delay_line(1:Np-1) = r_delay_line(2:Np);\r
-                    r_delay_line(Np) = s_ch(i);\r
+                    r_delay_line(1:Nps-1) = r_delay_line(2:Nps);\r
+                    r_delay_line(Nps) = s_ch(i);\r
 \r
                     % delay in tx data to compensate data for phase est window\r
 \r
-                    s_delay_line(1:Np-1) = s_delay_line(2:Np);\r
-                    s_delay_line(Np) = s(i);\r
-                    tx_bits(2*(i-1)+1:2*i) = qpsk_demod(s_delay_line(floor(Np/2)+1));\r
+                    s_delay_line(1:Nps-1) = s_delay_line(2:Nps);\r
+                    s_delay_line(Nps) = s(i);\r
+                    tx_bits(2*(i-1)+1:2*i) = qpsk_demod(s_delay_line(floor(Nps/2)+1));\r
 \r
-                    % estimate phase from known symbols and correct\r
+                    % estimate phase from surrounding known pilot symbols and correct\r
 \r
-                    corr = s_delay_line * r_delay_line';\r
-                    s_ch(i) = r_delay_line(floor(Np/2)+1).*exp(j*angle(corr));\r
+                    corr = 0; centre = floor(Nps/2)+1;\r
+                    for k=1:Ns:(Nps+1)\r
+                        if (k != centre)\r
+                            corr += s_delay_line(k) * r_delay_line(k)';\r
+                        end\r
+                    end\r
+                    s_ch(i) = r_delay_line(centre).*exp(j*angle(corr));\r
                end    \r
-                %printf("corr: %f angle: %f\n", corr, angle(corr));\r
+               %printf("corr: %f angle: %f\n", corr, angle(corr));\r
             end\r
 \r
             % de-modulate\r
@@ -242,42 +316,104 @@ function sim_out = ber_test(sim_in, modulation)
 \r
             % Measure BER\r
 \r
-            % discard bits from first 2*Nfiltsym symbols as tx and rx filter memories not full\r
+            % discard bits from first 2*Nfiltsym+Nps+1 symbols as tx\r
+            % and rx filter and phase est memories not full\r
 \r
+            skip = bps*(2*Nfiltsym+1+Nps+1);\r
             if nn == 1\r
-                tx_bits = tx_bits(2*bps*Nfiltsym+1:length(tx_bits));\r
-                rx_bits = rx_bits(2*bps*Nfiltsym+1:length(rx_bits));\r
+                tx_bits_tmp = tx_bits(skip:length(tx_bits));\r
+                rx_bits_tmp = rx_bits(skip:length(rx_bits));\r
             end\r
 \r
-            error_positions = xor( rx_bits, tx_bits );\r
+            error_positions = xor( rx_bits_tmp, tx_bits_tmp );\r
             Nerrs = sum(error_positions);\r
             Terrs += Nerrs;\r
-            Tbits = Tbits + length(tx_bits);\r
+            Tbits += length(tx_bits_tmp);\r
+\r
+            % Optionally LDPC decode\r
+            \r
+            if ldpc_code\r
+                % filter memories etc screw up frame alignment so we need to buffer a frame\r
+\r
+                tx_data_buffer(1:framesize) = tx_data_buffer(framesize+1:2*framesize);\r
+                s_data_buffer(1:Nsymb) = s_data_buffer(Nsymb+1:2*Nsymb);\r
+                tx_data_buffer(framesize+1:2*framesize) = tx_bits;\r
+                s_data_buffer(Nsymb+1:2*Nsymb) = s_ch;\r
+\r
+                st_tx = (Nfiltsym-1)*bps+1;\r
+                st_s = (Nfiltsym-1);\r
+\r
+                detected_data = ldpc_dec(code_param, max_iterations, demod_type, decoder_type, ...\r
+                                s_data_buffer(st_s+1:st_s+Nsymb), min(100,EsNo));\r
+\r
+                % ignore first frame as filter, phase est memories filling up\r
+                if nn != 1\r
+                    error_positions = xor( detected_data(1:framesize*rate), ...\r
+                                           tx_data_buffer(st_tx:st_tx+framesize*rate-1) );\r
+                    Nerrs = sum(error_positions);\r
+                    if Nerrs\r
+                        Ferrsldpc++;\r
+                    end\r
+                    Terrsldpc += Nerrs;\r
+                    Tbitsldpc += framesize*rate;\r
+                end\r
+            end\r
 \r
         end\r
     \r
         TERvec(ne) = Terrs;\r
-        FERvec(ne) = Ferrs;\r
         BERvec(ne) = Terrs/Tbits;\r
-        %printf("  Terrs: %d BER %f BER theory %f C %f N %f Es %f No %f Es/No %f\n\n", Terrs,\r
-        %       Terrs/Tbits, 0.5*erfc(sqrt(EsNo/2)), var(tx_filt_log), var(noise_log),\r
-        %       var(tx_filt_log)/Rs, var(noise_log)/Fs, (var(tx_filt_log)/Rs)/(var(noise_log)/Fs));\r
+        if ldpc_code\r
+            TERldpcvec(ne) = Terrsldpc;\r
+            FERldpcvec(ne) = Ferrsldpc;\r
+            BERldpcvec(ne) = Terrsldpc/Tbitsldpc;\r
+        end\r
+\r
+        if verbose \r
+            printf("EsNo (dB): %f  Terrs: %d BER %f BER theory %f", Es, Terrs,\r
+                   Terrs/Tbits, 0.5*erfc(sqrt(EsNo/2)));\r
+            if ldpc_code\r
+                printf(" LDPC: Terrs: %d BER: %f", Terrsldpc, Terrsldpc/Tbitsldpc);\r
+            end\r
+            printf("\n");\r
+        end\r
+        if verbose > 1\r
+            printf("Terrs: %d BER %f BER theory %f C %f N %f Es %f No %f Es/No %f\n\n", Terrs,\r
+                   Terrs/Tbits, 0.5*erfc(sqrt(EsNo/2)), var(tx_filt_log), var(noise_log),\r
+                   var(tx_filt_log)/Rs, var(noise_log)/Fs, (var(tx_filt_log)/Rs)/(var(noise_log)/Fs));\r
+        end\r
     end\r
     \r
     Ebvec = Esvec - 10*log10(bps);\r
-    sim_out.BERvec = BERvec;\r
-    sim_out.BER_theoryvec = 0.5*erfc(sqrt(10.^(Ebvec/10)));\r
-    sim_out.Ebvec = Ebvec;\r
-    sim_out.FERvec = FERvec;\r
-    sim_out.TERvec  = TERvec;\r
+    sim_out.BERvec      = BERvec;\r
+    sim_out.Ebvec       = Ebvec;\r
+    sim_out.TERvec      = TERvec;\r
+    if ldpc_code\r
+        sim_out.BERldpcvec  = BERldpcvec;\r
+        sim_out.TERldpcvec  = TERldpcvec;\r
+        sim_out.FERldpcvec  = FERldpcvec;\r
+    end\r
 \r
     if plot_scatter\r
         figure(2);\r
         clf;\r
         scat = rx_symb_log(2*Nfiltsym:length(rx_symb_log)) .* exp(j*pi/4);\r
         plot(real(scat), imag(scat),'+');\r
-        figure(3);\r
-        plot(c_est_log);\r
+\r
+        if hf_sim\r
+          figure(3);\r
+\r
+          if hf_phase_only\r
+              plot(hf_angle_log);\r
+              axis([1 10000 min(hf_angle_log) max(hf_angle_log)])\r
+          else\r
+            plot(abs(spread));\r
+            hold on;\r
+            plot(abs(spread_2ms),'g');\r
+            hold off;\r
+            axis([1 10000 0 1.4])\r
+          end\r
+       end\r
     end\r
 endfunction\r
 \r
@@ -296,6 +432,10 @@ endfunction
 % Gray coded QPSK demodulation function\r
 \r
 function two_bits = qpsk_demod(symbol)\r
+    if isscalar(symbol) == 0\r
+        printf("only works with scalars\n");\r
+        return;\r
+    end\r
     bit0 = real(symbol*exp(j*pi/4)) < 0;\r
     bit1 = imag(symbol*exp(j*pi/4)) < 0;\r
     two_bits = [bit1 bit0];\r
@@ -303,37 +443,69 @@ endfunction
 \r
 % Start simulations ---------------------------------------\r
 \r
-sim_in.Esvec            = 1:12; \r
-sim_in.Ntrials          = 100;\r
-sim_in.framesize        = 100;\r
-sim_in.Rs               = 100;\r
+more off;\r
+sim_in.verbose = 1;\r
+\r
+sim_in.Esvec            = 1:5; \r
+sim_in.Ntrials          = 3;\r
+sim_in.framesize        = 576;\r
+sim_in.Rs               = 400;\r
 sim_in.phase_offset     = 0;\r
 sim_in.phase_est        = 0;\r
 sim_in.w_offset         = 0;\r
 sim_in.plot_scatter     = 0;\r
+sim_in.hf_delay_ms      = 2;\r
 sim_in.hf_sim           = 0;\r
+sim_in.Np               = 6;\r
+sim_in.Ns               = 5;\r
+sim_in.hf_phase_only    = 0;\r
+sim_in.hf_mag_only      = 0;\r
+sim_in.ldpc_code        = 1;\r
 \r
-sim_qpsk                = ber_test(sim_in, 'qpsk');\r
-sim_dqpsk               = ber_test(sim_in, 'dqpsk');\r
+Ebvec = sim_in.Esvec - 10*log10(2);\r
+BER_theory = 0.5*erfc(sqrt(10.^(Ebvec/10)));\r
+\r
+sim_qpsk           = ber_test(sim_in, 'qpsk');\r
+\r
+figure(1); \r
+clf;\r
+semilogy(Ebvec, BER_theory,'r;QPSK theory;')\r
+hold on;\r
+semilogy(sim_qpsk.Ebvec, sim_qpsk.BERvec,'g;QPSK;')\r
+hold off;\r
+xlabel('Eb/N0')\r
+ylabel('BER')\r
+grid("minor")\r
+\r
+\r
+if 0\r
+sim_in.hf_mag_only      = 1;\r
+sim_qpsk_mag            = ber_test(sim_in, 'qpsk');\r
+\r
+sim_in.hf_mag_only      = 0;\r
+sim_in.hf_phase_only    = 1;\r
 sim_in.phase_est        = 1;\r
-sim_qpsk_coh            = ber_test(sim_in, 'qpsk');\r
-sim_in.hf_delay_ms      = 2;\r
-sim_in.hf_sim           = 1;\r
-sim_qpsk_hf             = ber_test(sim_in, 'qpsk');\r
+sim_qpsk_phase          = ber_test(sim_in, 'qpsk');\r
+\r
+sim_in.hf_phase_only    = 0;\r
+sim_qpsk_coh_6_5        = ber_test(sim_in, 'qpsk');\r
+\r
 sim_in.phase_est        = 0;\r
-sim_dqpsk_hf            = ber_test(sim_in, 'dqpsk');\r
+sim_dqpsk               = ber_test(sim_in, 'dqpsk');\r
 \r
 figure(1); \r
 clf;\r
-semilogy(sim_qpsk.Ebvec, sim_qpsk.BERvec,'b;qpsk;')\r
+semilogy(Ebvec, BER_theory,'r;QPSK theory;')\r
 hold on;\r
-semilogy(sim_qpsk.Ebvec, sim_qpsk.BER_theoryvec,'r;qpsk theory;')\r
-semilogy(sim_dqpsk.Ebvec, sim_dqpsk.BERvec,'g;dqpsk;')\r
-semilogy(sim_qpsk_coh.Ebvec, sim_qpsk_coh.BERvec,'k;qpsk coh;')\r
-semilogy(sim_qpsk_hf.Ebvec, sim_qpsk_hf.BERvec,'c;qpsk hf;')\r
-semilogy(sim_dqpsk_hf.Ebvec, sim_dqpsk_hf.BERvec,'m;dqpsk hf;')\r
+semilogy(sim_qpsk_mag.Ebvec, sim_qpsk_mag.BERvec,'g;QPSK CCIR poor mag;')\r
+semilogy(sim_qpsk_phase.Ebvec, sim_qpsk_phase.BERvec,'k;QPSK CCIR poor phase;')\r
+semilogy(sim_qpsk_coh_6_5.Ebvec, sim_qpsk_coh_6_5.BERvec,'c;QPSK CCIR poor Np=6 Ns=5;')\r
+semilogy(sim_dqpsk.Ebvec, sim_dqpsk.BERvec,'b;DQPSK CCIR poor;')\r
+%semilogy(sim_qpsk_coh_5_24.Ebvec, sim_qpsk_coh_5_24.BERvec,'k;QPSK Ns=5 Np=24;')\r
+%semilogy(sim_qpsk_coh_2_12.Ebvec, sim_qpsk_coh_2_12.BERvec,'c;QPSK Ns=2 Np=12;')\r
 hold off;\r
 xlabel('Eb/N0')\r
 ylabel('BER')\r
 grid("minor")\r
-\r
+axis([min(Ebvec)-1 max(Ebvec)+1 1E-2 1])\r
+end\r