aboutsummaryrefslogtreecommitdiff
path: root/octave/tofdm.m
blob: d2b80cea18cc89268f2128df9d72892bb4717e2b (plain)
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
% tofdm.m
% David Rowe and Steve Sampson June 2017
%
% Octave script for comparing Octave and C versions of OFDM modem
%
% If running from the Octave command line a good idea to clear globals before
% each run:
%
%   octave> clear; tofdm;

% ------------------------------------------------------------------

Nframes = 10;
sample_clock_offset_ppm = 100;
foff_hz = 0.5;

more off; format;
ofdm_lib;
autotest;
ldpc
global passes = 0;
global fails = 0;

init_cml()
cml_support = 1

% ---------------------------------------------------------------------
% Run Octave version 
% ---------------------------------------------------------------------

% useful to test the modem at other Nc's, but if Nc != 17 we aren't set up for
% LDPC testing so disable
if getenv("NC")
  Nc = str2num(getenv("NC"));
  cml_support = 0;
else
  Nc = 17;
end
printf("Nc = %d LDPC testing: %d\n", Nc, cml_support);

config = ofdm_init_mode("700D");
config.Nc = Nc;
states = ofdm_init(config);
states.verbose = 0;
ofdm_load_const;

printf("Nbitsperframe: %d\n", Nbitsperframe);

if cml_support
  Nuwtxtsymbolsperframe = (states.Nuwbits+states.Ntxtbits)/bps;
  S_matrix = [1, j, -j, -1];
  EsNo = 10;
  symbol_likelihood_log = bit_likelihood_log = detected_data_log = [];

  % Set up LDPC code

  mod_order = 4; bps = 2; modulation = 'QPSK'; mapping = 'gray';
  demod_type = 0; decoder_type = 0; max_iterations = 100;

  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 + states.Nuwbits + states.Ntxtbits));
end

tx_bits = zeros(1,Nbitsperframe);
rand('seed',1);

payload_data_bits = round(rand(1,(Nbitsperframe-Nuwbits-Ntxtbits)/2));
states.mean_amp = 1;  % start this with something sensible otherwise LDPC decode fails
if cml_support
  ibits = payload_data_bits;
  codeword = LdpcEncode(ibits, code_param.H_rows, code_param.P_matrix);
  tx_bits(Nuwbits+Ntxtbits+1:end) = codeword;
  tx_bits(1:Nuwbits+Ntxtbits) = [states.tx_uw zeros(1,Ntxtbits)];
else
  tx_bits = create_ldpc_test_frame(states, coded_frame=0);
end

% Run tx loop

tx_bits_log = []; tx_log = [];
for f=1:Nframes
  tx_bits_log = [tx_bits_log tx_bits];
  tx_log = [tx_log ofdm_mod(states, tx_bits)];
end

% Channel simulation ----------------------------------------------

rx_log = sample_clock_offset(tx_log, sample_clock_offset_ppm);
rx_log = freq_shift(rx_log, foff_hz, Fs);

% Rx ---------------------------------------------------------------

% Init rx with ideal timing so we can test with timing estimation disabled

Nsam = length(rx_log);
prx = 1;
nin = Nsamperframe+2*(M+Ncp);
states.rxbuf(Nrxbuf-nin+1:Nrxbuf) = rx_log(prx:nin);
prx += nin;

rxbuf_log = []; rxbuf_in_log = []; rx_sym_log = []; foff_hz_log = []; 
timing_est_log = timing_valid_log = timing_mx_log = [];
coarse_foff_est_hz_log = []; sample_point_log = [];
phase_est_pilot_log = []; rx_amp_log = [];
rx_np_log = []; rx_bits_log = [];
snr_log = []; mean_amp_log = [];

states.timing_en = 1;
states.foff_est_en = 1;
states.phase_est_en = 1;

if states.timing_en == 0
  % manually set ideal timing instant
  states.sample_point = Ncp;
