File: ofdm_ldpc_tx.m

package info (click to toggle)
codec2 0.9.2-4
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 113,072 kB
  • sloc: ansic: 412,877; python: 4,004; sh: 1,540; objc: 817; asm: 683; makefile: 588
file content (173 lines) | stat: -rw-r--r-- 5,232 bytes parent folder | download
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
% 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] = 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