1% channel.m
2%
3% Commonly used channel simulation functions
4
51;
6
7function [spread1 spread2 path_delay_samples] = channel_multipath(channel, Fs, Nsam)
8    % Winlink multipath definitions
9    if strcmp(channel, 'mpg')     dopplerSpreadHz = 0.1; path_delay_ms = 0.5;
10    elseif strcmp(channel, 'mpm') dopplerSpreadHz = 0.5; path_delay_ms = 1.0;
11    elseif strcmp(channel, 'mpp') dopplerSpreadHz = 1.0; path_delay_ms = 2.0;
12    elseif strcmp(channel, 'mpd') dopplerSpreadHz = 2.0; path_delay_ms = 4.0;
13    elseif strcmp(channel, 'mpf') dopplerSpreadHz = 4.0; path_delay_ms = 4.0;
14    elseif strcmp(channel, 'notch') dopplerSpreadHz = 0.0; path_delay_ms = 2.0;
15    elseif printf("Unknown multipath channel\n"); assert(0); end
16
17    path_delay_samples = path_delay_ms*Fs/1000;
18    %printf(" Doppler Spread: %3.2f Hz Path Delay: %3.2f ms %d samples\n", dopplerSpreadHz, path_delay_ms, path_delay_samples);
19
20    if strcmp(channel, "notch")
21      % simple notch filter (not time varying), hand tweaked to be 10dB down at about 1300 Hz (Fc-200Hz)
22      spread1 = 0.5*ones(1,Nsam);
23      spread2 = j*0.2*ones(1,Nsam);
24    else
25      % generate same fading pattern for every run
26      spread1 = doppler_spread(dopplerSpreadHz, Fs, Nsam);
27      spread2 = doppler_spread(dopplerSpreadHz, Fs, Nsam);
28    end
29
30    % sometimes doppler_spread() doesn't return exactly the number of samples we need
31    if length(spread1) < Nsam
32      printf("not enough doppler spreading samples %d %d\n", length(spread1), Nsam);
33      assert(0);
34    end
35    if length(spread2) < Nsam
36      printf("not enough doppler spreading samples %d %d\n", length(spread2), Nsam);
37      assert(0);
38    end
39endfunction
40
41% returns real rx signal with noise added, input is complex tx signal
42function [rx_real rx sigma] = channel_simulate(Fs, SNR3kdB, freq_offset_Hz, channel, tx, verbose=0)
43  Nsam = length(tx);
44  rx = tx;
45
46  if strcmp(channel, 'awgn') == 0
47    [spread1 spread2 path_delay_samples] = channel_multipath(channel, Fs, Nsam);
48    rx  = tx(1:Nsam) .* spread1(1:Nsam);
49    rx += [zeros(1,path_delay_samples) tx(1:Nsam-path_delay_samples)] .* spread2(1:Nsam);
50  end
51
52  woffset = 2*pi*freq_offset_Hz/Fs;
53  rx = rx .* exp(j*woffset*(1:Nsam));
54
55  rx_real = real(rx); S = rx_real*rx_real';
56  rpapr = 10*log10(max(abs(rx_real).^2)/mean(abs(rx_real).^2));
57
58  % SNR in a 4k bandwidth will be lower than 3k as total noise power N is higher
59  SNR4kdB = SNR3kdB - 10*log10(Fs/2) + 10*log10(3000); SNR = 10^(SNR4kdB/10);
60  N = S/SNR; sigma = sqrt(N/Nsam);
61  n = sigma*randn(1,Nsam);
62  % printf("SNR3kdB: %f SNR4kdB: %f N: %f %f\n", SNR3kdB, SNR4kdB, N, n*n');
63  rx_real += n;
64  % check our sums are OK to within 0.25 dB
65  SNR4kdB_measured = 10*log10(S/(n*n'));
66  assert (abs(SNR4kdB - SNR4kdB_measured) < 0.5);
67  if verbose
68    printf("foff: %3.1f Hz SNR(3k): %3.1f dB  ", freq_offset_Hz, SNR3kdB);
69    printf("measSNR3k: %3.2f dB N: %3.2f dB\n",
70           10*log10(S/(n*n')) + 10*log10(4000) - 10*log10(3000), 10*log10(n*n'));
71  end
72endfunction
73