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.
173 lines
5.1 KiB
173 lines
5.1 KiB
% ofdm_ldpc_tx.m
|
|
% David Rowe April 2017
|
|
%
|
|
% File based ofdm tx with LDPC encoding and interleaver. Generates a
|
|
% file of ofdm samples, including optional channel simulation.
|
|
|
|
#{
|
|
Examples:
|
|
|
|
i) 4 frame interleaver, 10 seconds, AWGN channel at (coded) Eb/No=3dB
|
|
|
|
octave:4> ofdm_ldpc_tx('awgn_ebno_3dB_700d.raw', "700D", 4, 10, 3);
|
|
|
|
ii) 4 frame interleaver, 10 seconds, HF channel at (coded) Eb/No=6dB
|
|
|
|
ofdm_ldpc_tx('hf_ebno_6dB_700d.raw', "700D", 4, 10, 6, 'hf');
|
|
#}
|
|
|
|
|
|
#{
|
|
TODO:
|
|
[ ] measure and report raw and coded BER
|
|
[ ] maybe 10s worth of frames, sync up to any one automatically
|
|
+ or start with maybe 10 frames
|
|
+ measure BER match on each one
|
|
[ ] model clipping/PA compression
|
|
[ ] sample clock offsets
|
|
[ ] compare with same SNR from pathsim
|
|
[ ] How to pack arbitrary frames into ofdm frame and codec 2 frames
|
|
+ integer number of ofdm frames?
|
|
+ how to sync at other end
|
|
|
|
#}
|
|
|
|
% Set up LDPC code and voice codec to "codeword" packing
|
|
|
|
|
|
function ofdm_ldpc_tx(filename, mode="700D", interleave_frames = 1, Nsec, EbNodB=100, channel='awgn', freq_offset_Hz=0)
|
|
ofdm_lib;
|
|
ldpc;
|
|
gp_interleaver;
|
|
|
|
% init modem
|
|
|
|
bps = 2; Ns = 8; Tcp = 0.002;
|
|
[bps Rs Tcp Ns Nc] = ofdm_init_mode(mode, Ns);
|
|
states = ofdm_init(bps, Rs, Tcp, Ns, Nc);
|
|
ofdm_load_const;
|
|
|
|
% some constants used for assembling modem frames
|
|
|
|
[code_param Nbitspercodecframe Ncodecframespermodemframe Nunprotect] = codec_to_frame_packing(states, mode);
|
|
|
|
% Generate fixed test frame of tx bits and run OFDM modulator
|
|
|
|
Nrows = Nsec*Rs;
|
|
Nframes = floor((Nrows-1)/Ns);
|
|
|
|
% Adjust Nframes so we have an integer number of interleaver frames
|
|
% in simulation
|
|
|
|
Nframes = interleave_frames*round(Nframes/interleave_frames);
|
|
|
|
% OK generate a modem frame using random payload bits
|
|
|
|
if strcmp(mode, "700D")
|
|
codec_bits = round(ofdm_rand(code_param.data_bits_per_frame)/32767);
|
|
else
|
|
codec_bits = round(ofdm_rand(Ncodecframespermodemframe*Nbitspercodecframe)/32767);
|
|
end
|
|
[frame_bits bits_per_frame] = assemble_frame(states, code_param, mode, codec_bits, Ncodecframespermodemframe, Nbitspercodecframe);
|
|
|
|
% modulate to create symbols and interleave
|
|
|
|
tx_bits = tx_symbols = [];
|
|
for f=1:interleave_frames
|
|
tx_bits = [tx_bits codec_bits];
|
|
for b=1:2:bits_per_frame
|
|
tx_symbols = [tx_symbols qpsk_mod(frame_bits(b:b+1))];
|
|
end
|
|
end
|
|
tx_symbols = gp_interleave(tx_symbols);
|
|
|
|
% generate txt symbols
|
|
|
|
txt_bits = zeros(1,Ntxtbits);
|
|
txt_symbols = [];
|
|
for b=1:2:length(txt_bits)
|
|
txt_symbols = [txt_symbols qpsk_mod(txt_bits(b:b+1))];
|
|
end
|
|
|
|
% assemble interleaved modem frames that include UW and txt symbols
|
|
|
|
atx = [];
|
|
for f=1:interleave_frames
|
|
st = (f-1)*bits_per_frame/bps+1; en = st + bits_per_frame/bps-1;
|
|
modem_frame = assemble_modem_frame_symbols(states, tx_symbols(st:en), txt_symbols);
|
|
atx = [atx ofdm_txframe(states, modem_frame) ];
|
|
end
|
|
|
|
tx = [];
|
|
for f=1:Nframes/interleave_frames
|
|
tx = [tx atx];
|
|
end
|
|
|
|
Nsam = length(tx);
|
|
|
|
% channel simulation
|
|
|
|
EsNo = rate * bps * (10 .^ (EbNodB/10));
|
|
variance = 1/(M*EsNo/2);
|
|
woffset = 2*pi*freq_offset_Hz/Fs;
|
|
|
|
SNRdB = EbNodB + 10*log10(Nc*bps*Rs*rate/3000);
|
|
printf("EbNo: %3.1f dB SNR(3k) est: %3.1f dB foff: %3.1fHz inter_frms: %d ",
|
|
EbNodB, SNRdB, freq_offset_Hz, interleave_frames);
|
|
|
|
% set up HF model ---------------------------------------------------------------
|
|
|
|
if strcmp(channel, 'hf') || strcmp(channel, 'hfgood')
|
|
randn('seed',1);
|
|
|
|
% ITUT "poor" or "moderate" channels
|
|
|
|
if strcmp(channel, 'hf')
|
|
dopplerSpreadHz = 1; path_delay_ms = 1;
|
|
else
|
|
% "hfgood"
|
|
dopplerSpreadHz = 0.1; path_delay_ms = 0.5;
|
|
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, (Nsec*(M+Ncp)/M)*Fs*1.1);
|
|
spread2 = doppler_spread(dopplerSpreadHz, Fs, (Nsec*(M+Ncp)/M)*Fs*1.1);
|
|
|
|
% sometimes doppler_spread() doesn't return exactly the number of samples we need
|
|
|
|
assert(length(spread1) >= Nsam, "not enough doppler spreading samples");
|
|
assert(length(spread2) >= Nsam, "not enough doppler spreading samples");
|
|
end
|
|
|
|
rx = tx;
|
|
|
|
if strcmp(channel, 'hf') || strcmp(channel, 'hfgood')
|
|
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
|
|
|
|
rx = rx .* exp(j*woffset*(1:Nsam));
|
|
|
|
% note variance/2 as we are using real() operator, mumble,
|
|
% reflection of -ve freq to +ve, mumble, hand wave
|
|
|
|
noise = sqrt(variance/2)*0.5*randn(1,Nsam);
|
|
rx = real(rx) + noise;
|
|
printf("measured SNR: %3.2f dB\n", 10*log10(var(real(tx))/var(noise)) + 10*log10(4000) - 10*log10(3000));
|
|
|
|
% adjusted by experiment to match rms power of early test signals
|
|
|
|
frx=fopen(filename,"wb"); fwrite(frx, states.amp_scale*rx, "short"); fclose(frx);
|
|
endfunction
|
|
|