freedv-gui/codec2-1.2.0/octave/tcohpsk.m

746 lines
24 KiB
Matlab

% tcohpsk.m
% David Rowe Oct 2014
%
% Octave coherent PSK modem script that has two modes:
%
% i) tests the C port of the coherent PSK modem. This script loads
% the output of unittest/tcohpsk.c and compares it to the output of
% the reference versions of the same modem written in Octave.
%
% (ii) Runs the Octave version of the cohpsk modem to tune and develop
% it, including extensive channel simulations such as AWGN noise,
% fading/HF, frequency offset, frequency drift, and tx/rx sample
% rate differences.
% TODO:
%
% [X] Test
% [X] AWGN channel
% [X] freq offset
% [X] fading channel
% [X] freq drift
% [X] timing drift
% [X] tune perf/impl loss to get closer to ideal
% [X] linear interp of phase for better fading perf
% [X] freq offset/drift feedback loop
% [X] PAPR measurement and reduction
% [X] false sync
% [X] doesn't sync up on noise (used EsNo = -12)
% [X] similar but invalid signal like huge f off
% [X] ability to "unsync" when signal disappears
% [ ] some calibrated tests against FreeDV 1600
% + compare sound quality at various Es/Nos
% [ ] sync
% + set some req & implement
% [ ] way to handle eom w/o nasties
% + like mute output when signal has gone or v low snr
% + instantaneous snr
% [X] ssb tx filter with 3dB passband ripple
% + diverisity helped for AWGN BER 0.024 down to 0.016
% + Only a small change in fading perf with filter on/off
% + however other filters may have other effects, should test this,
% e.g. scatter plots, some sort of BER metric?
% [X] EsNo estimation
% [ ] filter reqd with compression?
% + make sure not too much noise passed into noise floor
% [X] different diversity combination
% + taking largest symbol didn't help
% [X] histogram of bit errors
% + lot of data
% + ssb filter
% + compression
% + make sure it's flat with many errors
pkg load signal;
more off;
global passes = 0;
global fails = 0;
cohpsk_dev;
fdmdv_common;
autotest;
rand('state',1);
randn('state',1);
% select which test ----------------------------------------------------------
test = 'compare to c';
%test = 'awgn';
%test = 'fading';
% some parameters that can be over ridden, e.g. to disable parts of modem
initial_sync = 0; % setting this to 1 put us straight into sync w/o freq offset est
ftrack_en = 1; % set to 1 to enable freq tracking
ssb_tx_filt = 0; % set to 1 to to simulate SSB tx filter with passband ripple
Fs = 7500;
% predefined tests ....
if strcmp(test, 'compare to c')
frames = 30;
foff = 58.7;
dfoff = -0.5/Fs;
EsNodB = 8;
fading_en = 0;
hf_delay_ms = 2;
compare_with_c = 1;
sample_rate_ppm = -1500;
ssb_tx_filt = 0;
end
% should be BER around 0.015 to 0.02
if strcmp(test, 'awgn')
frames = 100;
foff = 58.7;
dfoff = -0.5/Fs;
EsNodB = 8;
fading_en = 0;
hf_delay_ms = 2;
compare_with_c = 0;
sample_rate_ppm = 0;
end
% Similar to AWGN - should be BER around 0.015 to 0.02
if strcmp(test, 'fading');
frames = 100;
foff = -25;
dfoff = 0.5/Fs;
EsNodB = 12;
fading_en = 1;
hf_delay_ms = 2;
compare_with_c = 0;
sample_rate_ppm = 0;
end
EsNo = 10^(EsNodB/10);
% modem constants ----------------------------------------------------------
Rs = 75; % symbol rate in Hz
Nc = 7; % number of carriers
Nd = 2; % diveristy factor
framesize = 56; % number of payload data bits in the frame
Nsw = 4; % frames we demod for initial sync window
afdmdv.Nsym = 6; % size of tx/tx root nyquist filter in symbols
afdmdv.Nt = 5; % number of symbols we estimate timing over
clip = 6.5; % Clipping of tx signal to reduce PAPR. Adjust by
% experiment as Nc and Nd change. Check out no noise
% scatter diagram and AWGN/fading BER perf
% at operating points
% FDMDV init ---------------------------------------------------------------
afdmdv.Fs = Fs;
afdmdv.Nc = Nd*Nc-1;
afdmdv.Rs = Rs;
if Fs/afdmdv.Rs != floor(Fs/afdmdv.Rs)
printf("\n Oops, Fs/Rs must be an integer!\n\n");
return
end
M = afdmdv.M = afdmdv.Fs/afdmdv.Rs;
afdmdv.Nfilter = afdmdv.Nsym*M;
afdmdv.tx_filter_memory = zeros(afdmdv.Nc+1, afdmdv.Nfilter);
excess_bw = 0.5;
afdmdv.gt_alpha5_root = gen_rn_coeffs(excess_bw, 1/Fs, Rs, afdmdv.Nsym, afdmdv.M);
Fcentre = afdmdv.Fcentre = 1500;
afdmdv.Fsep = afdmdv.Rs*(1+excess_bw);
afdmdv.phase_tx = ones(afdmdv.Nc+1,1);
% non linear carrier spacing, combined with clip, helps PAPR a lot!
freq_hz = afdmdv.Fsep*( -Nc*Nd/2 - 0.5 + (1:Nc*Nd).^0.98 );
afdmdv.freq_pol = 2*pi*freq_hz/Fs;
afdmdv.freq = exp(j*afdmdv.freq_pol);
afdmdv.Fcentre = 1500;
afdmdv.fbb_rect = exp(j*2*pi*Fcentre/Fs);
afdmdv.fbb_phase_tx = 1;
afdmdv.fbb_phase_rx = 1;
afdmdv.Nrxdec = 31;
afdmdv.rxdec_coeff = fir1(afdmdv.Nrxdec-1, 0.25)';
afdmdv.rxdec_lpf_mem = zeros(1,afdmdv.Nrxdec-1+afdmdv.M);
P = afdmdv.P = 4;
afdmdv.phase_rx = ones(afdmdv.Nc+1,1);
afdmdv.Nfilter = afdmdv.Nsym*afdmdv.M;
afdmdv.rx_fdm_mem = zeros(1,afdmdv.Nfilter + afdmdv.M);
Q = afdmdv.Q = afdmdv.M/4;
if Q != floor(Q)
printf("\n Yeah .... if (Fs/Rs)/4 = M/4 isn't an integer we will just go and break things.\n\n");
end
afdmdv.rx_filter_mem_timing = zeros(afdmdv.Nc+1, afdmdv.Nt*afdmdv.P);
afdmdv.Nfiltertiming = afdmdv.M + afdmdv.Nfilter + afdmdv.M;
afdmdv.rx_filter_memory = zeros(afdmdv.Nc+1, afdmdv.Nfilter);
afdmdv.filt = 0;
afdmdv.prev_rx_symb = ones(1,afdmdv.Nc+1);
% COHPSK Init --------------------------------------------------------
acohpsk = standard_init();
acohpsk.framesize = framesize;
acohpsk.ldpc_code = 0;
acohpsk.ldpc_code_rate = 1;
acohpsk.Nc = Nc;
acohpsk.Rs = Rs;
acohpsk.Ns = 4;
acohpsk.coh_en = 1;
acohpsk.Nd = Nd;
acohpsk.modulation = 'qpsk';
acohpsk.do_write_pilot_file = 0; % enable this to dump pilot symbols to C .h file, e.g. if frame params change
acohpsk = symbol_rate_init(acohpsk);
acohpsk.Ndft = 1024;
acohpsk.f_est = afdmdv.Fcentre;
ch_fdm_frame_buf = zeros(1, Nsw*acohpsk.Nsymbrowpilot*afdmdv.M);
% -----------------------------------------------------------
tx_bits_log = [];
tx_symb_log = [];
rx_amp_log = [];
rx_phi_log = [];
ch_symb_log = [];
rx_symb_log = [];
rx_bits_log = [];
tx_bits_prev_log = [];
uvnoise_log = [];
nerr_log = [];
tx_baseband_log = [];
tx_fdm_frame_log = [];
ch_fdm_frame_log = [];
rx_fdm_frame_bb_log = [];
rx_filt_log = [];
rx_fdm_filter_log = [];
rx_baseband_log = [];
rx_fdm_frame_log = [];
ct_symb_ff_log = [];
rx_timing_log = [];
ratio_log = [];
foff_log = [];
f_est_log = [];
sig_rms_log = [];
noise_rms_log = [];
noise_rms_filt_log = [];
% Channel modeling and BER measurement ----------------------------------------
rand('state',1);
tx_bits_coh = round(rand(1,framesize*10));
ptx_bits_coh = 1;
Nerrs = Tbits = 0;
prev_tx_bits = prev_tx_bits2 = [];
error_positions_hist = zeros(1,framesize);
phase_ch = 1;
sync = initial_sync;
acohpsk.f_est = Fcentre;
acohpsk.f_fine_est = 0;
acohpsk.ct = 4;
acohpsk.ftrack_en = ftrack_en;
if fading_en
[spread spread_2ms hf_gain] = init_hf_model(Fs, frames*acohpsk.Nsymbrowpilot*afdmdv.M);
hf_n = 1;
nhfdelay = floor(hf_delay_ms*Fs/1000);
ch_fdm_delay = zeros(1, acohpsk.Nsymbrowpilot*M + nhfdelay);
end
% simulated SSB tx filter
[b, a] = cheby1(4, 3, [600, 2600]/(Fs/2));
[y filt_states] = filter(b,a,0);
h = freqz(b,a,(600:2600)/(Fs/(2*pi)));
filt_gain = (2600-600)/sum(abs(h) .^ 2); % ensures power after filter == before filter
noise_rms_filt = 0;
% main loop --------------------------------------------------------------------
% run mod and channel as aseparate loop so we can resample to simulate sample rate differences
for f=1:frames
tx_bits = tx_bits_coh(ptx_bits_coh:ptx_bits_coh+framesize-1);
ptx_bits_coh += framesize;
if ptx_bits_coh > length(tx_bits_coh)
ptx_bits_coh = 1;
end
tx_bits_log = [tx_bits_log tx_bits];
[tx_symb tx_bits] = bits_to_qpsk_symbols(acohpsk, tx_bits, []);
tx_symb_log = [tx_symb_log; tx_symb];
tx_fdm_frame = [];
for r=1:acohpsk.Nsymbrowpilot
tx_onesymb = tx_symb(r,:);
[tx_baseband afdmdv] = tx_filter(afdmdv, tx_onesymb);
tx_baseband_log = [tx_baseband_log tx_baseband];
[tx_fdm afdmdv] = fdm_upconvert(afdmdv, tx_baseband);
tx_fdm_frame = [tx_fdm_frame tx_fdm];
end
% clipping, which along with non-linear carrier spacing, improves PAPR
% The value of clip is a function of Nc and is adjusted experimentally
% such that the BER hit over no clipping at Es/No=8dB is small.
ind = find(abs(tx_fdm_frame) > clip);
tx_fdm_frame(ind) = clip*exp(j*angle(tx_fdm_frame(ind)));
tx_fdm_frame_log = [tx_fdm_frame_log tx_fdm_frame];
%
% Channel --------------------------------------------------------------------
%
% simulate tx SSB filter with ripple
if ssb_tx_filt
[tx_fdm_frame filt_states] = filter(b,a,sqrt(filt_gain)*tx_fdm_frame, filt_states);
end
% frequency offset and frequency drift
ch_fdm_frame = zeros(1,acohpsk.Nsymbrowpilot*M);
for i=1:acohpsk.Nsymbrowpilot*M
foff_rect = exp(j*2*pi*foff/Fs);
foff += dfoff;
phase_ch *= foff_rect;
ch_fdm_frame(i) = tx_fdm_frame(i) * phase_ch;
end
foff_log = [foff_log foff];
phase_ch /= abs(phase_ch);
% optional fading
if fading_en
ch_fdm_delay(1:nhfdelay) = ch_fdm_delay(acohpsk.Nsymbrowpilot*M+1:nhfdelay+acohpsk.Nsymbrowpilot*M);
ch_fdm_delay(nhfdelay+1:nhfdelay+acohpsk.Nsymbrowpilot*M) = ch_fdm_frame;
for i=1:acohpsk.Nsymbrowpilot*M
ahf_model = hf_gain*(spread(hf_n)*ch_fdm_frame(i) + spread_2ms(hf_n)*ch_fdm_delay(i));
ch_fdm_frame(i) = ahf_model;
hf_n++;
end
end
% each carrier has power = 2, total power 2Nc, total symbol rate NcRs, noise BW B=Fs
% Es/No = (C/Rs)/(N/B), N = var = 2NcFs/NcRs(Es/No) = 2Fs/Rs(Es/No)
variance = 2*Fs/(acohpsk.Rs*EsNo);
uvnoise = sqrt(0.5)*(randn(1,acohpsk.Nsymbrowpilot*M) + j*randn(1,acohpsk.Nsymbrowpilot*M));
uvnoise_log = [uvnoise_log uvnoise];
noise = sqrt(variance)*uvnoise;
ch_fdm_frame += noise;
ch_fdm_frame_log = [ch_fdm_frame_log ch_fdm_frame];
end
% simulate difference in sample clocks
tin=1;
tout=1;
ch_fdm_frame_log_out = zeros(1,length(ch_fdm_frame_log));
while tin < length(ch_fdm_frame_log)
t1 = floor(tin);
t2 = ceil(tin);
f = tin - t1;
ch_fdm_frame_log_out(tout) = (1-f)*ch_fdm_frame_log(t1) + f*ch_fdm_frame_log(t2);
tout += 1;
tin += 1+sample_rate_ppm/1E6;
end
ch_fdm_frame_log = ch_fdm_frame_log_out(1:tout-1);
% Now run demod ----------------------------------------------------------------
ch_fdm_frame_log_index = 1;
nin = M;
f = 0;
nin_frame = acohpsk.Nsymbrowpilot*M;
%while (ch_fdm_frame_log_index + acohpsk.Nsymbrowpilot*M+M/P) < length(ch_fdm_frame_log)
for f=1:frames;
acohpsk.frame = f;
ch_fdm_frame = ch_fdm_frame_log(ch_fdm_frame_log_index:ch_fdm_frame_log_index + nin_frame - 1);
ch_fdm_frame_log_index += nin_frame;
%
% Demod ----------------------------------------------------------------------
%
% store two frames of received samples so we can rewind if we get a good candidate
ch_fdm_frame_buf(1:Nsw*acohpsk.Nsymbrowpilot*M-nin_frame) = ch_fdm_frame_buf(nin_frame+1:Nsw*acohpsk.Nsymbrowpilot*M);
ch_fdm_frame_buf(Nsw*acohpsk.Nsymbrowpilot*M-nin_frame+1:Nsw*acohpsk.Nsymbrowpilot*M) = ch_fdm_frame;
next_sync = sync;
% if out of sync do Initial Freq offset estimation over NSW frames to flush out memories
if (sync == 0)
% we can test +/- 20Hz, so we break this up into 3 tests to cover +/- 60Hz
max_ratio = 0;
for acohpsk.f_est = Fcentre-40:40:Fcentre+40
printf(" [%d] acohpsk.f_est: %f +/- 20\n", f, acohpsk.f_est);
% we are out of sync so reset f_est and process two frames to clean out memories
[ch_symb rx_timing rx_filt rx_baseband afdmdv acohpsk.f_est] = rate_Fs_rx_processing(afdmdv, ch_fdm_frame_buf, acohpsk.f_est, Nsw*acohpsk.Nsymbrowpilot, nin, 0);
rx_baseband_log = [rx_baseband_log rx_baseband];
rx_filt_log = [rx_filt_log rx_filt];
ch_symb_log = [ch_symb_log; ch_symb];
rx_timing_log = [rx_timing_log rx_timing];
for i=1:Nsw-1
acohpsk.ct_symb_buf = update_ct_symb_buf(acohpsk.ct_symb_buf, ch_symb((i-1)*acohpsk.Nsymbrowpilot+1:i*acohpsk.Nsymbrowpilot,:), acohpsk.Nct_sym_buf, acohpsk.Nsymbrowpilot);
end
[anext_sync acohpsk] = frame_sync_fine_freq_est(acohpsk, ch_symb((Nsw-1)*acohpsk.Nsymbrowpilot+1:Nsw*acohpsk.Nsymbrowpilot,:), sync, next_sync);
if anext_sync == 1
%printf(" [%d] acohpsk.ratio: %f\n", f, acohpsk.ratio);
if acohpsk.ratio > max_ratio
max_ratio = acohpsk.ratio;
f_est = acohpsk.f_est - acohpsk.f_fine_est;
next_sync = anext_sync;
end
end
end
if next_sync == 1
% we've found a sync candidate!
% re-process last two frames with adjusted f_est then check again
acohpsk.f_est = f_est;
printf(" [%d] trying sync and f_est: %f\n", f, acohpsk.f_est);
[ch_symb rx_timing rx_filt rx_baseband afdmdv f_est] = rate_Fs_rx_processing(afdmdv, ch_fdm_frame_buf, acohpsk.f_est, Nsw*acohpsk.Nsymbrowpilot, nin, 0);
rx_baseband_log = [rx_baseband_log rx_baseband];
rx_filt_log = [rx_filt_log rx_filt];
ch_symb_log = [ch_symb_log; ch_symb];
rx_timing_log = [rx_timing_log rx_timing];
for i=1:Nsw-1
acohpsk.ct_symb_buf = update_ct_symb_buf(acohpsk.ct_symb_buf, ch_symb((i-1)*acohpsk.Nsymbrowpilot+1:i*acohpsk.Nsymbrowpilot,:), acohpsk.Nct_sym_buf, acohpsk.Nsymbrowpilot);
end
[next_sync acohpsk] = frame_sync_fine_freq_est(acohpsk, ch_symb((Nsw-1)*acohpsk.Nsymbrowpilot+1:Nsw*acohpsk.Nsymbrowpilot,:), sync, next_sync);
if abs(acohpsk.f_fine_est) > 2
printf(" [%d] Hmm %f is a bit big so back to coarse est ...\n", f, acohpsk.f_fine_est);
next_sync = 0;
end
if acohpsk.ratio < 0.9
next_sync = 0;
end
if next_sync == 1
% OK we are in sync!
% demodulate first frame (demod completed below)
printf(" [%d] in sync! f_est: %f ratio: %f \n", f, f_est, acohpsk.ratio);
acohpsk.ct_symb_ff_buf(1:acohpsk.Nsymbrowpilot+2,:) = acohpsk.ct_symb_buf(acohpsk.ct+1:acohpsk.ct+acohpsk.Nsymbrowpilot+2,:);
end
end
end
% If in sync just do sample rate processing on latest frame
if sync == 1
[ch_symb rx_timing rx_filt rx_baseband afdmdv acohpsk.f_est] = rate_Fs_rx_processing(afdmdv, ch_fdm_frame, acohpsk.f_est, acohpsk.Nsymbrowpilot, nin, acohpsk.ftrack_en);
[next_sync acohpsk] = frame_sync_fine_freq_est(acohpsk, ch_symb, sync, next_sync);
acohpsk.ct_symb_ff_buf(1:2,:) = acohpsk.ct_symb_ff_buf(acohpsk.Nsymbrowpilot+1:acohpsk.Nsymbrowpilot+2,:);
acohpsk.ct_symb_ff_buf(3:acohpsk.Nsymbrowpilot+2,:) = acohpsk.ct_symb_buf(acohpsk.ct+3:acohpsk.ct+acohpsk.Nsymbrowpilot+2,:);
rx_baseband_log = [rx_baseband_log rx_baseband];
rx_filt_log = [rx_filt_log rx_filt];
ch_symb_log = [ch_symb_log; ch_symb];
rx_timing_log = [rx_timing_log rx_timing];
f_est_log = [f_est_log acohpsk.f_est];
end
% if we are in sync complete demodulation with symbol rate processing
if (next_sync == 1) || (sync == 1)
[rx_symb rx_bits rx_symb_linear amp_ phi_ sig_rms noise_rms] = qpsk_symbols_to_bits(acohpsk, acohpsk.ct_symb_ff_buf);
rx_symb_log = [rx_symb_log; rx_symb];
rx_amp_log = [rx_amp_log; amp_];
rx_phi_log = [rx_phi_log; phi_];
rx_bits_log = [rx_bits_log rx_bits];
tx_bits_prev_log = [tx_bits_prev_log prev_tx_bits2];
ratio_log = [ratio_log acohpsk.ratio];
ct_symb_ff_log = [ct_symb_ff_log; acohpsk.ct_symb_ff_buf(1:acohpsk.Nsymbrowpilot,:)];
sig_rms_log = [sig_rms_log sig_rms];
noise_rms_log = [noise_rms_log noise_rms];
noise_rms_filt = 0.9*noise_rms_filt + 0.1*noise_rms;
noise_rms_filt_log = [noise_rms_filt_log noise_rms_filt];
% BER stats
if f > 2
error_positions = xor(tx_bits_log((f-3)*framesize+1:(f-2)*framesize), rx_bits);
Nerrs += sum(error_positions);
nerr_log = [nerr_log sum(error_positions)];
Tbits += length(error_positions);
error_positions_hist += error_positions;
end
printf("\r [%d]", f);
end
% reset BER stats if we lose sync
if sync == 1
%Nerrs = 0;
%Tbits = 0;
%nerr_log = [];
end
[sync acohpsk] = sync_state_machine(acohpsk, sync, next_sync);
% work out how many samples we need for next time
nin = M;
if sync == 1
if rx_timing(length(rx_timing)) > M/P
nin = M + M/P;
end
if rx_timing(length(rx_timing)) < -M/P
nin = M - M/P;
end
end
nin_frame = (acohpsk.Nsymbrowpilot-1)*M + nin;
prev_tx_bits2 = prev_tx_bits;
prev_tx_bits = tx_bits;
end
ber = Nerrs/Tbits;
printf("\nOctave EsNodB: %4.1f ber..: %4.3f Nerrs..: %d Tbits..: %d\n", EsNodB, ber, Nerrs, Tbits);
if compare_with_c
% Output vectors from C port ---------------------------------------------------
load tcohpsk_out.txt
% Determine bit error rate
sz = length(rx_bits_log_c);
Nerrs_c = sum(xor(tx_bits_log(1:sz-framesize), rx_bits_log_c(framesize+1:sz)));
Tbits_c = length(tx_bits_prev_log);
ber_c = Nerrs_c/Tbits_c;
printf("C EsNodB.....: %4.1f ber_c: %4.3f Nerrs_c: %d Tbits_c: %d\n", EsNodB, ber_c, Nerrs_c, Tbits_c);
stem_sig_and_error(1, 111, tx_bits_log_c, tx_bits_log - tx_bits_log_c, 'tx bits', [1 length(tx_bits_log) -1.5 1.5])
stem_sig_and_error(2, 211, real(tx_symb_log_c), real(tx_symb_log - tx_symb_log_c), 'tx symb re', [1 length(tx_symb_log_c) -1.5 1.5])
stem_sig_and_error(2, 212, imag(tx_symb_log_c), imag(tx_symb_log - tx_symb_log_c), 'tx symb im', [1 length(tx_symb_log_c) -1.5 1.5])
stem_sig_and_error(3, 211, real(tx_fdm_frame_log_c), real(tx_fdm_frame_log - tx_fdm_frame_log_c), 'tx fdm frame re', [1 length(tx_fdm_frame_log) -10 10])
stem_sig_and_error(3, 212, imag(tx_fdm_frame_log_c), imag(tx_fdm_frame_log - tx_fdm_frame_log_c), 'tx fdm frame im', [1 length(tx_fdm_frame_log) -10 10])
stem_sig_and_error(4, 211, real(ch_fdm_frame_log_c), real(ch_fdm_frame_log - ch_fdm_frame_log_c), 'ch fdm frame re', [1 length(ch_fdm_frame_log) -10 10])
stem_sig_and_error(4, 212, imag(ch_fdm_frame_log_c), imag(ch_fdm_frame_log - ch_fdm_frame_log_c), 'ch fdm frame im', [1 length(ch_fdm_frame_log) -10 10])
c = 1;
stem_sig_and_error(5, 211, real(rx_baseband_log_c(c,:)), real(rx_baseband_log(c,:) - rx_baseband_log_c(c,:)), 'rx baseband re', [1 length(rx_baseband_log) -10 10])
stem_sig_and_error(5, 212, imag(rx_baseband_log_c(c,:)), imag(rx_baseband_log(c,:) - rx_baseband_log_c(c,:)), 'rx baseband im', [1 length(rx_baseband_log) -10 10])
stem_sig_and_error(6, 211, real(rx_filt_log_c(c,:)), real(rx_filt_log(c,:) - rx_filt_log_c(c,:)), 'rx filt re', [1 length(rx_filt_log) -1 1])
stem_sig_and_error(6, 212, imag(rx_filt_log_c(c,:)), imag(rx_filt_log(c,:) - rx_filt_log_c(c,:)), 'rx filt im', [1 length(rx_filt_log) -1 1])
[n m] = size(ch_symb_log);
stem_sig_and_error(7, 211, real(ch_symb_log_c), real(ch_symb_log - ch_symb_log_c), 'ch symb re', [1 n -1.5 1.5])
stem_sig_and_error(7, 212, imag(ch_symb_log_c), imag(ch_symb_log - ch_symb_log_c), 'ch symb im', [1 n -1.5 1.5])
[n m] = size(rx_symb_log);
stem_sig_and_error(8, 211, rx_amp_log_c, rx_amp_log - rx_amp_log_c, 'Amp Est', [1 n -1.5 1.5])
phi_log_diff = rx_phi_log - rx_phi_log_c;
phi_log_diff(find(phi_log_diff > pi)) -= 2*pi;
phi_log_diff(find(phi_log_diff < -pi)) += 2*pi;
stem_sig_and_error(8, 212, rx_phi_log_c, phi_log_diff, 'Phase Est', [1 n -4 4])
stem_sig_and_error(9, 211, real(rx_symb_log_c), real(rx_symb_log - rx_symb_log_c), 'rx symb re', [1 n -1.5 1.5])
stem_sig_and_error(9, 212, imag(rx_symb_log_c), imag(rx_symb_log - rx_symb_log_c), 'rx symb im', [1 n -1.5 1.5])
stem_sig_and_error(10, 111, rx_bits_log_c, rx_bits_log - rx_bits_log_c, 'rx bits', [1 length(rx_bits_log) -1.5 1.5])
stem_sig_and_error(11, 111, f_est_log_c - Fcentre - foff, f_est_log - f_est_log_c, 'f est', [1 length(f_est_log) -5 5])
stem_sig_and_error(12, 111, rx_timing_log_c, rx_timing_log_c - rx_timing_log, 'rx timing', [1 length(rx_timing_log) -M M])
check(tx_bits_log, tx_bits_log_c, 'tx_bits');
check(tx_symb_log, tx_symb_log_c, 'tx_symb');
check(tx_fdm_frame_log, tx_fdm_frame_log_c, 'tx_fdm_frame',0.01);
check(ch_fdm_frame_log, ch_fdm_frame_log_c, 'ch_fdm_frame',0.01);
check(ch_symb_log, ch_symb_log_c, 'ch_symb',0.05);
check(rx_amp_log, rx_amp_log_c, 'rx_amp_log',0.01);
check(phi_log_diff, zeros(length(phi_log_diff), Nc*Nd), 'rx_phi_log',0.1);
check(rx_symb_log, rx_symb_log_c, 'rx_symb',0.01);
check(rx_timing_log, rx_timing_log_c, 'rx_timing',0.005);
check(rx_bits_log, rx_bits_log_c, 'rx_bits');
check(f_est_log, f_est_log_c, 'f_est');
check(sig_rms_log, sig_rms_log_c, 'sig_rms');
check(noise_rms_log, noise_rms_log_c, 'noise_rms');
printf("\npasses: %d fails: %d\n", passes, fails);
else
papr = max(tx_fdm_frame_log.*conj(tx_fdm_frame_log)) / mean(tx_fdm_frame_log.*conj(tx_fdm_frame_log));
papr_dB = 10*log10(papr);
printf("av tx pwr: %4.2f PAPR: %4.2f av rx pwr: %4.2f\n", var(tx_fdm_frame_log), papr_dB, var(ch_fdm_frame_log));
% some other useful plots
f = figure(1)
clf
subplot(211)
plot(real(tx_fdm_frame_log))
title('tx fdm real');
subplot(212)
plot(imag(tx_fdm_frame_log))
title('tx fdm imag');
f = figure(2)
clf
spec = 20*log10(abs(fft(tx_fdm_frame_log)));
l = length(spec);
plot((Fs/l)*(1:l), spec)
axis([1 Fs/2 0 max(spec)]);
title('tx spectrum');
ylabel('Amplitude (dB)')
xlabel('Frequency (Hz)')
grid;
f = figure(3)
clf;
% plot combined signals to show diversity gains
combined = rx_symb_log(:,1:Nc);
for d=2:Nd
combined += rx_symb_log(:, (d-1)*Nc+1:d*Nc);
end
plot(combined*exp(j*pi/4)/sqrt(Nd),'+')
title('Scatter');
ymax = abs(max(max(combined)));
axis([-ymax ymax -ymax ymax])
f = figure(4)
clf;
subplot(211)
plot(rx_phi_log)
subplot(212)
plot(rx_amp_log)
f = figure(5)
clf;
subplot(211)
plot(rx_timing_log)
title('rx timing');
subplot(212)
stem(ratio_log)
title('Sync ratio');
f = figure(6)
clf;
subplot(211)
stem(nerr_log)
title('Bit Errors');
subplot(212)
plot(noise_rms_filt_log,'r', sig_rms_log,'g');
title('Est rms signal and noise')
f = figure(7);
clf;
subplot(211)
plot(foff_log,';freq offset;');
hold on;
plot(f_est_log - Fcentre,'g;freq offset est;');
hold off;
title('freq offset');
legend("boxoff");
subplot(212)
plot(foff_log(1:length(f_est_log)) - f_est_log + Fcentre)
title('freq offset estimation error');
f = figure(8)
clf
h = freqz(b,a,Fs/2);
plot(20*log10(abs(h)))
axis([1 Fs/2 -20 0])
grid
title('SSB tx filter')
f = figure(9)
clf
plot(error_positions_hist)
title('histogram of bit errors')
end
% function to write C header file of noise samples so C version gives
% exactly the same results
function write_noise_file(uvnoise_log)
m = length(uvnoise_log);
filename = sprintf("../unittest/noise_samples.h");
f=fopen(filename,"wt");
fprintf(f,"/* unit variance complex noise samples */\n\n");
fprintf(f,"/* Generated by write_noise_file() Octave function */\n\n");
fprintf(f,"COMP noise[]={\n");
for r=1:m
if r < m
fprintf(f, " {%f,%f},\n", real(uvnoise_log(r)), imag(uvnoise_log(r)));
else
fprintf(f, " {%f,%f}\n};", real(uvnoise_log(r)), imag(uvnoise_log(r)));
end
end
fclose(f);
endfunction
% function to write float fading samples for use by C programs
%function write_noise_file(raw_file_name, Fs, dopplerSpreadHz, len_samples)
% spread = doppler_spread(dopplerSpreadHz, Fs, len_samples);
% spread_2ms = doppler_spread(dopplerSpreadHz, Fs, len_samples);
% hf_gain = 1.0/sqrt(var(spread)+var(spread_2ms));
%
% % interleave real imag samples
%
% inter = zeros(1,len_samples*4);
% inter(1:4) = hf_gain;
% for i=1:len_samples
% inter(i*4+1) = real(spread(i));
% inter(i*4+2) = imag(spread(i));
% inter(i*4+3) = real(spread_2ms(i));
% inter(i*4+4) = imag(spread_2ms(i));
% end
% f = fopen(raw_file_name,"wb");
% fwrite(f, inter, "float32");
% fclose(f);
%endfunction