1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
|
% ofdm_helper.m
%
% Misc functions that are used to support OFDM modem development, that
% aren't required for modem operation
1;
%------------------------------------------------------------------------------
% print_config - utility function to use ascii-art to describe the modem frame
%------------------------------------------------------------------------------
function print_config(states)
ofdm_load_const;
% ASCII-art packet visualisation
s=1; u=1; Nuwsyms=length(uw_ind_sym);
cr = 1:Nc+2;
for f=1:Np
for r=1:Ns
for c=cr
if r == 1
if (c==1) && states.edge_pilots
sym="P";
elseif (c==Nc+1) && states.edge_pilots
sym="P";
elseif c>1 && c <=(Nc+1)
sym="P";
else
sym=" ";
end
elseif c>1 && c <=(Nc+1)
sym=".";
if (u <= Nuwsyms) && (s == uw_ind_sym(u)) sym="U"; u++; end
s++;
else
sym=" ";
end
printf("%s",sym);
end
printf("\n");
end
end
printf("Nc=%d Ts=%4.3f Tcp=%4.3f Ns: %d Np: %d\n", Nc, 1/Rs, Tcp, Ns, Np);
printf("Nsymperframe: %d Nbitsperpacket: %d Nsamperframe: %d Ntxtbits: %d Nuwbits: %d Nuwframes: %d\n",
Ns*Nc, Nbitsperpacket, Nsamperframe, Ntxtbits, Nuwbits, Nuwframes);
printf("uncoded bits/s: %4.1f Duration (incl post/preamble): %4.2f s\n",
Nbitsperpacket*Fs/(Np*Nsamperframe), (Np+2)*Ns*(Tcp+1/Rs));
end
%-----------------------------------------------------------------------
% create_ldpc_test_frame - generate a test frame of bits
%-----------------------------------------------------------------------
function [tx_bits payload_data_bits codeword] = create_ldpc_test_frame(states, coded_frame=1)
ofdm_load_const;
ldpc;
gp_interleaver;
if coded_frame
% Set up LDPC code
mod_order = 4; bps = 2; modulation = 'QPSK'; mapping = 'gray';
init_cml(); % TODO: make this path sensible and portable
load HRA_112_112.txt
[code_param framesize rate] = ldpc_init_user(HRA_112_112, modulation, mod_order, mapping);
assert(Nbitsperframe == (code_param.coded_bits_per_frame + Nuwbits + Ntxtbits));
payload_data_bits = round(ofdm_rand(code_param.data_bits_per_frame)/32767);
codeword = LdpcEncode(payload_data_bits, code_param.H_rows, code_param.P_matrix);
Nsymbolsperframe = length(codeword)/bps;
% need all these steps to get actual raw codeword bits at demod ..
tx_symbols = [];
for s=1:Nsymbolsperframe
tx_symbols = [tx_symbols qpsk_mod( codeword(2*(s-1)+1:2*s) )];
end
tx_symbols = gp_interleave(tx_symbols);
codeword_raw = [];
for s=1:Nsymbolsperframe
codeword_raw = [codeword_raw qpsk_demod(tx_symbols(s))];
end
else
codeword_raw = round(ofdm_rand(Nbitsperpacket-(Nuwbits+Ntxtbits))/32767);
end
% insert UW and txt bits
tx_bits = assemble_modem_packet(states, codeword_raw, zeros(1,Ntxtbits));
assert(Nbitsperpacket == length(tx_bits));
endfunction
% automated test
function test_assemble_disassemble(states)
ofdm_load_const;
Nsymsperpacket = Nbitsperpacket/bps;
Ndatabitsperpacket = Nbitsperpacket-(Nuwbits+Ntxtbits);
Ndatasymsperpacket = Ndatabitsperpacket/bps;
codeword_bits = round(ofdm_rand(Ndatabitsperpacket)/32767);
tx_bits = assemble_modem_packet(states, codeword_bits, zeros(1,Ntxtbits));
tx_syms = zeros(1,Nsymsperpacket);
for s=1:Nsymsperpacket
if bps == 2
tx_syms(s) = qpsk_mod(tx_bits(bps*(s-1)+1:bps*s));
elseif bps == 4
tx_syms(s) = qam16_mod(states.qam16,tx_bits(bps*(s-1)+1:bps*s));
end
end
codeword_syms = zeros(1,Ndatasymsperpacket);
for s=1:Ndatasymsperpacket
if bps == 2
codeword_syms(s) = qpsk_mod(codeword_bits(bps*(s-1)+1:bps*s));
elseif bps == 4
codeword_syms(s) = qam16_mod(states.qam16,codeword_bits(bps*(s-1)+1:bps*s));
end
end
[rx_uw rx_codeword_syms payload_amps txt_bits] = disassemble_modem_packet(states, tx_syms, ones(1,Nsymsperpacket));
assert(rx_uw == states.tx_uw);
Ndatasymsperframe = (Nbitsperpacket-(Nuwbits+Ntxtbits))/bps;
assert(codeword_syms == rx_codeword_syms);
endfunction
% test function, kind of like a CRC for QPSK symbols, to compare two vectors
function acc = test_acc(v)
sre = 0; sim = 0;
for i=1:length(v)
x = v(i);
re = round(real(x)); im = round(imag(x));
sre += re; sim += im;
%printf("%d %10f %10f %10f %10f\n", i, re, im, sre, sim);
end
acc = sre + j*sim;
end
% Save test bits frame to a text file in the form of a C array
%
% usage:
% ofdm_lib; test_bits_ofdm_file
%
function test_bits_ofdm_file
Ts = 0.018; Tcp = 0.002; Rs = 1/Ts; bps = 2; Nc = 17; Ns = 8;
states = ofdm_init(bps, Rs, Tcp, Ns, Nc);
[test_bits_ofdm payload_data_bits codeword] = create_ldpc_test_frame(states);
printf("%d test bits\n", length(test_bits_ofdm));
f=fopen("../src/test_bits_ofdm.h","wt");
fprintf(f,"/* Generated by test_bits_ofdm_file() Octave function */\n\n");
fprintf(f,"const int test_bits_ofdm[]={\n");
for m=1:length(test_bits_ofdm)-1
fprintf(f," %d,\n",test_bits_ofdm(m));
endfor
fprintf(f," %d\n};\n",test_bits_ofdm(end));
fprintf(f,"\nconst int payload_data_bits[]={\n");
for m=1:length(payload_data_bits)-1
fprintf(f," %d,\n",payload_data_bits(m));
endfor
fprintf(f," %d\n};\n",payload_data_bits(end));
fprintf(f,"\nconst int test_codeword[]={\n");
for m=1:length(codeword)-1
fprintf(f," %d,\n",codeword(m));
endfor
fprintf(f," %d\n};\n",codeword(end));
fclose(f);
endfunction
% Get rid of nasty unfiltered stuff either side of OFDM signal
% This may need to be tweaked, or better yet made a function of Nc, if Nc changes
%
% usage:
% ofdm_lib; make_ofdm_bpf(1);
function bpf_coeff = make_ofdm_bpf(write_c_header_file)
filt_n = 100;
Fs = 8000;
bpf_coeff = fir2(filt_n,[0 900 1000 2000 2100 4000]/(Fs/2),[0.001 0.001 1 1 0.001 0.001]);
if write_c_header_file
figure(1)
clf;
h = freqz(bpf_coeff,1,Fs/2);
plot(20*log10(abs(h)))
grid minor
% save coeffs to a C header file
f=fopen("../src/ofdm_bpf_coeff.h","wt");
fprintf(f,"/* 1000 - 2000 Hz FIR filter coeffs */\n");
fprintf(f,"/* Generated by make_ofdm_bpf() in ofdm_lib.m */\n");
fprintf(f,"\n#define OFDM_BPF_N %d\n\n", filt_n);
fprintf(f,"float ofdm_bpf_coeff[]={\n");
for r=1:filt_n
if r < filt_n
fprintf(f, " %f,\n", bpf_coeff(r));
else
fprintf(f, " %f\n};", bpf_coeff(r));
end
end
fclose(f);
end
endfunction
% Helper function to help design UW error thresholds, in particular for raw
% data modes. See also https://www.rowetel.com/wordpress/?p=7467
function ofdm_determine_bad_uw_errors(Nuw)
figure(1); clf;
% Ideally the 10% and 50% BER curves are a long way apart
plot(0:Nuw, binocdf(0:Nuw,Nuw,0.1),';BER=0.1;'); hold on;
plot(binocdf(0:Nuw,Nuw,0.5),';BER=0.5;');
% Suggested threshold for raw data modes is the 5% probability
% level for the 50% BER curve. The pre/post-amble has a low chance
% of failure. If it does make an error, then we will have random
% bits presented as the UW (50% BER in UW). This threshold means
% there is only a 5% case of random bits being accepted as a valid UW
bad_uw_errors = max(find(binocdf(0:Nuw,Nuw,0.5) <= 0.05))+1;
plot([bad_uw_errors bad_uw_errors],[0 1],';bad uw errors;'); hold off; grid
xlabel('bits');
printf("for Nuw = %d, suggest bad_uw_errors = %d\n", Nuw, bad_uw_errors);
end
% Returns level threshold such that threshold_cdf of the tx magnitudes are
% beneath that level. Helper function that can be used to design
% the clipper level. See also https://www.rowetel.com/?p=7596
function threshold_level = ofdm_determine_clip_threshold(tx, threshold_cdf)
Nsteps = 25;
mx = max(abs(tx));
cdf = empirical_cdf(mx*(1:Nsteps)/Nsteps,abs(tx));
threshold_level = find(cdf >= threshold_cdf)(1)*mx/25;
printf("threshold_cdf: %f threshold_level: %f\n", threshold_cdf, threshold_level);
figure(1); clf; [hh nn] = hist(abs(tx),Nsteps,1);
plotyy(nn,hh,mx*(1:Nsteps)/Nsteps,cdf); title('PDF and CDF Estimates'); grid;
end
% helper function that adds channel simulation and ensures we don't saturate int16 output samples
function [rx_real rx] = ofdm_channel(states, tx, SNR3kdB, channel, freq_offset_Hz)
[rx_real rx sigma] = channel_simulate(states.Fs, SNR3kdB, freq_offset_Hz, channel, tx, states.verbose);
% multipath models can lead to clipping of int16 samples
num_clipped = length(find(abs(rx_real>32767)));
while num_clipped/length(rx_real) > 0.001
rx_real /= 2;
num_clipped = length(find(abs(rx_real>32767)));
printf("WARNING: output samples clipped, reducing level\n")
end
endfunction
|