% fdmdv_demod.m % % Demodulator function for FDMDV modem (Octave version). Requires % 8kHz sample rate raw files as input % % Copyright David Rowe 2012 % This program is distributed under the terms of the GNU General Public License % Version 2 % function fdmdv_demod(rawfilename, nbits, NumCarriers=14, errorpatternfilename, symbolfilename) pkg load signal; fdmdv; % include modem code f = fdmdv_init(NumCarriers); Nc = f.Nc; Nb = f.Nb; Rs = f.Rs; M = f.M; Fs = f.Fs; Nsync_mem = f.Nsync_mem; test_bits = f.test_bits; Q = f.Q; P = f.P; modulation = 'dqpsk'; fin = fopen(rawfilename, "rb"); gain = 1000; frames = nbits/(Nc*Nb); prev_rx_symbols = ones(Nc+1,1); foff_phase_rect = 1; % BER stats total_bit_errors = 0; total_bits = 0; bit_errors_log = []; sync_log = []; test_frame_sync_log = []; test_frame_sync_state = 0; error_pattern_log = []; % SNR states sig_est = zeros(Nc+1,1); noise_est = zeros(Nc+1,1); % logs of various states for plotting rx_symbols_log = []; rx_timing_log = []; foff_coarse_log = []; foff_log = []; rx_fdm_log = []; snr_est_log = []; % misc states nin = M; % timing correction for sample rate differences foff = 0; fest_state = 0; fest_timer = 0; sync_mem = zeros(1,Nsync_mem); sync = 0; sync_log = []; % spectrum states Nspec=1024; spec_mem=zeros(1,Nspec); SdB = zeros(1,Nspec); % optionally save output symbols if nargin == 5 fm = fopen(symbolfilename,"wb"); dual_rx_symbols = zeros(1, 2*Nc); dual_rx_bits = zeros(1,2*Nc*Nb); end atimer = 0; % Main loop ---------------------------------------------------- for fr=1:frames % obtain nin samples of the test input signal for i=1:nin rx_fdm(i) = fread(fin, 1, "short")/gain; end rx_fdm_log = [rx_fdm_log rx_fdm(1:nin)]; % update spectrum l=length(rx_fdm); spec_mem(1:Nspec-l) = spec_mem(l+1:Nspec); spec_mem(Nspec-l+1:Nspec) = rx_fdm; S=fft(spec_mem.*hanning(Nspec)',Nspec); SdB = 0.9*SdB + 0.1*20*log10(abs(S)); % shift down to complex baseband for i=1:nin f.fbb_phase_rx = f.fbb_phase_rx*f.fbb_rect'; rx_fdm(i) = rx_fdm(i)*f.fbb_phase_rx; end mag = abs(f.fbb_phase_rx); f.fbb_phase_rx /= mag; % frequency offset estimation and correction [pilot prev_pilot f.pilot_lut_index f.prev_pilot_lut_index] = get_pilot(f, f.pilot_lut_index, f.prev_pilot_lut_index, nin); [foff_coarse S1 S2 f] = rx_est_freq_offset(f, rx_fdm, pilot, prev_pilot, nin, !sync ); if sync == 0 foff = foff_coarse; end foff_coarse_log = [foff_coarse_log foff_coarse]; foff_rect = exp(j*2*pi*foff/Fs); for i=1:nin foff_phase_rect *= foff_rect'; rx_fdm_fcorr(i) = rx_fdm(i)*foff_phase_rect; end % baseband processing if 0 % easier to understand, but more memory and CPU hungry filtering and down conversion [rx_baseband f] = fdm_downconvert(f, rx_fdm_fcorr, nin); [rx_filt f] = rx_filter(f, rx_baseband, nin); else % more efficient filtering and down conversion [rx_fdm_filter f] = rxdec_filter(f, rx_fdm_fcorr, nin); [rx_filt f] = down_convert_and_rx_filter(f, rx_fdm_filter, nin, M/Q); end [rx_symbols rx_timing env f] = rx_est_timing(f, rx_filt, nin); rx_timing_log = [rx_timing_log rx_timing]; nin = M; if rx_timing > M/P nin += M/P; end if rx_timing < -M/P; nin -= M/P; end %printf("fr: %d rx_timing: %d nin = %d\n", fr, rx_timing, nin); rx_symbols_log = [rx_symbols_log rx_symbols.*conj(prev_rx_symbols./abs(prev_rx_symbols))*exp(j*pi/4)]; [rx_bits sync_bit f_err pd] = psk_to_bits(f, prev_rx_symbols, rx_symbols, modulation); % optionally save output symbols if (nargin == 5) % this free runs, and is reset by an "entered sync" state if (sync_track == 0) sync_track = 1; else sync_track = 0; end if (track == 1) && (sync_track == 1) dual_rx_symbols(Nc+1:2*Nc) = rx_symbols(1:Nc).*conj(prev_rx_symbols(1:Nc)./abs(prev_rx_symbols(1:Nc))); dual_rx_symbols_float32 = []; k = 1; for i=1:2*Nc dual_rx_symbols_float32(k++) = real(dual_rx_symbols(i)); dual_rx_symbols_float32(k++) = imag(dual_rx_symbols(i)); end fwrite(fm, dual_rx_symbols_float32, "float32"); dual_rx_bits(Nc*Nb+1:2*Nc*Nb) = rx_bits; %dump_bits(dual_rx_bits); else dual_rx_symbols(1:Nc) = rx_symbols(1:Nc).*conj(prev_rx_symbols(1:Nc)./abs(prev_rx_symbols(1:Nc))); dual_rx_bits(1:Nc*Nb) = rx_bits; end end % update some states prev_rx_symbols = rx_symbols; [sig_est noise_est] = snr_update(f, sig_est, noise_est, pd); snr_est = calc_snr(f, sig_est, noise_est); snr_est_log = [snr_est_log snr_est]; foff -= 0.5*f_err; foff_log = [foff_log foff]; % freq est state machine [sync reliable_sync_bit fest_state fest_timer sync_mem] = freq_state(f, sync_bit, fest_state, fest_timer, sync_mem); sync_log = [sync_log sync]; % count bit errors if we find a test frame [test_frame_sync bit_errors error_pattern f] = put_test_bits(f, rx_bits); if (test_frame_sync == 1) if (bit_errors) printf("fr: %d bit_errors: %d\n", fr, bit_errors); end total_bit_errors = total_bit_errors + bit_errors; total_bits = total_bits + f.Ntest_bits; bit_errors_log = [bit_errors_log bit_errors/f.Ntest_bits]; else bit_errors_log = [bit_errors_log 0]; end % test frame sync state machine, just for more informative plots next_test_frame_sync_state = test_frame_sync_state; if (test_frame_sync_state == 0) if (test_frame_sync == 1) next_test_frame_sync_state = 1; test_frame_count = 0; end end if (test_frame_sync_state == 1) % we only expect another test_frame_sync pulse every 4 symbols test_frame_count++; if (test_frame_count == 4) test_frame_count = 0; if ((test_frame_sync == 0)) next_test_frame_sync_state = 0; else error_pattern_log = [error_pattern_log error_pattern]; end end end test_frame_sync_state = next_test_frame_sync_state; test_frame_sync_log = [test_frame_sync_log test_frame_sync_state]; end if nargin == 5 fclose(fm); etfilename = strcat(strtok(symbolfilename,"."),"_et.bin"); fet = fopen(etfilename, "wb"); fwrite(fet, entered_track_log, "short"); fclose(fet); end % --------------------------------------------------------------------- % Print Stats % --------------------------------------------------------------------- % Peak to Average Power Ratio calcs from http://www.dsplog.com papr = max(rx_fdm_log.*conj(rx_fdm_log)) / mean(rx_fdm_log.*conj(rx_fdm_log)); papr_dB = 10*log10(papr); ber = total_bit_errors / total_bits; printf("%d bits %d errors BER: %1.4f PAPR(rx): %1.2f dB\n",total_bits, total_bit_errors, ber, papr_dB); % --------------------------------------------------------------------- % Plots % --------------------------------------------------------------------- xt = (1:frames)/Rs; secs = frames/Rs; figure(1); clf; [n m] = size(rx_symbols_log); plot(real(rx_symbols_log(1:Nc+1,15:m)),imag(rx_symbols_log(1:Nc+1,15:m)),'+') axis([-2 2 -2 2]); title('Scatter Diagram'); figure(2); clf; plot(xt, rx_timing_log) title('timing offset (samples)'); figure(3); plot(xt, foff_log, '-;freq offset;') %hold on; %plot(xt, sync_log*75, 'r;course-fine;'); %hold off; title('Freq offset (Hz)'); grid; figure(4); clf; plot_specgram(rx_fdm_log, Fs); figure(5); clf; subplot(311) stem(xt, sync_log) axis([0 secs 0 1.5]); title('BPSK Sync') subplot(312) stem(xt, bit_errors_log); title('Bit Errors for test frames') subplot(313) plot(xt, test_frame_sync_log); axis([0 secs 0 1.5]); title('Test Frame Sync') figure(6); clf; subplot(211); plot(xt, snr_est_log); title('SNR Estimates') subplot(212) snrdB_pc = 20*log10(sig_est(1:Nc+1)) - 20*log10(noise_est(1:Nc+1)); bar(snrdB_pc(1:Nc) - mean(snrdB_pc(1:Nc))) axis([0 Nc+1 -3 3]); figure(7); clf; hold on; lep = length(error_pattern_log); if lep != 0 for p=1:Nc plot(p + 0.25*error_pattern_log((p-1)*2+1:Nc*Nb:lep)); plot(0.30 + p + 0.25*error_pattern_log(p*2:Nc*Nb:lep),'r') end hold off; axis([1 lep/(Nc*Nb) 0 Nc]) end figure(8); clf; subplot(211) [a b] = size(rx_fdm_log); xt1 = (1:b)/Fs; plot(xt1, rx_fdm_log); title('Rx FDM Signal'); subplot(212) plot((0:Nspec/2-1)*Fs/Nspec, SdB(1:Nspec/2) - 20*log10(Nspec/2)) axis([0 Fs/2 -40 0]) grid title('FDM Rx Spectrum'); if 0 % interleaving tests load ../unittest/inter560.txt lep = length(error_pattern_log); lep = floor(lep/560)*560; error_pattern_log_inter = zeros(1,lep); for i=1:560:lep for j=1:560 %printf("i: %4d j: %4d inter560(j): %4d\n", i,j,inter560(j)); index = inter560(j); error_pattern_log_inter(i-1+index+1) = error_pattern_log(i-1+j); end end figure(8) clf; hold on; for p=1:Nc plot(p + 0.25*error_pattern_log_inter((p-1)*2+1:Nc*Nb:lep)); plot(0.30 + p + 0.25*error_pattern_log_inter(p*2:Nc*Nb:lep),'r') end hold off; axis([1 lep/(Nc*Nb) 0 Nc]) end % optionally save error pattern file if nargin == 4 fout = fopen(errorpatternfilename, "wb"); fwrite(fout, error_pattern_log, "short"); fclose(fout); end endfunction