You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1360 lines
43 KiB
1360 lines
43 KiB
% ofdm_dev.m
|
|
% David Rowe April 2017
|
|
%
|
|
% Simulations used for development and testing of Rate Fs BPSK/QPSK
|
|
% OFDM modem.
|
|
|
|
ofdm_lib;
|
|
gp_interleaver;
|
|
ldpc;
|
|
|
|
#{
|
|
TODO:
|
|
[ ] run_sim neeeds to be refactored for coded operation at Nc=17 with UW
|
|
#}
|
|
|
|
function [sim_out rx states] = run_sim(sim_in)
|
|
|
|
% set up core modem constants
|
|
|
|
states = ofdm_init(sim_in.bps, sim_in.Rs, sim_in.Tcp, sim_in.Ns, sim_in.Nc);
|
|
ofdm_load_const;
|
|
Nbitspervocframe = 28;
|
|
|
|
% simulation parameters and flags
|
|
|
|
woffset = 2*pi*sim_in.foff_hz/Fs;
|
|
dwoffset = 0;
|
|
if isfield(sim_in, "dfoff_hz_per_sec")
|
|
dwoffset = 2*pi*sim_in.dfoff_hz_per_sec/(Fs*Fs);
|
|
end
|
|
EbNodB = sim_in.EbNodB;
|
|
verbose = states.verbose = sim_in.verbose;
|
|
hf_en = sim_in.hf_en;
|
|
|
|
timing_en = states.timing_en = sim_in.timing_en;
|
|
states.foff_est_en = foff_est_en = sim_in.foff_est_en;
|
|
states.phase_est_en = phase_est_en = sim_in.phase_est_en;
|
|
if hf_en
|
|
assert(phase_est_en == 1, "\nNo point running HF simulation without phase est!!\n");
|
|
end
|
|
if isfield(sim_in, "high_doppler")
|
|
states.high_doppler = sim_in.high_doppler;
|
|
end
|
|
if isfield(sim_in, "diversity_en")
|
|
diversity_en = sim_in.diversity_en;
|
|
else
|
|
diversity_en = 0;
|
|
end
|
|
|
|
if verbose == 2
|
|
printf("Rs:..........: %4.2f\n", Rs);
|
|
printf("M:...........: %d\n", M);
|
|
printf("Ncp:.........: %d\n", Ncp);
|
|
printf("bps:.........: %d\n", bps);
|
|
printf("Nbitsperframe: %d\n", Nbitsperframe);
|
|
printf("Nrowsperframe: %d\n", Nrowsperframe);
|
|
printf("Nsamperframe.: %d\n", Nsamperframe);
|
|
end
|
|
|
|
% Important to define run time in seconds so HF model will evolve the same way
|
|
% for different pilot insertion rates. So lets work backwards from approx
|
|
% seconds in run to get Nbits, the total number of payload data bits
|
|
|
|
Nrows = sim_in.Nsec*Rs;
|
|
Nframes = floor((Nrows-1)/Ns);
|
|
|
|
% if we are interleaving over multiple frames, adjust Nframes so we have an integer number
|
|
% of interleaver frames in simulation
|
|
|
|
interleave_en = 0;
|
|
if isfield(sim_in, "interleave_frames")
|
|
interleave_frames = sim_in.interleave_frames;
|
|
Nframes = interleave_frames*round(Nframes/interleave_frames);
|
|
interleave_en = 1;
|
|
end
|
|
|
|
Nbits = Nframes * Nbitsperframe; % number of payload data bits
|
|
|
|
Nr = Nbits/(Nc*bps); % Number of data rows to get Nbits total
|
|
|
|
% double check if Nbits fit neatly into carriers
|
|
|
|
assert(Nbits/(Nc*bps) == floor(Nbits/(Nc*bps)), "Nbits/(Nc*bps) must be an integer");
|
|
|
|
Nrp = Nr + Nframes + 1; % number of rows once pilots inserted
|
|
% extra row of pilots at end
|
|
|
|
if verbose == 2
|
|
printf("Nc...........: %d\n", Nc);
|
|
printf("Ns...........: %d (step size for pilots, Ns-1 data symbols between pilots)\n", Ns);
|
|
printf("Nr...........: %d\n", Nr);
|
|
printf("Nbits........: %d\n", Nbits);
|
|
printf("Nframes......: %d\n", Nframes);
|
|
if interleave_en
|
|
printf("Interleave fr: %d\n", interleave_frames);
|
|
end
|
|
printf("Nrp..........: %d (number of rows including pilots)\n", Nrp);
|
|
end
|
|
|
|
% Optional LPDC code -----------------------------------------------
|
|
|
|
ldpc_en = states.ldpc_en = sim_in.ldpc_en;
|
|
if sim_in.ldpc_en
|
|
assert(bps == 2, "Only QPSK supported for LDPC so far.....");
|
|
HRA = sim_in.ldpc_code;
|
|
[aNr aNc] = size(HRA);
|
|
rate = states.rate = (aNc-aNr)/aNc;
|
|
Ndatabitsperframe = Nbitsperframe;
|
|
assert(aNc == Ndatabitsperframe, "Dude: Num cols of LDPC HRA must == Nbitsperframe");
|
|
[H_rows, H_cols] = Mat2Hrows(HRA);
|
|
code_param.H_rows = H_rows;
|
|
code_param.H_cols = H_cols;
|
|
code_param.P_matrix = [];
|
|
code_param.data_bits_per_frame = length(code_param.H_cols) - length( code_param.P_matrix );
|
|
code_param.code_bits_per_frame = aNc;
|
|
assert(aNr == Ndatabitsperframe*rate);
|
|
|
|
modulation = states.ldpc_modulation = 'QPSK';
|
|
mapping = states.ldpc_mapping = 'gray';
|
|
demod_type = states.ldpc_demod_type = 0;
|
|
decoder_type = states.ldpc_decoder_type = 0;
|
|
max_iterations = states.ldpc_max_iterations = 100;
|
|
|
|
code_param.S_matrix = CreateConstellation( modulation, 4, mapping );
|
|
|
|
states.code_param = code_param;
|
|
elseif diversity_en
|
|
rate = 0.5;
|
|
else
|
|
rate = 1;
|
|
end
|
|
|
|
% set up HF model ---------------------------------------------------------------
|
|
|
|
if hf_en
|
|
|
|
% some typical values, or replace with user supplied
|
|
|
|
dopplerSpreadHz = 1.0; path_delay_ms = 1;
|
|
|
|
if isfield(sim_in, "dopplerSpreadHz")
|
|
dopplerSpreadHz = sim_in.dopplerSpreadHz;
|
|
end
|
|
if isfield(sim_in, "path_delay_ms")
|
|
path_delay_ms = sim_in.path_delay_ms;
|
|
end
|
|
path_delay_samples = path_delay_ms*Fs/1000;
|
|
printf("Doppler Spread: %3.2f Hz Path Delay: %3.2f ms %d samples\n", dopplerSpreadHz, path_delay_ms, path_delay_samples);
|
|
|
|
% generate same fading pattern for every run
|
|
|
|
randn('seed',1);
|
|
|
|
spread1 = doppler_spread(dopplerSpreadHz, Fs, Nrp*(M+Ncp)*1.1);
|
|
spread2 = doppler_spread(dopplerSpreadHz, Fs, Nrp*(M+Ncp)*1.1);
|
|
|
|
% sometimes doppler_spread() doesn't return exactly the number of samples we need
|
|
|
|
assert(length(spread1) >= Nrp*(M+Ncp), "not enough doppler spreading samples");
|
|
assert(length(spread2) >= Nrp*(M+Ncp), "not enough doppler spreading samples");
|
|
end
|
|
|
|
% ------------------------------------------------------------------
|
|
% simulate for each Eb/No point
|
|
% ------------------------------------------------------------------
|
|
|
|
for nn=1:length(EbNodB)
|
|
rand('seed',1);
|
|
randn('seed',1);
|
|
|
|
EsNo = rate * bps * (10 .^ (EbNodB(nn)/10));
|
|
variance = 1/(M*EsNo/2);
|
|
|
|
Nsam = Nrp*(M+Ncp);
|
|
|
|
% generate tx bits, optionaly LDPC encode, and modulate as QPSK symbols
|
|
% note for reasons unknown LdpcEncode() returns garbage if we use > 0.5 rather than round()
|
|
|
|
%tx_data_bits = round(rand(1,Nbits*rate));
|
|
|
|
% std test frame so we can x-check
|
|
|
|
tx_data_bits = create_ldpc_test_frame(states, ldpc_en);
|
|
|
|
tx_bits = []; tx_symbols = [];
|
|
for f=1:Nframes
|
|
st = (f-1)*Nbitsperframe*rate+1; en = st + Nbitsperframe*rate - 1;
|
|
if ldpc_en
|
|
codeword = LdpcEncode(tx_data_bits(st:en), code_param.H_rows, code_param.P_matrix);
|
|
elseif diversity_en
|
|
% Nc carriers, so Nc*bps bits/row coded, or Nc*bps*rate data bits that we repeat
|
|
codeword = [];
|
|
for rr=1:Ns-1
|
|
st1 = st + (rr-1)*Nc*bps*rate; en1 = st1 + Nc*bps*rate - 1;
|
|
codeword = [codeword tx_data_bits(st1:en1) tx_data_bits(st1:en1)];
|
|
end
|
|
assert(length(codeword) == Nbitsperframe);
|
|
else
|
|
% uncoded mode
|
|
codeword = tx_data_bits;
|
|
if isfield(sim_in, "uw_debug")
|
|
codeword(states.uw_ind) = states.tx_uw;
|
|
end
|
|
end
|
|
tx_bits = [tx_bits codeword];
|
|
for b=1:2:Nbitsperframe
|
|
tx_symbols = [tx_symbols qpsk_mod(codeword(b:b+1))];
|
|
end
|
|
end
|
|
|
|
% optional interleaving over multiple frames
|
|
|
|
if interleave_en
|
|
for f=1:interleave_frames:Nframes
|
|
st = (f-1)*Nbitsperframe/bps+1; en = st + Nbitsperframe*interleave_frames/bps - 1;
|
|
tx_symbols(st:en) = gp_interleave(tx_symbols(st:en));
|
|
end
|
|
end
|
|
|
|
% OFDM transmitter
|
|
|
|
tx = [];
|
|
for f=1:Nframes
|
|
st = (f-1)*Nbitsperframe/bps+1; en = st + Nbitsperframe/bps - 1;
|
|
tx = [tx ofdm_txframe(states, tx_symbols(st:en))];
|
|
end
|
|
|
|
% add extra row of pilots at end, to allow one frame simulations,
|
|
% useful for development
|
|
|
|
st = Nsamperframe*(Nframes-1)+1; en = st+Ncp+M-1;
|
|
tx = [tx tx(st:en)];
|
|
assert(length(tx) == Nsam);
|
|
|
|
% channel simulation ---------------------------------------------------------------
|
|
|
|
if isfield(sim_in, "sample_clock_offset_ppm")
|
|
% todo: this only works for large ppm like 500, runs out of memory
|
|
% for small ppm
|
|
|
|
if sim_in.sample_clock_offset_ppm
|
|
timebase = floor(abs(1E6/sim_in.sample_clock_offset_ppm));
|
|
if sim_in.sample_clock_offset_ppm > 0
|
|
tx = resample(tx, timebase+1, timebase);
|
|
else
|
|
tx = resample(tx, timebase, timebase+1);
|
|
end
|
|
|
|
% make sure length is correct for rest of simulation
|
|
|
|
tx = [tx zeros(1,Nsam-length(tx))];
|
|
tx = tx(1:Nsam);
|
|
end
|
|
end
|
|
|
|
rx = tx;
|
|
|
|
if hf_en
|
|
|
|
rx = tx(1:Nsam) .* spread1(1:Nsam);
|
|
rx += [zeros(1,path_delay_samples) tx(1:Nsam-path_delay_samples)] .* spread2(1:Nsam);
|
|
|
|
% normalise rx power to same as tx
|
|
|
|
nom_rx_pwr = 2/(Ns*(M*M)) + Nc/(M*M);
|
|
rx_pwr = var(rx);
|
|
rx *= sqrt(nom_rx_pwr/rx_pwr);
|
|
end
|
|
|
|
phase_offset = woffset*(1:Nsam) + 0.5*dwoffset*((1:Nsam).^2);
|
|
rx = rx .* exp(j*phase_offset);
|
|
|
|
if isfield(sim_in, "initial_noise_sams")
|
|
rx = [zeros(1, sim_in.initial_noise_sams) rx];
|
|
Nsam = length(rx);
|
|
end
|
|
|
|
noise = sqrt(variance)*(0.5*randn(1,Nsam) + j*0.5*randn(1,Nsam));
|
|
snrdB = 10*log10(var(rx)/var(noise)) + 10*log10(8000) - 10*log10(3000);
|
|
rx += noise;
|
|
|
|
% interfering carrier
|
|
|
|
% rx += 0.04*cos((1:length(rx))*states.w(10));
|
|
|
|
% gain
|
|
|
|
rx *= sim_in.gain;
|
|
|
|
% some spare samples at end to avoid overflow as est windows may poke into the future a bit
|
|
|
|
rx = [rx zeros(1,Nsamperframe)];
|
|
|
|
% optional save raw file
|
|
if 1
|
|
sraw = real(rx*5000);
|
|
fraw = fopen("ofdm_dev.raw", "wb");
|
|
fwrite(fraw, sraw, "short");
|
|
fclose(fraw);
|
|
end
|
|
|
|
% bunch of logs
|
|
|
|
phase_est_pilot_log = [];
|
|
delta_t_log = [];
|
|
timing_est_log = [];
|
|
foff_est_hz_log = [];
|
|
Nerrs_log = []; Nerrs_coded_log = [];
|
|
rx_bits = []; rx_np = []; rx_amp = [];
|
|
sig_var_log = []; noise_var_log = [];
|
|
uw_errors_log = [];
|
|
|
|
% reset some states for each EbNo simulation point
|
|
|
|
states.sample_point = states.timing_est = 1;
|
|
if timing_en == 0
|
|
states.sample_point = Ncp;
|
|
end
|
|
states.nin = Nsamperframe;
|
|
states.foff_est_hz = 0;
|
|
|
|
% for this simulation we "prime" buffer to allow one frame runs during development
|
|
|
|
prx = 1;
|
|
states.rxbuf(M+Ncp+2*Nsamperframe+1:Nrxbuf) = rx(prx:Nsamperframe+2*(M+Ncp));
|
|
prx += Nsamperframe+2*(M+Ncp);
|
|
|
|
for f=1:Nframes
|
|
|
|
% insert samples at end of buffer, set to zero if no samples
|
|
% available to disable phase estimation on future pilots on last
|
|
% frame of simulation
|
|
|
|
lnew = min(Nsam-prx,states.nin);
|
|
rxbuf_in = zeros(1,states.nin);
|
|
|
|
if lnew
|
|
rxbuf_in(1:lnew) = rx(prx:prx+lnew-1);
|
|
end
|
|
prx += states.nin;
|
|
|
|
[arx_bits states aphase_est_pilot_log arx_np arx_amp] = ofdm_demod(states, rxbuf_in);
|
|
|
|
rx_bits = [rx_bits arx_bits]; rx_np = [rx_np arx_np]; rx_amp = [rx_amp arx_amp];
|
|
|
|
% note: only supported in ldpc_en = 0 Nc=17 atm, see debug_false_sync()
|
|
|
|
if isfield(sim_in, "uw_debug")
|
|
rx_uw = arx_bits(states.uw_ind);
|
|
uw_errors = sum(xor(states.tx_uw,rx_uw));
|
|
if verbose
|
|
printf("f: %d uw_errors: %d\n", f, uw_errors);
|
|
end
|
|
uw_errors_log = [uw_errors_log uw_errors];
|
|
end
|
|
|
|
timing_est_log = [timing_est_log states.timing_est];
|
|
delta_t_log = [delta_t_log states.delta_t];
|
|
foff_est_hz_log = [foff_est_hz_log states.foff_est_hz];
|
|
phase_est_pilot_log = [phase_est_pilot_log; aphase_est_pilot_log];
|
|
sig_var_log = [sig_var_log states.sig_var];
|
|
noise_var_log = [noise_var_log states.noise_var];
|
|
end
|
|
assert(length(rx_bits) == Nbits);
|
|
|
|
% Optional de-interleave on rx QPSK symbols
|
|
|
|
if interleave_en
|
|
for f=1:interleave_frames:Nframes
|
|
st = (f-1)*Nbitsperframe/bps+1; en = st + Nbitsperframe*interleave_frames/bps - 1;
|
|
rx_np(st:en) = gp_deinterleave(rx_np(st:en));
|
|
rx_amp(st:en) = gp_deinterleave(rx_amp(st:en));
|
|
end
|
|
end
|
|
|
|
% Calculate raw BER/PER stats, after pilots extracted. As we may
|
|
% have used interleaving, we qpsk_demod() here rather than using
|
|
% rx_bits from ofdm_demod()
|
|
|
|
rx_bits = zeros(1, Nbits);
|
|
for s=1:Nbits/bps
|
|
rx_bits(2*(s-1)+1:2*s) = qpsk_demod(rx_np(s));
|
|
end
|
|
|
|
errors = xor(tx_bits, rx_bits);
|
|
Terrs = sum(errors);
|
|
|
|
Tpackets = Tpacketerrs = 0;
|
|
Nvocframes = floor(Nbits/Nbitspervocframe);
|
|
for fv=1:Nvocframes
|
|
st = (fv-1)*Nbitspervocframe + 1;
|
|
en = st + Nbitspervocframe - 1;
|
|
Nvocpacketerrs = sum(xor(tx_bits(st:en), rx_bits(st:en)));
|
|
if Nvocpacketerrs
|
|
Tpacketerrs++;
|
|
end
|
|
Tpackets++;
|
|
end
|
|
|
|
% Per-modem frame error log and optional LDPC/diversity error stats
|
|
|
|
Terrs_coded = 0; Tpackets_coded = 0; Tpacketerrs_coded = 0; sim_out.error_positions = [];
|
|
|
|
for f=1:Nframes
|
|
st = (f-1)*Nbitsperframe+1; en = st + Nbitsperframe - 1;
|
|
Nerrs_log(f) = sum(xor(tx_bits(st:en), rx_bits(st:en)));
|
|
|
|
st = (f-1)*Nbitsperframe/bps + 1;
|
|
en = st + Nbitsperframe/bps - 1;
|
|
r = rx_np(st:en); fade = rx_amp(st:en);
|
|
|
|
% optional LDPC decode
|
|
|
|
if ldpc_en
|
|
|
|
% scale based on amplitude ests
|
|
|
|
mean_amp = states.mean_amp;
|
|
rx_codeword = ldpc_dec(code_param, max_iterations, demod_type, decoder_type, r/mean_amp, min(EsNo,30), fade/mean_amp);
|
|
end
|
|
|
|
% optional diversity demod
|
|
|
|
if diversity_en
|
|
rx_codeword = [];
|
|
for rr=1:Ns-1
|
|
for c=1:Nc/2
|
|
s = (rr-1)*Nc + c;
|
|
rx_codeword = [rx_codeword qpsk_demod(r(s)+r(s+Nc/2))];
|
|
end
|
|
end
|
|
assert(length(rx_codeword) == Nbitsperframe*rate);
|
|
end
|
|
|
|
% running coded BER calcs
|
|
|
|
if ldpc_en || diversity_en
|
|
|
|
st = (f-1)*Nbitsperframe*rate + 1;
|
|
en = st + Nbitsperframe*rate - 1;
|
|
errors = xor(tx_data_bits(st:en), rx_codeword(1:Nbitsperframe*rate));
|
|
Nerrs_coded = sum(errors);
|
|
Nerrs_coded_log(f) = Nerrs_coded;
|
|
Terrs_coded += Nerrs_coded;
|
|
sim_out.error_positions = [sim_out.error_positions errors];
|
|
|
|
% PER based on vocoder packet size, not sure it makes much
|
|
% difference compared to using all bits in LDPC code for
|
|
% packet
|
|
|
|
atx_data_bits = tx_data_bits(st:en);
|
|
Nvocframes = Nbitsperframe*rate/Nbitspervocframe;
|
|
for fv=1:Nvocframes
|
|
st = (fv-1)*Nbitspervocframe + 1;
|
|
en = st + Nbitspervocframe - 1;
|
|
Nvocpacketerrs = sum(xor(atx_data_bits(st:en), rx_codeword(st:en)));
|
|
if Nvocpacketerrs
|
|
Tpacketerrs_coded++;
|
|
end
|
|
Tpackets_coded++;
|
|
end
|
|
end
|
|
end
|
|
|
|
% print results of this simulation point to the console
|
|
|
|
if verbose
|
|
if ldpc_en || diversity_en
|
|
printf("Coded EbNodB: % -4.1f BER: %5.4f Tbits: %5d Terrs: %5d PER: %5.4f Tpackets: %5d Tpacket_errs: %5d\n",
|
|
EbNodB(nn), Terrs_coded/(Nbits*rate), Nbits*rate, Terrs_coded,
|
|
Tpacketerrs_coded/Tpackets_coded, Tpackets_coded, Tpacketerrs_coded);
|
|
end
|
|
EbNodB_raw = EbNodB(nn) + 10*log10(rate);
|
|
printf("Raw EbNodB..: % -4.1f BER: %5.4f Tbits: %5d Terrs: %5d PER: %5.4f Tpackets: %5d Tpacket_errs: %5d\n",
|
|
EbNodB_raw, Terrs/Nbits, Nbits, Terrs,
|
|
Tpacketerrs/Tpackets, Tpackets, Tpacketerrs);
|
|
EsNo = mean(sig_var_log)/mean(noise_var_log);
|
|
%printf("Es/No est dB: % -4.1f\n", 10*log10(EsNo));
|
|
sim_out.snrdB(nn) = snrdB;
|
|
sim_out.snr_estdB(nn) = 10*log10(EsNo) + 10*log10(Nc*Rs/3000);
|
|
|
|
% returns results for plotting curves
|
|
|
|
if ldpc_en || diversity_en
|
|
sim_out.ber(nn) = Terrs_coded/(Nbits*rate);
|
|
sim_out.per(nn) = Tpacketerrs_coded/Tpackets_coded;
|
|
else
|
|
sim_out.ber(nn) = Terrs/Nbits;
|
|
sim_out.per(nn) = Tpacketerrs/Tpackets;
|
|
end
|
|
end
|
|
|
|
sim_out.uw_errors_log = uw_errors_log;
|
|
|
|
% Optional plots, mostly used with run-single
|
|
|
|
if verbose
|
|
|
|
figure(1); clf;
|
|
plot(rx_np,'+');
|
|
%axis([-2 2 -2 2]);
|
|
title('Scatter');
|
|
|
|
figure(2); clf;
|
|
plot(phase_est_pilot_log,'g+', 'markersize', 5);
|
|
title('Phase est');
|
|
axis([1 Nrp -pi pi]);
|
|
|
|
figure(3); clf;
|
|
subplot(211)
|
|
stem(delta_t_log)
|
|
title('delta t');
|
|
subplot(212)
|
|
plot(timing_est_log);
|
|
title('timing est');
|
|
|
|
figure(4); clf;
|
|
plot(foff_est_hz_log)
|
|
axis([1 max(Nframes,2) -3 3]);
|
|
title('Fine Freq');
|
|
|
|
figure(5); clf;
|
|
if ldpc_en
|
|
subplot(211)
|
|
stem(Nerrs_log/Nbitsperframe);
|
|
title("Uncoded BER/frame");
|
|
subplot(212)
|
|
stem(Nerrs_coded_log/(Nbitsperframe*rate));
|
|
title("Coded BER/frame");
|
|
else
|
|
title("BER/frame");
|
|
stem(Nerrs_log/Nbitsperframe);
|
|
end
|
|
|
|
figure(6)
|
|
Tx = abs(fft(rx(1:Nsam).*hanning(Nsam)'));
|
|
Tx_dB = 20*log10(Tx);
|
|
dF = Fs/Nsam;
|
|
plot((1:Nsam)*dF, Tx_dB);
|
|
mx = max(Tx_dB);
|
|
axis([0 Fs/2 mx-60 mx])
|
|
|
|
figure(7); clf;
|
|
plot(10*log10(sig_var_log),'b;Es;');
|
|
hold on;
|
|
plot(10*log10(noise_var_log),'r;No;');
|
|
snr_estdB = 10*log10(sig_var_log) - 10*log10(noise_var_log) + 10*log10(Nc*Rs/3000);
|
|
snr_est_smoothed_dB = filter(0.1,[1 -0.9],snr_estdB);
|
|
plot(snr_estdB,'g;SNR3k;');
|
|
plot(snr_est_smoothed_dB,'c;SNR3k smooth;');
|
|
|
|
hold off;
|
|
title('Signal and Noise Power estimates');
|
|
|
|
#{
|
|
if hf_en
|
|
figure(4); clf;
|
|
subplot(211)
|
|
plot(abs(spread1(1:Nsam)));
|
|
%hold on; plot(abs(spread2(1:Nsam)),'g'); hold off;
|
|
subplot(212)
|
|
plot(angle(spread1(1:Nsam)));
|
|
title('spread1 amp and phase');
|
|
end
|
|
#}
|
|
|
|
#{
|
|
% todo, work out a way to plot rate Fs hf model phase
|
|
if sim_in.hf_en
|
|
plot(angle(hf_model(:,2:Nc+1)));
|
|
end
|
|
#}
|
|
end
|
|
|
|
end
|
|
endfunction
|
|
|
|
|
|
function run_single(EbNodB = 100, error_pattern_filename);
|
|
Ts = 0.018; sim_in.Tcp = 0.002;
|
|
sim_in.Rs = 1/Ts; sim_in.bps = 2; sim_in.Nc = 16; sim_in.Ns = 8;
|
|
sim_in.high_doppler = 0;
|
|
|
|
sim_in.Nsec = (sim_in.Ns+1)/sim_in.Rs; % one frame, make sure sim_in.interleave_frames = 1
|
|
sim_in.Nsec = 120;
|
|
|
|
sim_in.EbNodB = 20;
|
|
sim_in.verbose = 1;
|
|
sim_in.dopplerSpreadHz = 1;
|
|
sim_in.path_delay_ms = 1;
|
|
sim_in.hf_en = 1;
|
|
sim_in.foff_hz = 0;
|
|
sim_in.dfoff_hz_per_sec = 0.00;
|
|
sim_in.sample_clock_offset_ppm = 0;
|
|
sim_in.gain = 1;
|
|
|
|
sim_in.timing_en = 0;
|
|
sim_in.foff_est_en = 0;
|
|
sim_in.phase_est_en = 1;
|
|
|
|
load HRA_112_112.txt
|
|
sim_in.ldpc_code = HRA_112_112;
|
|
sim_in.ldpc_en = 0;
|
|
|
|
sim_in.interleave_frames = 1;
|
|
%sim_in.diversity_en = 1;
|
|
|
|
sim_out = run_sim(sim_in);
|
|
|
|
if nargin == 2
|
|
fep = fopen(error_pattern_filename, "wb");
|
|
fwrite(fep, sim_out.error_positions, "short");
|
|
fclose(fep);
|
|
end
|
|
|
|
end
|
|
|
|
|
|
% Plot BER and PER curves for AWGN and HF:
|
|
%
|
|
% i) BER/PER against Eb/No for various coding schemes
|
|
% ii) BER/PER against Eb/No showing Pilot/CP overhead
|
|
% iii) BER/PER against SNR, with pilot/CP overhead, comparing 700C
|
|
|
|
function run_curves
|
|
|
|
% waveform
|
|
|
|
Ts = 0.018; sim_in.Tcp = 0.002;
|
|
sim_in.Rs = 1/Ts; sim_in.bps = 2; sim_in.Nc = 16; sim_in.Ns = 8;
|
|
|
|
pilot_overhead = (sim_in.Ns-1)/sim_in.Ns;
|
|
cp_overhead = Ts/(Ts+sim_in.Tcp);
|
|
overhead_dB = -10*log10(pilot_overhead*cp_overhead);
|
|
|
|
% simulation parameters
|
|
|
|
sim_in.verbose = 0;
|
|
sim_in.foff_hz = 0;
|
|
sim_in.gain = 1;
|
|
|
|
sim_in.timing_en = 1;
|
|
sim_in.foff_est_en = 1;
|
|
sim_in.phase_est_en = 1;
|
|
load HRA_112_112.txt
|
|
sim_in.ldpc_code = HRA_112_112;
|
|
sim_in.ldpc_en = 0;
|
|
sim_in.hf_en = 0;
|
|
|
|
sim_in.Nsec = 20;
|
|
sim_in.EbNodB = 0:8;
|
|
awgn_EbNodB = sim_in.EbNodB;
|
|
|
|
awgn_theory = 0.5*erfc(sqrt(10.^(sim_in.EbNodB/10)));
|
|
awgn = run_sim(sim_in);
|
|
sim_in.ldpc_en = 1; awgn_ldpc = run_sim(sim_in);
|
|
|
|
% Note for HF sim you really need >= 60 seconds (at Rs-50) to get sensible results c.f. theory
|
|
|
|
sim_in.hf_en = 1; sim_in.ldpc_en = 0;
|
|
sim_in.Nsec = 60;
|
|
sim_in.EbNodB = 4:2:14;
|
|
|
|
EbNoLin = 10.^(sim_in.EbNodB/10);
|
|
hf_theory = 0.5.*(1-sqrt(EbNoLin./(EbNoLin+1)));
|
|
|
|
hf = run_sim(sim_in);
|
|
sim_in.diversity_en = 1; hf_diversity = run_sim(sim_in); sim_in.diversity_en = 0;
|
|
sim_in.ldpc_en = 1; hf_ldpc = run_sim(sim_in);
|
|
|
|
% try a few interleavers
|
|
|
|
sim_in.interleave_frames = 1; hf_ldpc_1 = run_sim(sim_in);
|
|
sim_in.interleave_frames = 8; hf_ldpc_8 = run_sim(sim_in);
|
|
sim_in.interleave_frames = 16; hf_ldpc_16 = run_sim(sim_in);
|
|
sim_in.interleave_frames = 32; hf_ldpc_32 = run_sim(sim_in);
|
|
|
|
% Rate Fs modem BER curves of various coding schemes
|
|
|
|
figure(1); clf;
|
|
semilogy(awgn_EbNodB, awgn_theory,'b+-;AWGN theory;');
|
|
hold on;
|
|
semilogy(sim_in.EbNodB, hf_theory,'b+-;HF theory;');
|
|
semilogy(awgn_EbNodB, awgn.ber,'r+-;AWGN;');
|
|
semilogy(sim_in.EbNodB, hf.ber,'r+-;HF;');
|
|
semilogy(sim_in.EbNodB, hf_diversity.ber,'ro-;HF diversity;');
|
|
semilogy(awgn_EbNodB, awgn_ldpc.ber,'c+-;AWGN LDPC (224,112);');
|
|
semilogy(sim_in.EbNodB, hf_ldpc.ber,'c+-;HF LDPC (224,112);');
|
|
semilogy(sim_in.EbNodB, hf_ldpc_1.ber,'m+-;HF LDPC (224,112) interleave 1;');
|
|
semilogy(sim_in.EbNodB, hf_ldpc_8.ber,'g+-;HF LDPC (224,112) interleave 8;');
|
|
semilogy(sim_in.EbNodB, hf_ldpc_16.ber,'k+-;HF LDPC (224,112) interleave 16;');
|
|
semilogy(sim_in.EbNodB, hf_ldpc_32.ber,'k+-;HF LDPC (224,112) interleave 32;');
|
|
hold off;
|
|
axis([0 14 1E-3 2E-1])
|
|
xlabel('Eb/No (dB)');
|
|
ylabel('BER');
|
|
grid; grid minor on;
|
|
legend('boxoff');
|
|
legend("location", "southwest");
|
|
title('Rate Fs modem BER for various FEC coding schemes');
|
|
print('-deps', '-color', "ofdm_dev_ber_coding.eps")
|
|
|
|
awgn_per_theory = 1 - (1-awgn_theory).^28;
|
|
hf_per_theory = 1 - (1-hf_theory).^28;
|
|
|
|
% Rate Fs modem PER curves of various coding schemes
|
|
|
|
figure(2); clf;
|
|
semilogy(awgn_EbNodB, awgn_per_theory,'b+-;AWGN theory;');
|
|
hold on;
|
|
semilogy(sim_in.EbNodB, hf_per_theory,'b+-;HF theory;');
|
|
semilogy(awgn_EbNodB, awgn.per,'r+-;AWGN;');
|
|
semilogy(sim_in.EbNodB, hf.per,'r+-;HF sim;');
|
|
semilogy(sim_in.EbNodB, hf_diversity.per,'ro-;HF diversity;');
|
|
semilogy(awgn_EbNodB, awgn_ldpc.per,'c+-;AWGN LDPC (224,112);');
|
|
semilogy(sim_in.EbNodB, hf_ldpc.per,'c+-;HF LDPC (224,112);');
|
|
semilogy(sim_in.EbNodB, hf_ldpc_1.per,'m+-;HF LDPC (224,112) interleave 1;');
|
|
semilogy(sim_in.EbNodB, hf_ldpc_8.per,'g+-;HF LDPC (224,112) interleave 8;');
|
|
semilogy(sim_in.EbNodB, hf_ldpc_16.per,'ko-;HF LDPC (224,112) interleave 16;');
|
|
semilogy(sim_in.EbNodB, hf_ldpc_32.per,'k+-;HF LDPC (224,112) interleave 32;');
|
|
hold off;
|
|
axis([0 14 1E-2 1])
|
|
xlabel('Eb/No (dB)');
|
|
ylabel('PER');
|
|
grid; grid minor on;
|
|
legend('boxoff');
|
|
legend("location", "southwest");
|
|
title('Rate Fs modem PER for various FEC coding schemes');
|
|
print('-deps', '-color', "ofdm_dev_per_coding.eps")
|
|
|
|
% Rate Fs modem pilot/CP overhead BER curves
|
|
|
|
figure(3); clf;
|
|
semilogy(awgn_EbNodB, awgn_theory,'b+-;AWGN theory;');
|
|
hold on;
|
|
semilogy(sim_in.EbNodB, hf_theory,'b+-;HF theory;');
|
|
semilogy(awgn_EbNodB+overhead_dB, awgn_theory,'g+-;AWGN lower bound pilot + CP;');
|
|
semilogy(sim_in.EbNodB+overhead_dB, hf_theory,'g+-;HF lower bound pilot + CP;');
|
|
semilogy(awgn_EbNodB+overhead_dB, awgn.ber,'r+-;AWGN sim;');
|
|
semilogy(sim_in.EbNodB+overhead_dB, hf.ber,'r+-;HF sim;');
|
|
hold off;
|
|
axis([0 14 1E-3 2E-1])
|
|
xlabel('Eb/No (dB)');
|
|
ylabel('BER');
|
|
grid; grid minor on;
|
|
legend('boxoff');
|
|
legend("location", "southwest");
|
|
title('Rate Fs modem BER Pilot/Cyclic Prefix overhead');
|
|
print('-deps', '-color', "ofdm_dev_ber_overhead.eps")
|
|
|
|
% Rate Fs modem pilot/CP overhead PER curves
|
|
|
|
figure(4); clf;
|
|
semilogy(awgn_EbNodB, awgn_per_theory,'b+-;AWGN theory;','markersize', 10, 'linewidth', 2);
|
|
hold on;
|
|
semilogy(sim_in.EbNodB, hf_per_theory,'b+-;HF theory;','markersize', 10, 'linewidth', 2);
|
|
semilogy(awgn_EbNodB+overhead_dB, awgn_per_theory,'g+-;AWGN lower bound pilot + CP;','markersize', 10, 'linewidth', 2);
|
|
semilogy(sim_in.EbNodB+overhead_dB, hf_per_theory,'g+-;HF lower bound pilot + CP;','markersize', 10, 'linewidth', 2);
|
|
semilogy(awgn_EbNodB+overhead_dB, awgn.per,'r+-;AWGN sim;','markersize', 10, 'linewidth', 2);
|
|
semilogy(sim_in.EbNodB+overhead_dB, hf.per,'r+-;HF sim;','markersize', 10, 'linewidth', 2);
|
|
hold off;
|
|
axis([0 14 1E-2 1])
|
|
xlabel('Eb/No (dB)');
|
|
ylabel('PER');
|
|
grid; grid minor on;
|
|
legend('boxoff');
|
|
legend("location", "southwest");
|
|
title('Rate Fs modem PER Pilot/Cyclic Prefix overhead');
|
|
print('-deps', '-color', "ofdm_dev_per_overhead.eps")
|
|
|
|
% SNR including pilots, CP, and 700C est
|
|
|
|
snr_awgn_theory = awgn_EbNodB + 10*log10(700/3000);
|
|
snr_hf_theory = sim_in.EbNodB + 10*log10(700/3000);
|
|
snr_awgn = snr_awgn_theory + overhead_dB;
|
|
snr_hf = sim_in.EbNodB + 10*log10(700/3000) + overhead_dB;
|
|
|
|
% est 700C: 2/6 symbols are pilots, 1dB implementation loss
|
|
|
|
snr_awgn_700c = awgn_EbNodB + 10*log10(700/3000) + 10*log10(6/4) + 1;
|
|
snr_hf_700c = sim_in.EbNodB + 10*log10(700/3000) + 10*log10(6/4) + 1;
|
|
|
|
figure(5); clf;
|
|
semilogy(snr_awgn_theory, awgn_theory,'b+-;AWGN theory;','markersize', 10, 'linewidth', 2);
|
|
hold on;
|
|
semilogy(snr_awgn_700c, awgn_theory,'g+-;AWGN 700C;','markersize', 10, 'linewidth', 2);
|
|
semilogy(snr_hf_700c, hf_diversity.ber,'go-;HF 700C;','markersize', 10, 'linewidth', 2);
|
|
semilogy(snr_hf_theory, hf_theory,'b+-;HF theory;','markersize', 10, 'linewidth', 2);
|
|
semilogy(snr_awgn, awgn_ldpc.ber,'c+-;AWGN LDPC (224,112);','markersize', 10, 'linewidth', 2);
|
|
semilogy(snr_hf, hf_ldpc.ber,'c+-;HF LDPC (224,112);','markersize', 10, 'linewidth', 2);
|
|
semilogy(snr_hf, hf_diversity.ber,'bo-;HF diversity;','markersize', 10, 'linewidth', 2);
|
|
semilogy(snr_hf, hf_ldpc_16.ber,'k+-;HF LDPC (224,112) interleave 16;','markersize', 10, 'linewidth', 2);
|
|
hold off;
|
|
axis([-5 8 1E-3 2E-1])
|
|
xlabel('SNR (3000Hz noise BW) (dB)');
|
|
ylabel('BER');
|
|
grid; grid minor on;
|
|
legend('boxoff');
|
|
legend("location", "southwest");
|
|
title('Rate Fs modem BER versus SNR including pilot/CP overhead');
|
|
print('-deps', '-color', "ofdm_dev_ber_snr.eps")
|
|
|
|
end
|
|
|
|
|
|
% Plot BER and PER curves for AWGN and HF with various estimators
|
|
|
|
function run_curves_estimators
|
|
|
|
Nsec_awgn = 20;
|
|
Nsec_hf = 60;
|
|
|
|
% waveform
|
|
|
|
Ts = 0.018; sim_in.Tcp = 0.002;
|
|
sim_in.Rs = 1/Ts; sim_in.bps = 2; sim_in.Nc = 16; sim_in.Ns = 8;
|
|
|
|
% simulation parameters
|
|
|
|
sim_in.verbose = 0; sim_in.foff_hz = 0; sim_in.ldpc_en = 0;
|
|
|
|
sim_in.phase_est_en = 1;
|
|
sim_in.timing_en = sim_in.foff_est_en = 0;
|
|
|
|
% AWGN simulations
|
|
|
|
sim_in.hf_en = 0; sim_in.Nsec = Nsec_awgn; sim_in.EbNodB = 0:2:6;
|
|
sim_in.timing_en = sim_in.foff_est_en = 0;
|
|
|
|
awgn_EbNodB = sim_in.EbNodB;
|
|
awgn_theory = 0.5*erfc(sqrt(10.^(sim_in.EbNodB/10)));
|
|
awgn = run_sim(sim_in);
|
|
sim_in.timing_en = 1; awgn_timing = run_sim(sim_in);
|
|
sim_in.foff_est_en = 1; awgn_foff_est = run_sim(sim_in);
|
|
|
|
sim_in.dfoff_hz_per_sec = 0.02; awgn_dfoff = run_sim(sim_in);
|
|
|
|
% HF simulations
|
|
|
|
sim_in.hf_en = 1; sim_in.Nsec = Nsec_hf; sim_in.EbNodB = 4:2:8;
|
|
sim_in.timing_en = sim_in.foff_est_en = 0;
|
|
|
|
EbNoLin = 10.^(sim_in.EbNodB/10);
|
|
hf_theory = 0.5.*(1-sqrt(EbNoLin./(EbNoLin+1)));
|
|
|
|
hf = run_sim(sim_in);
|
|
sim_in.timing_en = 1; hf_timing = run_sim(sim_in);
|
|
sim_in.timing_en = 0; sim_in.foff_est_en = 1; hf_foff_est = run_sim(sim_in);
|
|
sim_in.timing_en = 1; hf_timing_foff_est = run_sim(sim_in);
|
|
|
|
sim_in.dfoff_hz_per_sec = 0.02; hf_dfoff = run_sim(sim_in); sim_in.dfoff_hz_per_sec = 0.0;
|
|
|
|
figure(1); clf;
|
|
semilogy(awgn_EbNodB, awgn_theory,'b+-;AWGN theory;');
|
|
hold on;
|
|
semilogy(awgn_EbNodB, awgn.ber,'r+-;AWGN phase;');
|
|
semilogy(awgn_EbNodB, awgn_timing.ber,'go-;AWGN phase+timing;');
|
|
semilogy(awgn_EbNodB, awgn_foff_est.ber,'d+-;AWGN phase+timing+foff_est;');
|
|
semilogy(awgn_EbNodB, awgn_dfoff.ber,'m+-;AWGN all + 0.02Hz/s drift;');
|
|
semilogy(sim_in.EbNodB, hf_theory,'b+-;HF theory;');
|
|
semilogy(sim_in.EbNodB, hf.ber,'r+-;HF phase;');
|
|
semilogy(sim_in.EbNodB, hf_timing.ber,'go-;HF phase+timing;');
|
|
semilogy(sim_in.EbNodB, hf_timing_foff_est.ber,'kd-;HF phase+foff_est;');
|
|
semilogy(sim_in.EbNodB, hf_foff_est.ber,'c+-;HF phase+timing+foff_est;');
|
|
semilogy(sim_in.EbNodB, hf_dfoff.ber,'m+-;HF + 0.02Hz/s drift;');
|
|
hold off;
|
|
axis([0 8 5E-3 1E-1])
|
|
xlabel('Eb/No (dB)');
|
|
ylabel('BER');
|
|
grid; grid minor on;
|
|
legend('boxoff');
|
|
legend("location", "southeast");
|
|
title('Rate Fs modem BER with estimators');
|
|
print('-deps', '-color', "ofdm_dev_estimators.eps")
|
|
|
|
% Simulate different HF path delays
|
|
|
|
sim_in.hf_en = 1; sim_in.Nsec = Nsec_hf; sim_in.EbNodB = 4:2:8;
|
|
sim_in.timing_en = sim_in.foff_est_en = 0;
|
|
|
|
sim_in.path_delay_ms = 0; hf0 = run_sim(sim_in);
|
|
sim_in.path_delay_ms = 0.5; hf500us = run_sim(sim_in);
|
|
sim_in.path_delay_ms = 1; hf1ms = run_sim(sim_in);
|
|
sim_in.path_delay_ms = 2; hf2ms = run_sim(sim_in);
|
|
|
|
figure(2); clf;
|
|
semilogy(sim_in.EbNodB, hf_theory,'b+-;HF theory;');
|
|
hold on;
|
|
semilogy(sim_in.EbNodB, hf0.ber,'r+-;HF phase 0 ms;');
|
|
semilogy(sim_in.EbNodB, hf500us.ber,'g+-;HF phase 0.5 ms;');
|
|
semilogy(sim_in.EbNodB, hf1ms.ber,'c+-;HF phase 1 ms;');
|
|
semilogy(sim_in.EbNodB, hf2ms.ber,'k+-;HF phase 2 ms;');
|
|
hold off;
|
|
axis([3 9 1E-2 1E-1])
|
|
xlabel('Eb/No (dB)');
|
|
ylabel('BER');
|
|
grid; grid minor on;
|
|
legend('boxoff');
|
|
legend("location", "southeast");
|
|
title('Rate Fs modem BER across HF path delay');
|
|
print('-deps', '-color', "ofdm_dev_estimators.eps")
|
|
end
|
|
|
|
|
|
% Plot SNR actual and estimated for various channels
|
|
% Note no acquistion, as this can upset results, e.g. if
|
|
% sync is lost. We average SNR over entire run, in practice
|
|
% there will be some sort of IIR averager.
|
|
|
|
function run_curves_snr
|
|
|
|
Nsec_awgn = 30;
|
|
Nsec_hf = 60;
|
|
|
|
% waveform
|
|
|
|
Ts = 0.018; sim_in.Tcp = 0.002;
|
|
sim_in.Rs = 1/Ts; sim_in.bps = 2; sim_in.Nc = 16; sim_in.Ns = 8;
|
|
|
|
% simulation parameters
|
|
|
|
sim_in.verbose = 1; sim_in.foff_hz = 0; sim_in.ldpc_en = 0; sim_in.gain = 1;
|
|
sim_in.phase_est_en = sim_in.timing_en = sim_in.foff_est_en = 1;
|
|
|
|
% AWGN simulation
|
|
|
|
sim_in.EbNodB = 0:3:12;
|
|
sim_in.hf_en = 0; sim_in.Nsec = Nsec_awgn;
|
|
|
|
awgn = run_sim(sim_in);
|
|
|
|
% HF simulations
|
|
|
|
sim_in.hf_en = 1; sim_in.Nsec = Nsec_hf; sim_in.dopplerSpreadHz = 1; hf_fast = run_sim(sim_in);
|
|
sim_in.dopplerSpreadHz = 0.5; hf_mid = run_sim(sim_in);
|
|
sim_in.dopplerSpreadHz = 0.2; hf_slow = run_sim(sim_in);
|
|
|
|
figure(1); clf;
|
|
plot(sim_in.EbNodB, awgn.snrdB,'b+-;AWGN SNR;');
|
|
hold on;
|
|
plot(sim_in.EbNodB, awgn.snr_estdB ,'g+-;AWGN SNR est;');
|
|
plot(sim_in.EbNodB, hf_slow.snr_estdB,'k+-;HF 0.2 Hz SNR est;');
|
|
plot(sim_in.EbNodB, hf_mid.snr_estdB ,'r+-;HF 0.5 Hz SNR est;');
|
|
plot(sim_in.EbNodB, hf_fast.snr_estdB,'c+-;HF 1.0 Hz SNR est;');
|
|
hold off;
|
|
xlabel('Eb/No (dB)');
|
|
ylabel('SNR (dB)');
|
|
grid;
|
|
legend('boxoff');
|
|
title('SNR Actual and Estimated');
|
|
legend("location", "northwest");
|
|
print('-deps', '-color', "ofdm_dev_snr.eps")
|
|
|
|
end
|
|
|
|
|
|
% Run an acquisition test, returning vectors of estimation errors.
|
|
% Generates a vector of noise followed by continous rx signal to
|
|
% simulate signal starting. Freq est will improve over time as we
|
|
% have an averaging statistic. We then measure how long it takes to
|
|
% get good timing and freq estimates.
|
|
|
|
function [delta_ct delta_foff timing_mx_log] = acquisition_test(Ntests=10, EbNodB=100, foff_hz=0, hf_en=0, verbose=0)
|
|
|
|
Ts = 0.018;
|
|
sim_in.Tcp = 0.002;
|
|
sim_in.Rs = 1/Ts; sim_in.bps = 2; sim_in.Nc = 17; sim_in.Ns = 8;
|
|
|
|
sim_in.Nsec = (Ntests+1)*(sim_in.Ns+1)/sim_in.Rs;
|
|
|
|
#{
|
|
Notes:
|
|
1) uncoded modem operating point, e.g -1dB for AWGN
|
|
2) run_sim adds complex noise, when we take the real() below, this relects
|
|
the -ve noise over to the +ve side, increasing the noise by 3dB. In
|
|
ofdm_tx.m and friends, we add real nosie that is correctly scaled so
|
|
no problemo. This means we need to increase the Eb/No below by 3dB
|
|
to ensure the correct level of nosie ta the input of the timing_est.
|
|
#}
|
|
|
|
sim_in.EbNodB = EbNodB + 3;
|
|
sim_in.verbose = 0;
|
|
sim_in.hf_en = hf_en;
|
|
sim_in.foff_hz = foff_hz;
|
|
sim_in.gain = 1;
|
|
sim_in.timing_en = 1;
|
|
sim_in.foff_est_en = 1;
|
|
sim_in.phase_est_en = 1;
|
|
sim_in.ldpc_en = 0;
|
|
|
|
% optionally stick a bunch of noise in front of signal to confuse things
|
|
|
|
sim_in.initial_noise_sams = 0;
|
|
|
|
[sim_out rx states] = run_sim(sim_in);
|
|
|
|
states.verbose = verbose;
|
|
|
|
% set up acquistion
|
|
|
|
Nsamperframe = states.Nsamperframe; M = states.M; Ncp = states.Ncp;
|
|
rate_fs_pilot_samples = states.rate_fs_pilot_samples;
|
|
|
|
% test fine or acquisition over test signal
|
|
|
|
delta_ct = []; delta_foff = []; timing_mx_log = []; foff_metric_log = [];
|
|
|
|
|
|
% coarse acquiistion test. We have no idea of timing or freq
|
|
% offset for coarse we just use constant window shifts to simulate
|
|
% a bunch of trials, this allows averaging of freq est
|
|
% metric over time as we receive more and more frames
|
|
|
|
st = 0.5*Nsamperframe;
|
|
en = 2.5*Nsamperframe - 1; % note this gives Nsamperframe possibilities for coarse timing
|
|
|
|
% actual known position of correct coarse timing
|
|
|
|
ct_target = mod(sim_in.initial_noise_sams + Nsamperframe/2, Nsamperframe);
|
|
|
|
i = 1;
|
|
states.foff_metric = 0;
|
|
for w=1:Nsamperframe:length(rx)-4*Nsamperframe
|
|
[ct_est timing_valid timing_mx] = est_timing(states, real(rx(w+st:w+en)), rate_fs_pilot_samples);
|
|
[foff_est states] = est_freq_offset(states, real(rx(w+st:w+en)), rate_fs_pilot_samples, ct_est);
|
|
if states.verbose
|
|
printf("i: %2d w: %5d ct_est: %4d foff_est: %5.1f timing_mx: %3.2f timing_vld: %d\n", i++, w, ct_est, foff_est, timing_mx, timing_valid);
|
|
end
|
|
|
|
% valid coarse timing ests are modulo Nsamperframe
|
|
|
|
delta_ct = [delta_ct ct_est-ct_target];
|
|
delta_foff = [delta_foff (foff_est-foff_hz)];
|
|
timing_mx_log = [timing_mx_log; timing_mx];
|
|
foff_metric_log = [foff_metric_log states.foff_metric];
|
|
end
|
|
|
|
if states.verbose > 1
|
|
%printf("mean: %f std: %f\n", mean(delta_foff), std(delta_foff));
|
|
figure(1); clf; plot(timing_mx_log,'+-');
|
|
figure(2); clf; plot(delta_ct,'+-');
|
|
figure(3); clf; plot(delta_foff,'+-');
|
|
figure(4); clf; plot(foff_metric_log,'+');
|
|
figure(5); clf; plot(real(rx))
|
|
end
|
|
|
|
endfunction
|
|
|
|
|
|
#{
|
|
|
|
Generates aquisistion statistics for AWGN and HF channels for
|
|
continuous signals. Probability of acquistion is what matters,
|
|
e.g. if it's 50% we can expect sync within 2 frames.
|
|
|
|
#}
|
|
|
|
function res = acquisition_histograms(fine_en = 0, foff, EbNoAWGN=-1, EbNoHF=3, verbose=1)
|
|
Fs = 8000;
|
|
Ntests = 100;
|
|
|
|
% allowable tolerance for acquistion
|
|
|
|
ftol_hz = 1.5; % we can sync up on this
|
|
ttol_samples = 0.002*Fs; % 2ms, ie CP length
|
|
|
|
% AWGN channel at uncoded Eb/No operating point
|
|
|
|
[dct dfoff] = acquisition_test(Ntests, EbNoAWGN, foff, 0, fine_en);
|
|
|
|
% Probability of acquistion is what matters, e.g. if it's 50% we can
|
|
% expect sync within 2 frames
|
|
|
|
PtAWGN = length(find (abs(dct) < ttol_samples))/length(dct);
|
|
printf("AWGN P(time offset acq) = %3.2f\n", PtAWGN);
|
|
if fine_en == 0
|
|
PfAWGN = length(find (abs(dfoff) < ftol_hz))/length(dfoff);
|
|
printf("AWGN P(freq offset acq) = %3.2f\n", PfAWGN);
|
|
end
|
|
|
|
if verbose
|
|
figure(1); clf;
|
|
hist(dct(find (abs(dct) < ttol_samples)))
|
|
t = sprintf("Coarse Timing Error AWGN EbNo = %3.2f foff = %3.1f", EbNoAWGN, foff);
|
|
title(t)
|
|
if fine_en == 0
|
|
figure(2)
|
|
hist(dfoff(find(abs(dfoff) < 2*ftol_hz)))
|
|
t = sprintf("Coarse Freq Error AWGN EbNo = %3.2f foff = %3.1f", EbNoAWGN, foff);
|
|
title(t);
|
|
end
|
|
end
|
|
|
|
% HF channel at uncoded operating point
|
|
|
|
[dct dfoff] = acquisition_test(Ntests, EbNoHF, foff, 1, fine_en);
|
|
|
|
PtHF = length(find (abs(dct) < ttol_samples))/length(dct);
|
|
printf("HF P(time offset acq) = %3.2f\n", PtHF);
|
|
if fine_en == 0
|
|
PfHF = length(find (abs(dfoff) < ftol_hz))/length(dfoff)
|
|
printf("HF P(freq offset acq) = %3.2f\n", PfHF);
|
|
end
|
|
|
|
if verbose
|
|
figure(3); clf;
|
|
hist(dct(find (abs(dct) < ttol_samples)))
|
|
t = sprintf("Coarse Timing Error HF EbNo = %3.2f foff = %3.1f", EbNoHF, foff);
|
|
title(t)
|
|
if fine_en == 0
|
|
figure(4)
|
|
hist(dfoff(find(abs(dfoff) < 2*ftol_hz)))
|
|
t = sprintf("Coarse Freq Error HF EbNo = %3.2f foff = %3.1f", EbNoHF, foff);
|
|
title(t);
|
|
end
|
|
end
|
|
|
|
res = [PtAWGN PfAWGN PtHF PfHF];
|
|
endfunction
|
|
|
|
|
|
% plot some curves of Acquisition probability against EbNo and freq offset
|
|
|
|
function acquistion_curves
|
|
|
|
EbNo = [-1 2 5 8];
|
|
%foff = [-20 -15 -10 -5 0 5 10 15 20];
|
|
foff = [-15 -5 0 5 15];
|
|
cc = ['b' 'g' 'k' 'c' 'm'];
|
|
|
|
figure(1); clf; hold on; title('P(timing) AWGN'); xlabel('Eb/No dB'); legend('location', 'southeast');
|
|
figure(2); clf; hold on; title('P(freq) AWGN'); xlabel('Eb/No dB'); legend('location', 'southeast');
|
|
figure(3); clf; hold on; title('P(timing) HF'); xlabel('Eb/No dB'); legend('location', 'southeast');
|
|
figure(4); clf; hold on; title('P(freq) HF'); xlabel('Eb/No dB'); legend('location', 'southeast');
|
|
|
|
for f = 1:length(foff)
|
|
afoff = foff(f);
|
|
res_log = [];
|
|
for e = 1:length(EbNo)
|
|
aEbNo = EbNo(e);
|
|
res = zeros(1,4);
|
|
res = acquisition_histograms(fine_en = 0, afoff, aEbNo, aEbNo+4, verbose = 0);
|
|
res_log = [res_log; res];
|
|
end
|
|
figure(1); l = sprintf('%c+-;%3.1f Hz;', cc(f), afoff); plot(EbNo, res_log(:,1), l);
|
|
figure(2); l = sprintf('%c+-;%3.1f Hz;', cc(f), afoff); plot(EbNo, res_log(:,3), l);
|
|
figure(3); l = sprintf('%c+-;%3.1f Hz;', cc(f), afoff); plot(EbNo+4, res_log(:,2), l);
|
|
figure(4); l = sprintf('%c+-;%3.1f Hz;', cc(f), afoff); plot(EbNo+4, res_log(:,4), l);
|
|
end
|
|
|
|
figure(1); print('-deps', '-color', "ofdm_dev_acq_curves_time_awgn.eps")
|
|
figure(2); print('-deps', '-color', "ofdm_dev_acq_curves_freq_awgn.eps")
|
|
figure(3); print('-deps', '-color', "ofdm_dev_acq_curves_time_hf.eps")
|
|
figure(4); print('-deps', '-color', "ofdm_dev_acq_curves_freq_hf.eps")
|
|
endfunction
|
|
|
|
|
|
% Used to develop sync state machine - in particular a metric to show
|
|
% we are out of sync, or have sync with a bad freq offset est, or have
|
|
% lost modem signal
|
|
|
|
function sync_metrics(x_axis = 'EbNo')
|
|
Fs = 8000;
|
|
Ntests = 4;
|
|
f_offHz = [-25:25];
|
|
EbNodB = [-10 0 3 6 10 20];
|
|
%f_offHz = [-5:5:5];
|
|
%EbNodB = [-10 0 10];
|
|
cc = ['b' 'g' 'k' 'c' 'm' 'b'];
|
|
pt = ['+' '+' '+' '+' '+' 'o'];
|
|
|
|
mean_mx1_log = mean_dfoff_log = [];
|
|
for f = 1:length(f_offHz)
|
|
af_offHz = f_offHz(f);
|
|
mean_mx1_row = mean_dfoff_row = [];
|
|
for e = 1:length(EbNodB)
|
|
aEbNodB = EbNodB(e);
|
|
[dct dfoff timing_mx_log] = acquisition_test(Ntests, aEbNodB, af_offHz);
|
|
mean_mx1 = mean(timing_mx_log(:,1));
|
|
printf("f_offHz: %5.2f EbNodB: % 6.2f mx1: %3.2f\n", af_offHz, aEbNodB, mean_mx1);
|
|
mean_mx1_row = [mean_mx1_row mean_mx1];
|
|
mean_dfoff_row = [mean_dfoff_row mean(dfoff)];
|
|
end
|
|
mean_mx1_log = [mean_mx1_log; mean_mx1_row];
|
|
mean_dfoff_log = [mean_dfoff_log; mean_dfoff_row];
|
|
end
|
|
|
|
figure(1); clf; hold on; grid;
|
|
if strcmp(x_axis,'EbNo')
|
|
for f = 1:length(f_offHz)
|
|
if f == 2, hold on, end;
|
|
leg1 = sprintf("b+-;mx1 %4.1f Hz;", f_offHz(f));
|
|
plot(EbNodB, mean_mx1_log(f,:), leg1)
|
|
end
|
|
hold off;
|
|
xlabel('Eb/No (dB)');
|
|
ylabel('Coefficient')
|
|
title('Pilot Correlation Metric against Eb/No for different Freq Offsets');
|
|
legend("location", "northwest"); legend("boxoff");
|
|
axis([min(EbNodB) max(EbNodB) 0 1.2])
|
|
print('-deps', '-color', "ofdm_dev_pilot_correlation_ebno.eps")
|
|
end
|
|
|
|
if strcmp(x_axis,'freq')
|
|
% x axis is freq
|
|
|
|
for e = length(EbNodB):-1:1
|
|
leg1 = sprintf("%c%c-;mx1 %3.0f dB;", cc(e), pt(e), EbNodB(e));
|
|
plot(f_offHz, mean_mx1_log(:,e), leg1)
|
|
end
|
|
hold off;
|
|
xlabel('freq offset (Hz)');
|
|
ylabel('Coefficient')
|
|
title('Pilot Correlation Metric against Freq Offset for different Eb/No dB');
|
|
legend("location", "northwest"); legend("boxoff");
|
|
axis([min(f_offHz) max(f_offHz) 0 1])
|
|
print('-deps', '-color', "ofdm_dev_pilot_correlation_freq.eps")
|
|
|
|
mean_dfoff_log
|
|
|
|
figure(2); clf;
|
|
for e = 1:length(EbNodB)
|
|
if e == 2, hold on, end;
|
|
leg1 = sprintf("+-;mx1 %3.0f dB;", EbNodB(e));
|
|
plot(f_offHz, mean_dfoff_log(:,e), leg1)
|
|
end
|
|
hold off;
|
|
xlabel('freq offset (Hz)');
|
|
ylabel('Mean Freq Est Error')
|
|
title('Freq Est Error against Freq Offset for different Eb/No dB');
|
|
axis([min(f_offHz) max(f_offHz) -5 5])
|
|
end
|
|
|
|
endfunction
|
|
|
|
|
|
% during development it was discovered demod could obtain a flase sync with no UW
|
|
% errors at +/- 7Hz, approx the frame rate. This function is used to explore that
|
|
|
|
function debug_false_sync(EbNodB = 100)
|
|
Ts = 0.018;
|
|
sim_in.Tcp = 0.002;
|
|
sim_in.Rs = 1/Ts; sim_in.bps = 2; sim_in.Nc = 17; sim_in.Ns = 8;
|
|
|
|
sim_in.Nsec = (sim_in.Ns+1)/sim_in.Rs; % one frame, make sure sim_in.interleave_frames = 1
|
|
sim_in.Nsec = 1;
|
|
|
|
sim_in.EbNodB = 40;
|
|
sim_in.verbose = 0;
|
|
sim_in.hf_en = 0;
|
|
sim_in.foff_hz = 0;
|
|
sim_in.dfoff_hz_per_sec = 0.00;
|
|
sim_in.sample_clock_offset_ppm = 0;
|
|
sim_in.gain = 1;
|
|
|
|
sim_in.timing_en = 0;
|
|
sim_in.foff_est_en = 0;
|
|
sim_in.phase_est_en = 1;
|
|
|
|
load HRA_112_112.txt
|
|
sim_in.ldpc_code = HRA_112_112;
|
|
sim_in.ldpc_en = 0;
|
|
|
|
%sim_in.interleave_frames = 1;
|
|
%sim_in.diversity_en = 1;
|
|
|
|
sim_in.uw_debug = 1;
|
|
|
|
foff = -25:0.5:25;
|
|
for f=1:length(foff)
|
|
sim_in.foff_hz = foff(f);
|
|
sim_out = run_sim(sim_in);
|
|
min_uw_errors(f) = min(sim_out.uw_errors_log(2:end-1));
|
|
printf("f: %4.2f %2d\n", foff(f), min_uw_errors(f));
|
|
end
|
|
figure(1); clf;
|
|
plot(foff, min_uw_errors);
|
|
title('UW errors versus freq offset');
|
|
xlabel('Freq Offset (Hz)');
|
|
end
|
|
|
|
|
|
% Using an input raw file, plot frame by frame metric information,
|
|
% used to debug false syncs
|
|
|
|
function metric_fbf(fn, Nsec)
|
|
Ts = 0.018;
|
|
states = ofdm_init(bps=2, Rs=1/Ts, Tcp=0.002, Ns=8, Nc=17);
|
|
ofdm_load_const;
|
|
states.verbose = 2;
|
|
|
|
% factor of 2 as input is a real valued signal
|
|
|
|
Ascale = states.amp_scale/2;
|
|
f = fopen(fn,"rb"); rx = fread(f,Inf,"short")'/Ascale; fclose(f);
|
|
if (nargin == 2) && (length(rx) > Nsec*Fs)
|
|
rx = rx(1:Nsec*Fs);
|
|
end
|
|
Nsam = length(rx);
|
|
%bpf_coeff = make_ofdm_bpf(write_c_header_file=0);
|
|
%rx = filter(bpf_coeff,1,rx);
|
|
|
|
st = 0.5*Nsamperframe;
|
|
en = 2.5*Nsamperframe - 1; % note this gives Nsamperframe possibilities for coarse timing
|
|
|
|
i = 1; w_log = timing_mx_log = av_level_log = [];
|
|
states.foff_metric = 0;
|
|
for w=1:Nsamperframe:length(rx)-4*Nsamperframe
|
|
printf("%3d %5d", i,w);
|
|
#{
|
|
if i == 30
|
|
states.verbose = 3;
|
|
else
|
|
states.verbose = 2;
|
|
end
|
|
#}
|
|
[ct_est timing_valid timing_mx av_level] = est_timing(states, real(rx(w+st:w+en)), states.rate_fs_pilot_samples);
|
|
i++;
|
|
w_log = [w_log w];
|
|
timing_mx_log = [timing_mx_log timing_mx];
|
|
av_level_log = [av_level_log av_level];
|
|
end
|
|
|
|
figure(2); clf;
|
|
mx = max(abs(rx));
|
|
subplot(211); plot(rx); axis([0 Nsam -mx mx]);
|
|
subplot(212); hold on;
|
|
plot(w_log,timing_mx_log,'b+-;timing mx;');
|
|
plot(w_log,av_level_log,'g+-;av level;');
|
|
hold off;
|
|
endfunction
|
|
|
|
|
|
% ---------------------------------------------------------
|
|
% choose simulation to run here
|
|
% ---------------------------------------------------------
|
|
|
|
format;
|
|
more off;
|
|
|
|
path_to_cml = '~/cml';
|
|
addpath(strcat(path_to_cml, "/mex"), strcat(path_to_cml, "/mat"));
|
|
if exist("Somap") == 0
|
|
printf("Can't find CML mex directory so we wont init CML...\n");
|
|
else
|
|
printf("OK found CML mex directory so will init CML...\n");
|
|
init_cml('~/cml/');
|
|
end
|
|
|
|
run_single(100);
|
|
%run_curves
|
|
%run_curves_estimators
|
|
%acquisition_histograms(fin_en=0, foff_hz=-15, EbNoAWGN=-1, EbNoHF=3)
|
|
%acquisition_test(Ntests=10, EbNodB=-1, foff_hz=0, hf_en=0, verbose=1);
|
|
%sync_metrics('freq')
|
|
%run_curves_snr
|
|
%acquisition_dev(Ntests=10, EbNodB=100, foff_hz=0)
|
|
%acquistion_curves
|
|
%debug_false_sync
|
|
%metric_fbf("ofdm_test.raw")
|
|
|