end


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
 
  nin = states.nin;
  lnew = min(Nsam-prx+1,nin);
  rxbuf_in = zeros(1,nin);
  %printf("nin: %d prx: %d lnew: %d\n", nin, prx, lnew);
  if lnew
    rxbuf_in(1:lnew) = rx_log(prx:prx+lnew-1);
  end
  prx += lnew;

  [states rx_bits achannel_est_pilot_log arx_np arx_amp] = ofdm_demod(states, rxbuf_in);
  
  % log some states for comparison to C

  rxbuf_in_log = [rxbuf_in_log rxbuf_in];
  rxbuf_log = [rxbuf_log states.rxbuf];
  rx_sym_log = [rx_sym_log; states.rx_sym];
  phase_est_pilot_log = [phase_est_pilot_log; angle(achannel_est_pilot_log)];
  rx_amp_log = [rx_amp_log arx_amp];
  foff_hz_log = [foff_hz_log; states.foff_est_hz];
  timing_est_log = [timing_est_log; states.timing_est];
  timing_valid_log = [timing_valid_log; states.timing_valid];
  timing_mx_log = [timing_mx_log; states.timing_mx];
  coarse_foff_est_hz_log = [coarse_foff_est_hz_log; states.coarse_foff_est_hz];
  sample_point_log = [sample_point_log; states.sample_point];
  rx_np_log = [rx_np_log arx_np];
  rx_bits_log = [rx_bits_log rx_bits];
  mean_amp_log = [mean_amp_log; states.mean_amp];
  EsNo_estdB = esno_est_calc(arx_np);
  SNR_estdB = snr_from_esno(states, EsNo_estdB);
  snr_log = [snr_log; SNR_estdB];
  
  % Optional testing of LDPC functions

  if cml_support
    mean_amp = states.mean_amp;
    %mean_amp = 1;
    symbol_likelihood = Demod2D(arx_np(Nuwtxtsymbolsperframe+1:end)/mean_amp, S_matrix, EsNo, arx_amp(Nuwtxtsymbolsperframe+1:end)/mean_amp);
    bit_likelihood = Somap(symbol_likelihood);

    [x_hat paritychecks] = MpDecode(-bit_likelihood(1:code_param.coded_bits_per_frame), code_param.H_rows, code_param.H_cols, max_iterations, decoder_type, 1, 1);
    [mx mx_ind] = max(paritychecks);
    detected_data = x_hat(mx_ind,:);
    
    % make sure LDPC decoding is working OK
    
    % assert(codeword == detected_data);
    
    [m n] = size(symbol_likelihood);
    symbol_likelihood_log = [symbol_likelihood_log; reshape(symbol_likelihood,m*n,1)];
    bit_likelihood_log = [bit_likelihood_log; bit_likelihood'];
    detected_data_log = [detected_data_log detected_data];
  end
  
end

% ---------------------------------------------------------------------
% Run C version and plot Octave and C states and differences 
% ---------------------------------------------------------------------

printf("\nRunning C version....\n");

% Override default path by:
%   1. if running from octave CLI: setting path_to_tofdm = "/your/path/to/tofdm"
%   2. If running from shell....." set PATH_TO_OFDM = "/your/path/to/tofdm"

if exist("path_to_tofdm", "var") == 0
  path_to_tofdm = "../build_linux/unittest/tofdm"
end

if getenv("PATH_TO_TOFDM")
  path_to_tofdm = getenv("PATH_TO_TOFDM")
  printf("setting path from env var\n");
end

path_to_tofdm = sprintf("%s --nc %d", path_to_tofdm, Nc); % append Nc for variable Nc tests

if cml_support == 0
  path_to_tofdm = sprintf("%s --noldpc", path_to_tofdm);
end

system(path_to_tofdm);
load tofdm_out.txt;

fg = 1;

f = figure(fg++); clf; plot(rx_np_log,'+'); title('Octave Scatter Diagram'); axis([-1.5 1.5 -1.5 1.5]);
f = figure(fg++); clf; plot(rx_np_log_c,'+'); title('C Scatter Diagram'); axis([-1.5 1.5 -1.5 1.5]);

stem_sig_and_error(fg++, 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(fg, 211, real(tx_log_c), real(tx_log - tx_log_c), 'tx re', [1 length(tx_log_c) -0.1 0.1])
stem_sig_and_error(fg++, 212, imag(tx_log_c), imag(tx_log - tx_log_c), 'tx im', [1 length(tx_log_c) -0.1 0.1])

stem_sig_and_error(fg, 211, real(rx_log_c), real(rx_log - rx_log_c), 'rx re', [1 length(rx_log_c) -0.1 0.1])
stem_sig_and_error(fg++, 212, imag(rx_log_c), imag(rx_log - rx_log_c), 'rx im', [1 length(rx_log_c) -0.1 0.1])

stem_sig_and_error(fg, 211, real(rxbuf_in_log_c), real(rxbuf_in_log - rxbuf_in_log_c), 'rxbuf in re', [1 length(rxbuf_in_log_c) -0.1 0.1])
stem_sig_and_error(fg++, 212, imag(rxbuf_in_log_c), imag(rxbuf_in_log - rxbuf_in_log_c), 'rxbuf in im', [1 length(rxbuf_in_log_c) -0.1 0.1])

stem_sig_and_error(fg, 211, real(rxbuf_log_c), real(rxbuf_log - rxbuf_log_c), 'rxbuf re', [1 length(rxbuf_log_c) -0.1 0.1])
stem_sig_and_error(fg++, 212, imag(rxbuf_log_c), imag(rxbuf_log - rxbuf_log_c), 'rxbuf im', [1 length(rxbuf_log_c) -0.1 0.1])

stem_sig_and_error(fg, 211, real(rx_sym_log_c), real(rx_sym_log - rx_sym_log_c), 'rx sym re', [1 length(rx_sym_log_c) -1.5 1.5])
stem_sig_and_error(fg++, 212, imag(rx_sym_log_c), imag(rx_sym_log - rx_sym_log_c), 'rx sym im', [1 length(rx_sym_log_c) -1.5 1.5])

% for angles pi and -pi are the same

d = phase_est_pilot_log - phase_est_pilot_log_c; d = angle(exp(j*d));

stem_sig_and_error(fg, 211, phase_est_pilot_log_c, d, 'phase est pilot', [1 length(phase_est_pilot_log_c) -1.5 1.5])
stem_sig_and_error(fg++, 212, rx_amp_log_c, rx_amp_log - rx_amp_log_c, 'rx amp', [1 length(rx_amp_log_c) -1.5 1.5])

stem_sig_and_error(fg  , 211, foff_hz_log_c, (foff_hz_log - foff_hz_log_c), 'foff hz', [1 length(foff_hz_log_c) -1.5 1.5])

stem_sig_and_error(fg++, 212, timing_mx_log_c, (timing_mx_log - timing_mx_log_c), 'timing mx', [1 length(timing_mx_log_c) 0 2])

stem_sig_and_error(fg,   211, timing_est_log_c, (timing_est_log - timing_est_log_c), 'timing est', [1 length(timing_est_log_c) -1.5 1.5])
stem_sig_and_error(fg++, 212, sample_point_log_c, (sample_point_log - sample_point_log_c), 'sample point', [1 length(sample_point_log_c) -1.5 1.5])

stem_sig_and_error(fg++, 111, rx_bits_log_c, rx_bits_log - rx_bits_log_c, 'rx bits', [1 length(rx_bits_log) -1.5 1.5])

% Run through checklist -----------------------------

check(states.rate_fs_pilot_samples, pilot_samples_c, 'pilot_samples');
check(tx_bits_log, tx_bits_log_c, 'tx_bits');
check(tx_log, tx_log_c, 'tx');
check(rx_log, rx_log_c, 'rx');
check(rxbuf_in_log, rxbuf_in_log_c, 'rxbuf in');
check(rxbuf_log, rxbuf_log_c, 'rxbuf');
check(rx_sym_log, rx_sym_log_c, 'rx_sym', tol=10E-3);
check(phase_est_pilot_log, phase_est_pilot_log_c, 'phase_est_pilot', tol=1E-2, its_an_angle=1);
check(rx_amp_log, rx_amp_log_c, 'rx_amp');
check(timing_est_log, timing_est_log_c, 'timing_est');
check(timing_valid_log, timing_valid_log_c, 'timing_valid');
check(timing_mx_log, timing_mx_log_c, 'timing_mx');
check(coarse_foff_est_hz_log, coarse_foff_est_hz_log_c, 'coarse_foff_est_hz');
check(sample_point_log, sample_point_log_c, 'sample_point');
check(foff_hz_log, foff_hz_log_c, 'foff_est_hz');
check(rx_bits_log, rx_bits_log_c, 'rx_bits');
if cml_support
  check(symbol_likelihood_log, symbol_likelihood_log_c, 'symbol_likelihood_log', tol=1E-2);
  check(bit_likelihood_log, bit_likelihood_log_c, 'bit_likelihood_log');
  check(detected_data_log, detected_data_log_c, 'detected_data');
end
check(mean_amp_log, mean_amp_log_c, 'mean_amp_log');
check(snr_log, snr_log_c, 'snr_log');
printf("\npasses: %d fails: %d\n", passes, fails);