maximum likelihood dqpsk, works well for AWGN, not so good for HF
authordrowe67 <drowe67@01035d8c-6547-0410-b346-abe4f91aad63>
Tue, 7 Oct 2014 09:50:04 +0000 (09:50 +0000)
committerdrowe67 <drowe67@01035d8c-6547-0410-b346-abe4f91aad63>
Tue, 7 Oct 2014 09:50:04 +0000 (09:50 +0000)
git-svn-id: https://svn.code.sf.net/p/freetel/code@1879 01035d8c-6547-0410-b346-abe4f91aad63

codec2-dev/octave/fdmdv.m
codec2-dev/octave/ldpcut.m
codec2-dev/octave/test_ml.m [new file with mode: 0644]
codec2-dev/octave/test_qpsk2.m
codec2-dev/octave/test_ucinter.m [deleted file]

index c6aaf866d3aca7a7869fe9aa266e3c6251109189..415a6d7d6930dc82575b47e46b82e8a04044772c 100644 (file)
@@ -1147,7 +1147,7 @@ for c=1:Nc/2
   freq_pol(c)  = 2*pi*carrier_freq/Fs;
   freq(c)      = exp(j*freq_pol(c));
 end
-for c=Nc/2+1:Nc
+for c=floor(Nc/2)+1:Nc
   %carrier_freq = (-Nc/2 + c)*Fsep + Fcentre;
   carrier_freq = (-Nc/2 + c)*Fsep;
   freq_pol(c)  = 2*pi*carrier_freq/Fs;
index 80142ba6ead0c13d1b1ec7407dbf4723350f698e..9dfdc94e41cc9a3b84c34388ea0cd2b2d59bedd3 100644 (file)
@@ -53,7 +53,7 @@ end
 for nn = 1: Ntrials        \r
     st = (nn-1)*code_param.symbols_per_frame + 1;\r
     en = (nn)*code_param.symbols_per_frame;\r
-    detected_data = ldpc_dec(code_param, max_iterations, demod_type, decoder_type, r(st:en), EsNo);\r
+    detected_data = ldpc_dec(code_param, max_iterations, demod_type, decoder_type, r(st:en), EsNo, ones(1,code_param.symbols_per_frame));\r
     st = (nn-1)*code_param.data_bits_per_frame + 1;\r
     en = (nn)*code_param.data_bits_per_frame;\r
     error_positions = xor( detected_data(1:code_param.data_bits_per_frame), data(st:en) );\r
