diff options
| author | drowe67 <[email protected]> | 2023-07-14 10:33:23 +0930 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-07-14 10:33:23 +0930 |
| commit | 6588e77f38bdebd7adffe091b22e7760d95d0ccb (patch) | |
| tree | e015b6d01db10ff219f5d1cf49eb3dcadb7dbe48 /octave/fm.m | |
| parent | ac7c48b4dee99d4c772f133d70d8d1b38262fcd2 (diff) | |
| parent | 98992bc3585124981450659394d6f84032b81370 (diff) | |
Merge pull request #1 from drowe67/dr-cleanup
Cleanup
Diffstat (limited to 'octave/fm.m')
| -rw-r--r-- | octave/fm.m | 484 |
1 files changed, 0 insertions, 484 deletions
diff --git a/octave/fm.m b/octave/fm.m deleted file mode 100644 index e25432f..0000000 --- a/octave/fm.m +++ /dev/null @@ -1,484 +0,0 @@ -% fm.m -% David Rowe Dec 2014 -% -% Analog FM Octave simulation functions. - -1; - -graphics_toolkit ("gnuplot"); - -function fm_states = analog_fm_init(fm_states) - - % FM modulator constants - - Fs = fm_states.Fs; FsOn2 = Fs/2; - fm_max = fm_states.fm_max; % max modulation freq - fd = fm_states.fd; % (max) deviation - fm_states.m = fd/fm_max; % modulation index - fm_states.Bfm = Bfm = 2*(fd+fm_max); % Carson's rule for FM signal bandwidth - fm_states.tc = tc = 50E-6; - fm_states.prede = [1 -(1 - 1/(tc*Fs))]; % pre/de emp filter coeffs - fm_states.ph_dont_limit = 0; % Limit rx delta-phase - - % Select length of filter to be an integer number of symbols to - % assist with "fine" timing offset estimation. Set Ts to 1 for - % analog modulation. - - Ts = fm_states.Ts; - desired_ncoeffs = 200; - ncoeffs = floor(desired_ncoeffs/Ts+1)*Ts; - - % "coarse" timing offset is half filter length, we have two filters. - % This is the delay the two filters introduce, so we need to adjust - % for this when comparing tx to trx bits for BER calcs. - - fm_states.nsym_delay = ncoeffs/Ts; - - % input filter gets rid of excess noise before demodulator, as too much - % noise causes atan2() to jump around, e.g. -pi to pi. However this - % filter can cause harmonic distortion at very high SNRs, as it knocks out - % some of the FM signal spectra. This filter isn't really required for high - % SNRs > 20dB. - - fc = (Bfm/2)/(FsOn2); - fm_states.bin = firls(ncoeffs,[0 fc*(1-0.05) fc*(1+0.05) 1],[1 1 0.01 0.01]); - - % demoduator output filter to limit us to fm_max (e.g. 3kHz) - - fc = fm_max/(FsOn2); - fm_states.bout = firls(ncoeffs,[0 0.95*fc 1.05*fc 1], [1 1 0.01 0.01]); -endfunction - - -function fm_fir_coeff_file(fm_states, filename) - global gt_alpha5_root; - global Nfilter; - - f=fopen(filename,"wt"); - - fprintf(f,"/* Generated by fm_fir_coeff_file() Octave function in fm.m */\n\n"); - fprintf(f,"const float bin[]={\n"); - for m=1:length(fm_states.bin)-1 - fprintf(f," %g,\n", fm_states.bin(m)); - endfor - fprintf(f," %g\n};\n\n", fm_states.bin(length(fm_states.bin))); - - fprintf(f,"const float bout[]={\n"); - for m=1:length(fm_states.bout)-1 - fprintf(f," %g,\n", fm_states.bout(m)); - endfor - fprintf(f," %g\n};\n", fm_states.bout(length(fm_states.bout))); - - fclose(f); -endfunction - - -function tx = analog_fm_mod(fm_states, mod) - Fs = fm_states.Fs; - fc = fm_states.fc; wc = 2*pi*fc/Fs; - fd = fm_states.fd; wd = 2*pi*fd/Fs; - nsam = length(mod); - - if fm_states.pre_emp - mod = filter(fm_states.prede,1,mod); - mod = mod/max(mod); % AGC to set deviation - end - - tx_phase = 0; - tx = zeros(1,nsam); - - for i=0:nsam-1 - w = wc + wd*mod(i+1); - tx_phase = tx_phase + w; - tx_phase = tx_phase - floor(tx_phase/(2*pi))*2*pi; - tx(i+1) = exp(j*tx_phase); - end -endfunction - - -function [rx_out rx_bb] = analog_fm_demod(fm_states, rx) - Fs = fm_states.Fs; - fc = fm_states.fc; wc = 2*pi*fc/Fs; - fd = fm_states.fd; wd = 2*pi*fd/Fs; - nsam = length(rx); - t = 0:(nsam-1); - - rx_bb = rx .* exp(-j*wc*t); % down to complex baseband - rx_bb = filter(fm_states.bin,1,rx_bb); - - % differentiate first, in rect domain, then find angle, this puts - % signal on the positive side of the real axis - - rx_bb_diff = [ 1 rx_bb(2:nsam) .* conj(rx_bb(1:nsam-1))]; - rx_out = atan2(imag(rx_bb_diff),real(rx_bb_diff)); - - % limit maximum phase jumps, to remove static type noise at low SNRs - if !fm_states.ph_dont_limit - rx_out(find(rx_out > wd)) = wd; - rx_out(find(rx_out < -wd)) = -wd; - end - rx_out *= (1/wd); - - if fm_states.output_filter - rx_out = filter(fm_states.bout,1,rx_out); - end - if fm_states.de_emp - rx_out = filter(1,fm_states.prede,rx_out); - end -endfunction - - -function sim_out = analog_fm_test(sim_in) - nsam = sim_in.nsam; - CNdB = sim_in.CNdB; - verbose = sim_in.verbose; - - Fs = fm_states.Fs = 96000; - fm_max = fm_states.fm_max = 3E3; - fd = fm_states.fd = 5E3; - fm_states.fc = 24E3; - - fm_states.pre_emp = pre_emp = sim_in.pre_emp; - fm_states.de_emp = de_emp = sim_in.de_emp; - fm_states.Ts = 1; - fm_states.output_filter = 1; - fm_states = analog_fm_init(fm_states); - sim_out.Bfm = fm_states.Bfm; - - Bfm = fm_states.Bfm; - m = fm_states.m; tc = fm_states.tc; - t = 0:(nsam-1); - - fm = 1000; wm = 2*pi*fm/fm_states.Fs; - - % start simulation - - for ne = 1:length(CNdB) - - % work out the variance we need to obtain our C/N in the bandwidth - % of the FM demod. The gaussian generator randn() generates noise - % with a bandwidth of Fs - - aCNdB = CNdB(ne); - CN = 10^(aCNdB/10); - variance = Fs/(CN*Bfm); - - % FM Modulator ------------------------------- - - mod = sin(wm*t); - tx = analog_fm_mod(fm_states, mod); - - % Channel --------------------------------- - - noise = sqrt(variance/2)*(randn(1,nsam) + j*randn(1,nsam)); - rx = tx + noise; - - % FM Demodulator - - [rx_out rx_bb] = analog_fm_demod(fm_states, rx); - - % notch out test tone - - w = 2*pi*fm/Fs; beta = 0.99; - rx_notch = filter([1 -2*cos(w) 1],[1 -2*beta*cos(w) beta*beta], rx_out); - - % measure power with and without test tone to determine S+N and N - - settle = 1000; % filter settling time, to avoid transients - nsettle = nsam - settle; - - sinad = (rx_out(settle:nsam) * rx_out(settle:nsam)')/nsettle; - nad = (rx_notch(settle:nsam) * rx_notch(settle:nsam)')/nsettle; - - snr = (sinad-nad)/nad; - sim_out.snrdB(ne) = 10*log10(snr); - - % Theory from FMTutorial.pdf, Lawrence Der, Silicon labs paper - - snr_theory_dB = aCNdB + 10*log10(3*m*m*(m+1)); - fx = 1/(2*pi*tc); W = fm_max; - I = (W/fx)^3/(3*((W/fx) - atan(W/fx))); - I_dB = 10*log10(I); - - sim_out.snr_theorydB(ne) = snr_theory_dB; - sim_out.snr_theory_pre_dedB(ne) = snr_theory_dB + I_dB; - - if verbose > 1 - printf("modn index: %2.1f Bfm: %.0f Hz\n", m, Bfm); - end - - if verbose > 0 - printf("C/N: %4.1f SNR: %4.1f dB THEORY: %4.1f dB or with pre/de: %4.1f dB\n", - aCNdB, 10*log10(snr), snr_theory_dB, snr_theory_dB+I_dB); - end - - if verbose > 1 - figure(1) - subplot(211) - plot(20*log10(abs(fft(rx)))) - title('FM Modulator Output Spectrum'); - axis([1 length(tx) 0 100]); - subplot(212) - Rx_bb = 20*log10(abs(fft(rx_bb))); - plot(Rx_bb) - axis([1 length(tx) 0 100]); - title('FM Demodulator (baseband) Input Spectrum'); - - figure(2) - subplot(211) - plot(rx_out(settle:nsam)) - axis([1 4000 -1 1]) - subplot(212) - Rx = 20*log10(abs(fft(rx_out(settle:nsam)))); - plot(Rx(1:10000)) - axis([1 10000 0 100]); - end - - end - -endfunction - - -function run_fm_curves - sim_in.nsam = 96000; - sim_in.verbose = 1; - sim_in.pre_emp = 0; - sim_in.de_emp = 0; - sim_in.CNdB = -4:2:20; - - sim_out = analog_fm_test(sim_in); - - figure(1) - clf - plot(sim_in.CNdB, sim_out.snrdB,"r;FM Simulated;"); - hold on; - plot(sim_in.CNdB, sim_out.snr_theorydB,"g;FM Theory;"); - plot(sim_in.CNdB, sim_in.CNdB,"b; SSB Theory;"); - hold off; - grid("minor"); - xlabel("FM demod input C/N (dB)"); - ylabel("FM demod output S/N (dB)"); - legend("boxoff"); - - % C/No curves - - Bfm_dB = 10*log10(sim_out.Bfm); - Bssb_dB = 10*log10(3000); - - figure(2) - clf - plot(sim_in.CNdB + Bfm_dB, sim_out.snrdB,"r;FM Simulated;"); - hold on; - plot(sim_in.CNdB + Bfm_dB, sim_out.snr_theorydB,"g;FM Theory;"); - plot(sim_in.CNdB + Bssb_dB, sim_in.CNdB,"b; SSB Theory;"); - hold off; - grid("minor"); - xlabel("FM demod input C/No (dB)"); - ylabel("FM demod output S/N (dB)"); - legend("boxoff"); - -endfunction - - -function run_fm_single - sim_in.nsam = 96000; - sim_in.verbose = 2; - sim_in.pre_emp = 0; - sim_in.de_emp = 0; - - sim_in.CNdB = 20; - sim_out = analog_fm_test(sim_in); -end - - -function fm_mod_file(file_name_out, file_name_in, CNdB) - fm_states.Fs = 48000; - fm_states.fm_max = 3E3; - fm_states.fd = 5E3; - fm_states.fc = fm_states.Fs/4; - fm_states.pre_emp = 0; - fm_states.de_emp = 0; - fm_states.Ts = 1; - fm_states.output_filter = 1; - fm_states = analog_fm_init(fm_states); - - if nargin == 1 - nsam = fm_states.Fs * 10; - t = 0:(nsam-1); - fm = 1000; wm = 2*pi*fm/fm_states.Fs; - mod = sin(wm*t); - else - fin = fopen(file_name_in,"rb"); - mod = fread(fin,"short")'; - mod /= 32767; - fclose(fin); - end - tx = analog_fm_mod(fm_states, mod); - - if (nargin == 3) - % Optionally add some noise - - CN = 10^(CNdB/10); - variance = fm_states.Fs/(CN*fm_states.Bfm); - tx += sqrt(variance)*randn(1,length(tx)); - end - - tx_out = tx*16384; - fout = fopen(file_name_out,"wb"); - fwrite(fout, tx_out, "short"); - fclose(fout); -endfunction - - -function fm_demod_file(file_name_out, file_name_in) - fin = fopen(file_name_in,"rb"); - rx = fread(fin,"short")'; - rx = rx(100000:length(rx)); % strip of wave header - fclose(fin); - - Fs = fm_states.Fs = 48000; - fm_max = fm_states.fm_max = 3E3; - fd = fm_states.fd = 5E3; - fm_states.fc = 12E3; - - fm_states.pre_emp = 0; - fm_states.de_emp = 1; - fm_states.Ts = 1; - fm_states.output_filter = 1; - fm_states = analog_fm_init(fm_states); - - [rx_out rx_bb] = analog_fm_demod(fm_states, rx); - - rx_out *= 20000; - fout = fopen(file_name_out,"wb"); - fwrite(fout, rx_out, "short"); - fclose(fout); - - figure(1) - subplot(211) - plot(rx) - subplot(212) - plot(20*log10(abs(fft(rx)))) - title('FM Dmodulator Input Spectrum'); - - figure(2) - subplot(211) - Rx_bb = 20*log10(abs(fft(rx_bb))); - plot(Rx_bb) - title('FM Demodulator (baseband) Input Spectrum'); - - subplot(212) - plot(20*log10(abs(fft(rx_out)))) - title('FM Dmodulator Output Spectrum'); - - figure(3) - plot(rx_out) - title('FM Dmodulator Output'); - - % estimate SNR, C/No etc - - npower_window = 1024; - rx_power = conv(rx.^2,ones(1,npower_window))/(npower_window); - rx_power_dB = 10*log10(rx_power); - figure; - subplot(211) - plot(rx); - subplot(212) - plot(rx_power_dB); - axis([1 length(rx_power) max(rx_power_dB)-9 max(rx_power_dB)+1]) - grid("minor") - - % estimate FM demod output SNR if a 1000 Hz tone is present - - w = 2*pi*1000/Fs; beta = 0.99; - rx_notch = filter([1 -2*cos(w) 1],[1 -2*beta*cos(w) beta*beta], rx_out); - - rx_out_power = conv(rx_out.^2,ones(1,npower_window))/(npower_window); - rx_out_power_dB = 10*log10(rx_out_power); - rx_notch_power = conv(rx_notch.^2,ones(1,npower_window))/(npower_window); - rx_notch_power_dB = 10*log10(rx_notch_power); - figure; - plot(rx_out_power_dB,'r;FM demod output power;'); - hold on; - plot(rx_notch_power_dB,'b;1000 Hz notch filter output power;'); - plot(rx_out_power_dB-rx_notch_power_dB,'g;1000 Hz tone SNR;'); - hold off; - legend("boxoff"); - ylabel('dB'); - xlabel('Time (samples)'); - grid("minor") - -endfunction - - -% generate filter coeffs for C implementation of FM demod - -function make_coeff_file - fm_states.Fs = 44400; - fm_states.fm_max = 3E3; - fm_states.fd = 5E3; - fm_states.fc = fm_states.Fs/4; - - fm_states.pre_emp = 0; - fm_states.de_emp = 0; - fm_states.Ts = 1; - fm_states.output_filter = 1; - fm_states = analog_fm_init(fm_states); - - fm_fir_coeff_file(fm_states, "fm_fir_coeff.h") -endfunction - -function test_fm_modulator - fm_states.Fs = 48000; - fm_states.fm_max = 3E3; - fm_states.fd = 5E3; - %fm_states.fc = fm_states.Fs/4; - fm_states.fc = 0; - - fm_states.pre_emp = 0; - fm_states.de_emp = 0; - fm_states.Ts = 1; - fm_states.output_filter = 1; - fm_states = analog_fm_init(fm_states); - - test_t = [1:(fm_states.Fs*10)]; - test_freq1 = 2*pi*3000/fm_states.Fs; - test_freq2 = 2*pi*1000/fm_states.Fs; - - test_sig = .5*sin(test_t*test_freq1) + .5*sin(test_t*test_freq2); - %test_sig = zeros(1,length(test_t)); - %test_sig = ones(1,length(test_t)); - - ftsig = fopen("fm_test_sig.raw","wb"); - fwrite(ftsig,test_sig*16384,"short"); - fclose(ftsig); - - system("../fm_test fm_test_sig.raw fm_test_out.raw"); - ftmod = fopen("fm_test_out.raw","r"); - test_mod_p = rot90(fread(ftmod,"short"))/16384; - test_mod_r = test_mod_p(1:2:length(test_mod_p)); - test_mod_i = test_mod_p(2:2:length(test_mod_p)); - test_mod = test_mod_r .+ i*test_mod_i; - fclose(ftmod); - - comp_mod = analog_fm_mod(fm_states,test_sig); - - figure(1) - comp_mod_real = real(comp_mod); - size(comp_mod_real) - size(test_mod) - mod_diff = zeros(1,length(test_mod)); - mod_diff = test_mod .- comp_mod; - plot(test_t,real(test_mod .- comp_mod),test_t,imag(test_mod .- comp_mod)); - -endfunction - -more off; - -%run_fm_curves -%fm_demod_file("ssb_fm_out.raw","~/Desktop/ssb_fm.wav") -%fm_demod_file("ssb25_fm_de.raw", "~/Desktop/ssb25db.wav") -%run_fm_single -%make_coeff_file -%fm_mod_file("fm_1000.raw"); -%test_fm_modulator |
