% cohpsk_dev.m % David Rowe Mar 2015 % % Coherent PSK modem development and testing functions % cohpsk_lib; % Init HF channel model from stored sample files of spreading signal ---------------------------------- function [spread spread_2ms hf_gain] = init_hf_model(Fs, nsam) % convert "spreading" samples from 1kHz carrier at Fss 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; Fss = 8000; 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/Fss)*(1:length(spread1k))'); spreadbb_2ms = spread1k_2ms.*exp(-j*(2*pi*Fc/Fss)*(1:length(spread1k_2ms))'); % remove -2000 Hz image b = fir1(50, 5/Fss); 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)); % change output samples so they are at rate Fs reqd by caller spread = resample(spread, Fs, Fss); spread_2ms = resample(spread_2ms, Fs, Fss); % 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(1:nsam))+var(spread_2ms(1:nsam))); endfunction function write_pilot_file(pilot, Nsymbrowpilot, Ns, Nsymrow, Npilotsframe, Nc); filename = sprintf("../src/cohpsk_defs.h", Npilotsframe, Nc); f=fopen(filename,"wt"); fprintf(f,"/* Generated by write_pilot_file() Octave function */\n\n"); fprintf(f,"#define NSYMROW %d /* number of data symbols on each row (i.e. each carrier) */\n", Nsymrow); fprintf(f,"#define NS %d /* number of data symbols between pilots */\n", Ns); fprintf(f,"#define NPILOTSFRAME %d /* number of pilot symbols on each row */\n", Npilotsframe); fprintf(f,"#define PILOTS_NC %d /* number of carriers */\n\n", Nc); fprintf(f,"#define NSYMROWPILOT %d /* length of row after pilots inserted */\n\n", Nsymbrowpilot); fclose(f); filename = sprintf("../src/pilots_coh.h", Npilotsframe, Nc); f=fopen(filename,"wt"); fprintf(f,"/* Generated by write_pilot_file() Octave function */\n\n"); fprintf(f,"float pilots_coh[][PILOTS_NC]={\n"); for r=1:Npilotsframe fprintf(f, " {"); for c=1:Nc-1 fprintf(f, " %f,", pilot(r, c)); end if r < Npilotsframe fprintf(f, " %f},\n", pilot(r, Nc)); else fprintf(f, " %f}\n};", pilot(r, Nc)); end end fclose(f); endfunction % Save test bits frame to a text file in the form of a C array function test_bits_coh_file(test_bits_coh) f=fopen("../src/test_bits_coh.h","wt"); fprintf(f,"/* Generated by test_bits_coh_file() Octave function */\n\n"); fprintf(f,"const int test_bits_coh[]={\n"); for m=1:length(test_bits_coh)-1 fprintf(f," %d,\n",test_bits_coh(m)); endfor fprintf(f," %d\n};\n",test_bits_coh(length(test_bits_coh))); fclose(f); endfunction % Rate Rs BER tests ------------------------------------------------------------------------------ function sim_out = ber_test(sim_in) sim_in = symbol_rate_init(sim_in); Fs = sim_in.Fs; Rs = sim_in.Rs; Ntrials = sim_in.Ntrials; verbose = sim_in.verbose; plot_scatter = sim_in.plot_scatter; framesize = sim_in.framesize; bps = sim_in.bps; Esvec = sim_in.Esvec; ldpc_code = sim_in.ldpc_code; rate = sim_in.ldpc_code_rate; code_param = sim_in.code_param; tx_bits_buf = sim_in.tx_bits_buf; Nsymb = sim_in.Nsymb; Nsymbrow = sim_in.Nsymbrow; Nsymbrowpilot = sim_in.Nsymbrowpilot; Nc = sim_in.Nc; Npilotsframe = sim_in.Npilotsframe; Ns = sim_in.Ns; Np = sim_in.Np; Nd = sim_in.Nd; modulation = sim_in.modulation; pilot = sim_in.pilot; prev_sym_tx = sim_in.prev_sym_tx; prev_sym_rx = sim_in.prev_sym_rx; rx_symb_buf = sim_in.rx_symb_buf; tx_pilot_buf = sim_in.tx_pilot_buf; rx_pilot_buf = sim_in.rx_pilot_buf; hf_sim = sim_in.hf_sim; nhfdelay = sim_in.hf_delay_ms*Rs/1000; hf_mag_only = sim_in.hf_mag_only; f_off = sim_in.f_off; div_time_shift = sim_in.div_timeshift; [spread spread_2ms hf_gain] = init_hf_model(Rs, Nsymbrowpilot*(Ntrials+2)); if strcmp(modulation,'dqpsk') Nsymbrowpilot = Nsymbrow; end % 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; s_ch_tx_log = []; rx_symb_log = []; noise_log = []; errors_log = []; Nerrs_log = []; phi_log = []; amp_log = []; EsNo__log = []; ldpc_errors_log = []; ldpc_Nerrs_log = []; Terrsldpc = Tbitsldpc = Ferrsldpc = 0; % init HF channel hf_n = 1; phase_offset_rect = 1; w_offset = 2*pi*f_off/Rs; w_offset_rect = exp(j*w_offset); ct_symb_buf = zeros(2*Nsymbrowpilot, Nc*Nd); prev_tx_symb = prev_rx_symb = ones(1, Nc*Nd); % simulation starts here----------------------------------- for nn = 1:Ntrials+2 if ldpc_code tx_bits = round(rand(1,framesize*rate)); else tx_bits = round(rand(1,framesize)); end if strcmp(modulation,'qpsk') [tx_symb tx_bits] = bits_to_qpsk_symbols(sim_in, tx_bits, code_param); % one frame delay on bits for qpsk tx_bits_buf(1:framesize) = tx_bits_buf(framesize+1:2*framesize); tx_bits_buf(framesize+1:2*framesize) = tx_bits; end if strcmp(modulation, 'dqpsk') [tx_symb prev_tx_symb] = bits_to_dqpsk_symbols(sim_in, tx_bits, prev_tx_symb); tx_bits_buf(1:framesize) = tx_bits; end s_ch = tx_symb; % HF channel simulation ------------------------------------ hf_fading = ones(1,Nsymb); 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 hf_model(hf_n, :) = zeros(1,Nc*Nd); for r=1:Nsymbrowpilot for c=1:Nd*Nc if c > Nc time_shift = sim_in.div_timeshift; else time_shift = 1; end ahf_model = hf_gain*(spread(hf_n+time_shift) + exp(-j*c*wsep*nhfdelay)*spread_2ms(hf_n+time_shift)); if hf_mag_only s_ch(r,c) *= abs(ahf_model); else s_ch(r,c) *= ahf_model; end hf_model(hf_n, c) = ahf_model; end hf_n++; end end % keep a record of each tx symbol so we can check average power for r=1:Nsymbrow for c=1:Nd*Nc s_ch_tx_log = [s_ch_tx_log s_ch(r,c)]; end end % 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(Nsymbrowpilot,Nc*Nd) + j*randn(Nsymbrowpilot,Nc*Nd)); noise_log = [noise_log noise]; for r=1:Nsymbrowpilot s_ch(r,:) *= phase_offset_rect; phase_offset_rect *= w_offset_rect; end s_ch += noise; ct_symb_buf(1:Nsymbrowpilot,:) = ct_symb_buf(Nsymbrowpilot+1:2*Nsymbrowpilot,:); ct_symb_buf(Nsymbrowpilot+1:2*Nsymbrowpilot,:) = s_ch; if strcmp(modulation,'qpsk') [rx_symb rx_bits rx_symb_linear amp_ phi_ sig_rms noise_rms sim_in] = qpsk_symbols_to_bits(sim_in, ct_symb_buf(1:Nsymbrowpilot+Npilotsframe,:)); phi_log = [phi_log; phi_]; amp_log = [amp_log; amp_]; end if strcmp(modulation,'dqpsk') [rx_symb rx_bits rx_symb_linear prev_rx_symb] = dqpsk_symbols_to_bits(sim_in, s_ch, prev_rx_symb); end % Wait until we have enough frames to do pilot assisted phase estimation if nn > 1 rx_symb_log = [rx_symb_log rx_symb_linear]; %EsNo__log = [EsNo__log EsNo_]; % Measure BER error_positions = xor(rx_bits, tx_bits_buf(1:framesize)); Nerrs = sum(error_positions); Terrs += Nerrs; Tbits += length(tx_bits); errors_log = [errors_log error_positions]; Nerrs_log = [Nerrs_log Nerrs]; % Optionally LDPC decode if ldpc_code detected_data = ldpc_dec(code_param, sim_in.max_iterations, sim_in.demod_type, sim_in.decoder_type, ... rx_symb_linear, min(100,EsNo_), amp_linear); error_positions = xor( detected_data(1:framesize*rate), tx_bits_buf(1:framesize*rate) ); Nerrs = sum(error_positions); ldpc_Nerrs_log = [ldpc_Nerrs_log Nerrs]; ldpc_errors_log = [ldpc_errors_log error_positions]; if Nerrs Ferrsldpc++; end Terrsldpc += Nerrs; Tbitsldpc += framesize*rate; end end end TERvec(ne) = Terrs; BERvec(ne) = Terrs/Tbits; if verbose av_tx_pwr = (s_ch_tx_log * s_ch_tx_log')/length(s_ch_tx_log); printf("EsNo (dB): %3.1f Terrs: %d Tbits: %d BER %5.3f QPSK BER theory %5.3f av_tx_pwr: %3.2f", EsNodB, Terrs, Tbits, Terrs/Tbits, 0.5*erfc(sqrt(EsNo/2)), av_tx_pwr); if ldpc_code printf("\n LDPC: Terrs: %d BER: %4.2f Ferrs: %d FER: %4.2f", Terrsldpc, Terrsldpc/Tbitsldpc, Ferrsldpc, Ferrsldpc/Ntrials); end 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; sim_out.ldpc_errors_log = ldpc_errors_log; if plot_scatter figure(2); clf; scat = rx_symb_log .* exp(j*pi/4); plot(real(scat), imag(scat),'+'); title('Scatter plot'); a = 1.5*max(real(scat)); b = 1.5*max(imag(scat)); axis([-a a -b b]); if hf_sim figure(3); clf; y = 1:(hf_n-1); x = 1:Nc*Nd; EsNodBSurface = 20*log10(abs(hf_model(y,:))) - 10*log10(variance); EsNodBSurface(find(EsNodBSurface < -5)) = -5; EsNodBSurface(find(EsNodBSurface > 25)) = 25; mesh(x,y,EsNodBSurface); grid axis([1 Nc*Nd 1 Rs*5 -5 25]) title('HF Channel Es/No'); if verbose [m n] = size(hf_model); av_hf_pwr = sum(sum(abs(hf_model(:,:)).^2))/(m*n); printf("average HF power: %3.2f over %d symbols\n", av_hf_pwr, m*n); end end if strcmp(modulation,'qpsk') % set up time axis to include gaps for pilots [m1 n1] = size(phi_log); phi_x = []; phi_x_counter = 1; p = Ns; for r=1:m1 if p == Ns phi_x_counter += Npilotsframe; p = 0; end p++; phi_x = [phi_x phi_x_counter++]; end phi_x -= Nsymbrowpilot; % account for delay in pilot buffer figure(5); clf subplot(211) [m n] = size(phi_log); plot(phi_x, phi_log(:,2),'r+;Estimated HF channel phase;') if hf_sim hold on; [m n] = size(hf_model); plot(angle(hf_model(1:m,2)),'g;HF channel phase;') hold off; end ylabel('Phase (rads)'); legend('boxoff'); axis([1 m -1.1*pi 1.1*pi]) subplot(212) plot(phi_x, amp_log(:,2),'r+;Estimated HF channel amp;') if hf_sim hold on; plot(abs(hf_model(1:m,2))) hold off; end ylabel('Amplitude'); xlabel('Time (symbols)'); legend('boxoff'); axis([1 m 0 3]) end figure(4) clf stem(Nerrs_log) axis([1 length(Nerrs_log) 0 max(Nerrs_log)+1]) end endfunction function sim_in = standard_init sim_in.verbose = 1; sim_in.do_write_pilot_file = 0; sim_in.plot_scatter = 0; sim_in.Esvec = 50; sim_in.Ntrials = 30; sim_in.framesize = 2; sim_in.Rs = 50; 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_mag_only = 0; sim_in.Nd = 1; endfunction