diff --git a/codec2-dev/octave/test_ml.m b/codec2-dev/octave/test_ml.m
new file mode 100644 (file)
index 0000000..51dd8cb
--- /dev/null
@@ -0,0 +1,347 @@
+% test_ml.m
+% David Rowe Oct 2014
+%
+
+% Simulation to test FDM QPSK with maximum likelihood decoding on
+% fading channels.
+  
+1;
+
+% main test function 
+
+function sim_out = ber_test(sim_in, modulation)
+    Fs = 8000;
+
+    verbose          = sim_in.verbose;
+    Ntrials          = sim_in.Ntrials;
+    Esvec            = sim_in.Esvec;
+    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_mag_only      = sim_in.hf_mag_only;
+    framesize        = sim_in.framesize;
+    ml               = sim_in.ml;
+
+    bps              = 2;
+    Nc = Nsymb       = framesize/bps;      % total number of symbols
+
+    prev_sym_tx      = qpsk_mod([0 0])*ones(1,Nc);
+    prev_sym_rx      = qpsk_mod([0 0])*ones(1,Nc);
+    r                = qpsk_mod([0 0])*ones(Nc,4);
+
+    % 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;
+
+        tx_symb_log      = [];
+        rx_symb_log      = [];
+        errors_log       = [];
+        Nerrs_log        = [];
+
+        % init HF channel
+
+        hf_n = 1;
+
+        % simulation starts here-----------------------------------
+        for nn = 1: Ntrials
+                  
+            tx_bits = round(rand(1,framesize));                       
+
+            % modulate --------------------------------------------
+
+            for c=1:Nc
+              tx_symb(c) = qpsk_mod(tx_bits((2*(c-1)+1):(2*c)));
+              if strcmp(modulation,'dqpsk')
+                tx_symb(c) *= prev_sym_tx(c);
+                prev_sym_tx(c) = tx_symb(c);
+              end
+            end
+            s_ch = tx_symb;
+            
+            % HF channel simulation  ------------------------------------
+            
+            if hf_sim
+
+                % separation between carriers.  Note this 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
+
+                for c=1:Nc
+                    ahf_model = hf_gain*(spread(hf_n) + exp(-j*c*wsep*nhfdelay)*spread_2ms(hf_n));
+                    if hf_mag_only
+                        s_ch(c) *= abs(ahf_model);
+                    else
+                        s_ch(c) *= ahf_model;
+                    end
+                    hf_model(hf_n, c) = ahf_model;
+                end
+                hf_n++;
+            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,Nc) + j*randn(1,Nc));
+
+            s_ch = s_ch + noise;
+
+            % de-modulate
+
+            rx_bits = zeros(1, framesize);
+            for c=1:Nc
+
+                r(c,1:3) = r(c, 2:4);
+                r(c,4) = s_ch(c);
+
+                rx_symb(c) = s_ch(c);
+                if strcmp(modulation,'dqpsk')
+                  tmp = rx_symb(c);
+                  rx_symb(c) *= conj(prev_sym_rx(c)/abs(prev_sym_rx(c)));
+                  prev_sym_rx(c) = tmp;
+                end
+
+                % r(c,:)
+
+                if ml == 0
+                  rx_bits((2*(c-1)+1):(2*c)) = qpsk_demod(rx_symb(c));
+                else
+                  tx = [1 j -j -1];
+                  max_eta = 0;  max_symb = tx(1);
+                  for k=1:4
+                    for k_1=1:4
+                      for k_2=1:4
+                        %eta = abs(r(c,1) + r(c,3)*tx(k)'*tx(k_1)' + r(c,2)*tx(k_1)')^2;
+                        eta = abs(r(c,1) + r(c,4)*tx(k)'*tx(k_1)'*tx(k_2)' + r(c,3)*tx(k_1)'*tx(k_2)' + r(c,2)*tx(k_2)')^2;
+                        %printf("  %d %d %f \n", k_1, k, eta);
+                        if eta > max_eta
+                          max_eta = eta;
+                          max_symb = tx(k);
+                        end
+                      end
+                    end
+                  end
+                  rx_bits((2*(c-1)+1):(2*c)) = qpsk_demod(max_symb);      
+                end
+                rx_symb_log = [rx_symb_log rx_symb(c)];
+            end
+            
+            % Measure BER
+
+            error_positions = xor(rx_bits, tx_bits);
+            Nerrs = sum(error_positions);
+            Terrs += Nerrs;
+            Tbits += length(tx_bits);
+            errors_log = [errors_log error_positions];
+            Nerrs_log = [Nerrs_log Nerrs];
+        end
+    
+        TERvec(ne) = Terrs;
+        BERvec(ne) = Terrs/Tbits;
+
+        if verbose 
+            av_tx_pwr = (tx_symb_log * tx_symb_log')/length(tx_symb_log)
+
+            printf("EsNo (dB): %3.1f  Terrs: %d BER %4.3f QPSK BER theory %4.3f av_tx_pwr: %3.2f", EsNodB, Terrs,
+                   Terrs/Tbits, 0.5*erfc(sqrt(EsNo/2)), av_tx_pwr);
+            printf("\n");
+        end
+    end
+    
+    Ebvec = Esvec - 10*log10(bps);
+    sim_out.BERvec          = BERvec;
+    sim_out.Ebvec           = Ebvec;
+    sim_out.TERvec          = TERvec;
+    sim_out.errors_log      = errors_log;
+
+    if plot_scatter
+        figure(2);
+        clf;
+        scat = rx_symb_log .* exp(j*pi/4);
+        plot(real(scat), imag(scat),'+');
+        title('Scatter plot');
+
+        if hf_sim
+          figure(3);
+          clf;
+        
+          y = 1:(hf_n-1);
+          x = 1:Nc;
+          EsNodBSurface = 20*log10(abs(hf_model(y,:))) - 10*log10(variance);
+          EsNodBSurface(find(EsNodBSurface < -5)) = -5;
+          mesh(x,y,EsNodBSurface);
+          grid
+          axis([1 Nc 1 Rs*5 -5 15])
+          title('HF Channel Es/No');
+
+          if verbose 
+            av_hf_pwr = sum(abs(hf_model(y)).^2)/(hf_n-1);
+            printf("average HF power: %3.2f over %d symbols\n", av_hf_pwr, hf_n-1);
+          end
+        end
+
+        figure(4)
+        clf
+        stem(Nerrs_log)
+   end
+
+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.Rs               = 50;
+  sim_in.framesize        = 8;
+  sim_in.ml               = 0;
+
+  sim_in.hf_delay_ms      = 2;
+  sim_in.hf_sim           = 0;
+  sim_in.hf_mag_only      = 0;
+endfunction
+
+
+function test_curves
+
+  sim_in = standard_init();
+
+  sim_in.Ntrials          = 1000;
+
+  sim_in.hf_sim           = 0;
+  sim_in.plot_scatter     = 0;
+  sim_in.Esvec            = 5:15; 
+  Ebvec = sim_in.Esvec - 10*log10(2);
+  BER_theory = 0.5*erfc(sqrt(10.^(Ebvec/10)));
+  sim_in.ml               = 0;
+  sim_dqpsk               = ber_test(sim_in, 'dqpsk');
+  sim_in.ml               = 1;  
+  sim_dqpsk_ml            = ber_test(sim_in, 'dqpsk');
+  sim_in.hf_sim           = 1;
+  sim_in.ml               = 0;
+  sim_dqpsk_hf            = ber_test(sim_in, 'dqpsk');
+  sim_in.ml               = 1;  
+  sim_dqpsk_ml_hf         = ber_test(sim_in, 'dqpsk');
+
+  figure(1); 
+  clf;
+  semilogy(Ebvec, BER_theory,'r;QPSK theory;')
+  hold on;
+  semilogy(sim_dqpsk.Ebvec, sim_dqpsk.BERvec,'c;DQPSK AWGN;')
+  semilogy(sim_dqpsk_ml.Ebvec, sim_dqpsk_ml.BERvec,'k;DQPSK ML AWGN;')
+  semilogy(sim_dqpsk_hf.Ebvec, sim_dqpsk_hf.BERvec,'c;DQPSK HF;')
+  semilogy(sim_dqpsk_ml_hf.Ebvec, sim_dqpsk_ml_hf.BERvec,'k;DQPSK ML HF;')
+  hold off;
+
+  xlabel('Eb/N0')
+  ylabel('BER')
+  grid("minor")
+  axis([min(Ebvec) max(Ebvec) 1E-3 1])
+endfunction
+
+
+function test_single
+  sim_in = standard_init();
+
+  sim_in.verbose          = 1;
+  sim_in.plot_scatter     = 1;
+  sim_in.Ntrials          = 500;
+
+  sim_in.hf_mag_only      = 0;
+  sim_in.hf_sim           = 1;
+  sim_in.ml               = 0;
+  sim_in.Esvec            = 10;
+
+  sim_qpsk_hf             = ber_test(sim_in, 'dqpsk');
+endfunction
+
+
+% Start simulations ---------------------------------------
+
+more off;
+
+test_curves();
+%test_single();
index a17e1ee4ce705073e5aed3d1ba811476a03b26ea..bf7bd8e9e1b4c476f174cc21e22f71914249e209 100644 (file)
@@ -515,7 +515,7 @@ function ideal
 \r
   sim_in.hf_sim           = 0;\r
   sim_in.plot_scatter     = 0;\r
-  sim_in.Esvec            = 2:15\r
+  sim_in.Esvec            = 2:10\r
   sim_in.ldpc_code        = 0;\r
   Ebvec = sim_in.Esvec - 10*log10(2);\r
   BER_theory = 0.5*erfc(sqrt(10.^(Ebvec/10)));\r
@@ -523,16 +523,13 @@ function ideal
   sim_dqpsk               = ber_test(sim_in, 'dqpsk');\r
 \r
   sim_in.hf_sim           = 1;\r
-  sim_in.Esvec            = 2:15; \r
   sim_qpsk_hf             = ber_test(sim_in, 'qpsk');\r
   sim_dqpsk_hf            = ber_test(sim_in, 'dqpsk');\r
-  sim_in.ldpc_code        = 1;\r
-  sim_qpsk_hf_ldpc1       = ber_test(sim_in, 'qpsk');\r
   sim_in.ldpc_code_rate   = 1/2;\r
-  sim_qpsk_hf_ldpc2       = ber_test(sim_in, 'qpsk');\r
-  sim_in.ldpc_code_rate   = 3/4;\r
-  sim_in.hf_sim           = 0;\r
-  sim_qpsk_awgn_ldpc      = ber_test(sim_in, 'qpsk');\r
+  sim_in.ldpc_code        = 1;\r
+  sim_qpsk_hf_ldpc        = ber_test(sim_in, 'qpsk');\r
+  sim_in.hf_mag_only      = 0;\r
+  sim_dqpsk_hf_ldpc       = ber_test(sim_in, 'dqpsk');\r
 \r
   figure(1); \r
   clf;\r
@@ -540,11 +537,10 @@ function ideal
   hold on;\r
   semilogy(sim_qpsk.Ebvec, sim_qpsk.BERvec,'g;QPSK AWGN;')\r
   semilogy(sim_qpsk_hf.Ebvec, sim_qpsk_hf.BERvec,'r;QPSK HF;')\r
-  semilogy(sim_dqpsk.Ebvec, sim_dqpsk.BERvec,'c;DQPSK AWGN;')\r
-  semilogy(sim_dqpsk_hf.Ebvec, sim_dqpsk_hf.BERvec,'m;DQPSK HF;')\r
-  semilogy(sim_qpsk_hf_ldpc1.Ebvec, sim_qpsk_hf_ldpc1.BERldpcvec,'k;QPSK HF LDPC 3/4;')\r
-  semilogy(sim_qpsk_hf_ldpc2.Ebvec, sim_qpsk_hf_ldpc2.BERldpcvec,'b;QPSK HF LDPC 1/2;')\r
-  semilogy(sim_qpsk_awgn_ldpc.Ebvec, sim_qpsk_awgn_ldpc.BERldpcvec,'k;QPSK AWGN LDPC 3/4;')\r
+  semilogy(sim_dqpsk.Ebvec, sim_dqpsk.BERvec,'g;DQPSK AWGN;')\r
+  semilogy(sim_dqpsk_hf.Ebvec, sim_dqpsk_hf.BERvec,'r;DQPSK HF;')\r
+  semilogy(sim_qpsk_hf_ldpc.Ebvec, sim_qpsk_hf_ldpc.BERldpcvec,'b;QPSK HF LDPC 1/2;')\r
+  semilogy(sim_dqpsk_hf_ldpc.Ebvec, sim_dqpsk_hf_ldpc.BERldpcvec,'b;DQPSK HF LDPC 1/2;')\r
 \r
   hold off;\r
   xlabel('Eb/N0')\r
@@ -636,4 +632,4 @@ endfunction
 \r
 more off;\r
 \r
-test_phase_est();\r
+ideal();\r
diff --git a/codec2-dev/octave/test_ucinter.m b/codec2-dev/octave/test_ucinter.m
deleted file mode 100644 (file)
index 365bca4..0000000
+++ /dev/null
@@ -1,353 +0,0 @@
-% test_ucinter.m
-% David Rowe August 2014
-%
-
-% FDM QPSK modem simulation to test uncododed interleaving ideas on HF
-% channels without building a full blown modem.
-
-% [X] baseline QPSK simulation AWGN
-% [ ] Spreading function
-% [ ] visualise different carriers with and without spreading
-% [ ] prove perf same as AWGN when one carrier is knocked out
-%     + AWGN and "faded channel" with same average SNR
-%     + use contrived example
-%     + then try less contrived but still well behaived maths channel
-
-% Ideas:
-%  + decode quickly but then slow down while playing, using full interleave
-%  + exp type window so we can decode using current symbols alone in gd SNR, or extend window over greater time
-%  + like root nyq filtering, combining multiple symbols, weighted
-%  + spreading could make it worse, e.g. short term average might be very low
-%  + SSB spreads out over a long way.  We could so this too!  Like spread spectrum.  Don't have to be related to
-%    carrier width.  Minimise amount of stuff that gets nailed.  But didn't we try this with "1600 wide"?  OK, so 
-     key issue was BER.  That, is what we need to impove, using "high areas".
-     
-1;
-
-% main test function 
-
-function sim_out = ber_test(sim_in, modulation)
-    Fs = 8000;
-
-    verbose          = sim_in.verbose;
-    framesize        = sim_in.framesize;
-    Ntrials          = sim_in.Ntrials;
-    Esvec            = sim_in.Esvec;
-    phase_offset     = sim_in.phase_offset;
-    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;
-
-    bps              = 2;
-    Nsymb            = framesize/bps;
-    prev_sym_tx      = qpsk_mod([0 0]);
-    prev_sym_rx      = qpsk_mod([0 0]);
-
-    tx_bits_buf = zeros(1,2*framesize);
-    rx_bits_buf = zeros(1,2*framesize);
-    rx_symb_buf = zeros(1,2*Nsymb);
-
-    % 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;
-
-        tx_symb_log      = [];
-        rx_symb_log      = [];
-        noise_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
-
-        for nn = 1: Ntrials
-                  
-            tx_bits = round( rand( 1, framesize) );
-            % modulate --------------------------------------------
-
-            s = zeros(1, Nsymb);
-            for i=1:Nsymb
-                tx_symb = qpsk_mod(tx_bits(2*(i-1)+1:2*i));
-                if strcmp(modulation,'dqpsk')
-                    tx_symb *= prev_sym_tx;
-                    prev_sym_tx = tx_symb;
-                end 
-                s(i) = tx_symb;
-            end
-            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];
-            % 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) + noise(i+k-1);
-              end 
-              phase_offset += w_offset;
-            end
-               
-            % de-modulate
-
-            rx_bits = zeros(1, framesize);
-            for i=1:Nsymb
-                rx_symb = s_ch(i);
-                if strcmp(modulation,'dqpsk')
-                    tmp = rx_symb;
-                    rx_symb *= conj(prev_sym_rx/abs(prev_sym_rx));
-                    prev_sym_rx = tmp;
-                end
-                rx_bits((2*(i-1)+1):(2*i)) = qpsk_demod(rx_symb);
-                rx_symb_log = [rx_symb_log rx_symb];
-            end
-
-            % Measure BER
-
-            error_positions = xor(rx_bits, tx_bits);
-            Nerrs = sum(error_positions);
-            Terrs += Nerrs;
-            Tbits += length(tx_bits);
-        end
-    
-        TERvec(ne) = Terrs;
-        BERvec(ne) = Terrs/Tbits;
-
-        if verbose 
-            printf("EsNo (dB): %f  Terrs: %d BER %f BER theory %f", EsNodB, Terrs,
-                   Terrs/Tbits, 0.5*erfc(sqrt(EsNo/2)));
-            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);
-    sim_out.BERvec          = BERvec;
-    sim_out.Ebvec           = Ebvec;
-    sim_out.TERvec          = TERvec;
-
-    if plot_scatter
-        figure(2);
-        clf;
-        scat = rx_symb_log .* exp(j*pi/4);
-        plot(real(scat), imag(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 -5 15])
-        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');
-
-        figure(6)
-        tmp = [];
-        for i = 1:hf_n-1
-            tmp = [tmp abs(hf_model(i,:))];
-        end
-        hist(tmp);
-     end
-
-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;
-endfunction
-
-function test_ideal
-
-  sim_in = standard_init();
-
-  sim_in.verbose          = 1;
-  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:5; 
-  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');
-
-  figure(1); 
-  clf;
-  semilogy(Ebvec, BER_theory,'r;QPSK theory;')
-  hold on;
-  semilogy(sim_qpsk.Ebvec, sim_qpsk.BERvec,'g;QPSK AWGN;')
-  semilogy(sim_dqpsk.Ebvec, sim_dqpsk.BERvec,'c;DQPSK AWGN;')
-  hold off;
-
-  xlabel('Eb/N0')
-  ylabel('BER')
-  grid("minor")
-  axis([min(Ebvec) max(Ebvec) 1E-3 1])
-endfunction
-
-
-% Start simulations ---------------------------------------
-
-more off;
-
-test_ideal();